Considérer cette représentation pour lambda termes paramétrés par leurs variables libres. (Voir les documents par Bellegarde et Crochet de 1994, d'Oiseaux et de Paterson 1999, Altenkirch et Reus 1999.)
data Tm a = Var a
| Tm a :$ Tm a
| Lam (Tm (Maybe a))
Vous pouvez certainement faire de cette un Functor
, saisir la notion de changement de nom et un Monad
saisir la notion de substitution.
instance Functor Tm where
fmap rho (Var a) = Var (rho a)
fmap rho (f :$ s) = fmap rho f :$ fmap rho s
fmap rho (Lam t) = Lam (fmap (fmap rho) t)
instance Monad Tm where
return = Var
Var a >>= sig = sig a
(f :$ s) >>= sig = (f >>= sig) :$ (s >>= sig)
Lam t >>= sig = Lam (t >>= maybe (Var Nothing) (fmap Just . sig))
Considérons maintenant le fermé termes: ce sont les habitants de Tm Void
. Vous devriez être en mesure d'intégrer la fermé termes en termes arbitraire de variables libres. Comment?
fmap absurd :: Tm Void -> Tm a
Le hic, bien sûr, est que cette fonction va parcourir le terme fait rien de précis. Mais c'est un peu plus honnête que d' unsafeCoerce
. Et c'est pourquoi, vacuous
a été ajouté à l' Data.Void
...
Ou écrire un évaluateur. Voici les valeurs, avec variables libres en b
.
data Val b
= b :$$ [Val b] -- a stuck application
| forall a. LV (a -> Val b) (Tm (Maybe a)) -- we have an incomplete environment
J'ai simplement représenté lambdas que les fermetures. L'évaluateur est paramétrisée par un environnement de mappage de variables libres en a
pour des valeurs en b
.
eval :: (a -> Val b) -> Tm a -> Val b
eval g (Var a) = g a
eval g (f :$ s) = eval g f $$ eval g s where
(b :$$ vs) $$ v = b :$$ (vs ++ [v]) -- stuck application gets longer
LV g t $$ v = eval (maybe v g) t -- an applied lambda gets unstuck
eval g (Lam t) = LV g t
Vous l'aurez deviné. Pour évaluer une période fermée à toute cible
eval absurd :: Tm Void -> Val b
Plus généralement, Void
est rarement utilisé seul, mais il est pratique lorsque vous souhaitez instancier un paramètre de type dans une manière qui indique une sorte d'impossibilité (par exemple, ici, à utiliser une variable dans une période fermée). Souvent, ces types paramétrés viennent avec des fonctions d'ordre supérieur opérations de levage sur les paramètres des opérations sur tout type (par exemple, ici, fmap
, >>=
, eval
). Si vous passez absurd
comme l'opération sur Void
.
Pour un autre exemple, imaginez l'aide d' Either e v
pour capturer les calculs qui nous l'espérons vous donner un v
mais peut soulever une exception de type e
. Vous pouvez utiliser cette approche pour le document risque de mal se comporter de manière uniforme. Pour parfaitement bien comportés les calculs dans ce cadre, prendre en e
être Void
, puis utilisez
either absurd id :: Either Void v -> v
pour exécuter en toute sécurité ou
either absurd Right :: Either Void v -> Either e v
pour intégrer des composants de sécurité dans un monde dangereux.
Oh, et un dernier tour de piste, la manipulation d'un "ne peut pas se produire". Il apparaît dans le générique de fermeture à glissière de la construction, de partout que le curseur ne peut pas être.
class Differentiable f where
type D f :: * -> * -- an f with a hole
plug :: (D f x, x) -> f x -- plugging a child in the hole
newtype K a x = K a -- no children, just a label
newtype I x = I x -- one child
data (f :+: g) x = L (f x) -- choice
| R (g x)
data (f :*: g) x = f x :&: g x -- pairing
instance Differentiable (K a) where
type D (K a) = K Void -- no children, so no way to make a hole
plug (K v, x) = absurd v -- can't reinvent the label, so deny the hole!
J'ai décidé de ne pas supprimer le reste, même si elle n'est pas pertinente.
instance Differentiable I where
type D I = K ()
plug (K (), x) = I x
instance (Differentiable f, Differentiable g) => Differentiable (f :+: g) where
type D (f :+: g) = D f :+: D g
plug (L df, x) = L (plug (df, x))
plug (R dg, x) = R (plug (dg, x))
instance (Differentiable f, Differentiable g) => Differentiable (f :*: g) where
type D (f :*: g) = (D f :*: g) :+: (f :*: D g)
plug (L (df :&: g), x) = plug (df, x) :&: g
plug (R (f :&: dg), x) = f :&: plug (dg, x)
En fait, c'est peut-être pertinente. Si vous vous sentez aventureux, ce inachevé article montre comment utiliser Void
pour compresser la représentation des termes avec variables libres
data Term f x = Var x | Con (f (Term f x)) -- the Free monad, yet again
dans toute la syntaxe générée librement à partir d'un Differentiable
et Traversable
foncteur f
. Nous utilisons Term f Void
pour représenter les régions avec variables libres, et [D f (Term f Void)]
pour représenter des tubes de tunnels à travers des régions avec pas de variables libres, soit à un cas isolé gratuit variable, ou à un point de jonction dans les chemins de deux ou plusieurs variables libres. Doit terminer cet article de temps en temps.
Pour un type sans valeurs (ou du moins n'en vaut parler de dans la bonne société), Void
est remarquablement utile. Et absurd
est la façon dont vous l'utiliser.