Betrachten Sie diese Darstellung für Lambda-Begriffe, die durch ihre freien Variablen parametrisiert sind. (Siehe Artikel von Bellegarde und Hook 1994, Bird und Paterson 1999, Altenkirch und Reus 1999.)
data Tm a = Var a
| Tm a :$ Tm a
| Lam (Tm (Maybe a))
Sie können dies sicherlich zu einem Functor
Begriff machen , der den Begriff des Umbenennens und Monad
den Begriff der Substitution erfasst.
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))
Betrachten Sie nun die geschlossenen Begriffe: Dies sind die Einwohner von Tm Void
. Sie sollten in der Lage sein, die geschlossenen Begriffe in Begriffe mit beliebigen freien Variablen einzubetten. Wie?
fmap absurd :: Tm Void -> Tm a
Der Haken ist natürlich, dass diese Funktion den Begriff durchquert und genau nichts tut. Aber es ist ein Hauch ehrlicher als unsafeCoerce
. Und deshalb vacuous
wurde hinzugefügt, um Data.Void
...
Oder schreiben Sie einen Bewerter. Hier sind Werte mit freien Variablen in b
.
data Val b
= b :$$ [Val b] -- a stuck application
| forall a. LV (a -> Val b) (Tm (Maybe a)) -- we have an incomplete environment
Ich habe gerade Lambdas als Verschlüsse dargestellt. Der Evaluator wird durch eine Umgebung parametrisiert, in der freie Variablen a
Werten zugeordnet werden 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
Du hast es erraten. Um einen geschlossenen Begriff an einem beliebigen Ziel zu bewerten
eval absurd :: Tm Void -> Val b
Im Allgemeinen wird Void
es nur selten alleine verwendet, ist jedoch praktisch, wenn Sie einen Typparameter auf eine Weise instanziieren möchten, die auf eine Art Unmöglichkeit hinweist (z. B. hier die Verwendung einer freien Variablen in einem geschlossenen Begriff). Oft sind diese parametrisierte Typen kommen mit Funktionen höherer Ordnung Operationen an den Parameter - Operationen auf der ganzen Art ( zum Beispiel hier, Heben fmap
, >>=
, eval
). Sie geben also absurd
als Allzweckoperation weiter Void
.
Stellen Sie sich als weiteres Beispiel vor, Either e v
wie Sie Berechnungen erfassen möchten, die Ihnen hoffentlich v
eine Ausnahme vom Typ geben, aber möglicherweise auslösen e
. Sie können diesen Ansatz verwenden, um das Risiko eines schlechten Verhaltens einheitlich zu dokumentieren. Für perfekt verhaltene Berechnungen in dieser Einstellung nehmen Sie e
an Void
und verwenden Sie sie
either absurd id :: Either Void v -> v
sicher laufen oder
either absurd Right :: Either Void v -> Either e v
sichere Komponenten in eine unsichere Welt einzubetten.
Oh, und ein letztes Hurra, mit einem "kann nicht passieren" umzugehen. Es zeigt sich in der generischen Reißverschlusskonstruktion überall dort, wo der Cursor nicht sein kann.
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!
Ich habe beschlossen, den Rest nicht zu löschen, obwohl er nicht genau relevant ist.
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)
Eigentlich ist es vielleicht relevant. Wenn Sie sich abenteuerlustig fühlen, zeigt dieser unvollendete Artikel , wie Sie Void
die Darstellung von Begriffen mit freien Variablen komprimieren können
data Term f x = Var x | Con (f (Term f x)) -- the Free monad, yet again
in jeder Syntax, die frei von a Differentiable
und Traversable
functor generiert wird f
. Wir verwenden Term f Void
, um Regionen ohne freie Variablen [D f (Term f Void)]
darzustellen , und um Röhren darzustellen , die durch Regionen ohne freie Variablen entweder zu einer isolierten freien Variablen oder zu einem Knotenpunkt in den Pfaden zu zwei oder mehr freien Variablen tunneln. Muss diesen Artikel irgendwann beenden.
Für einen Typ ohne Werte (oder zumindest keinen, von dem es sich lohnt, in höflicher Gesellschaft zu sprechen) Void
ist dies bemerkenswert nützlich. Und absurd
so benutzt du es.
absurd
Funktion in diesem Artikel verwendet wurde, der sich mit derCont
Monade befasst: haskellforall.com/2012/12/the-continuation-monad.html