Considera questa rappresentazione per termini lambda parametrizzati dalle loro variabili libere. (Vedi articoli di Bellegarde e Hook 1994, Bird e Paterson 1999, Altenkirch e Reus 1999.)
data Tm a = Var a
| Tm a :$ Tm a
| Lam (Tm (Maybe a))
Puoi certamente renderlo un Functor, catturare il concetto di rinomina e Monadcatturare il concetto di sostituzione.
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))
Consideriamo ora i termini chiusi : questi sono gli abitanti di Tm Void. Dovresti essere in grado di incorporare i termini chiusi in termini con variabili libere arbitrarie. Come?
fmap absurd :: Tm Void -> Tm a
Il problema, ovviamente, è che questa funzione attraverserà il termine senza fare proprio nulla. Ma è un tocco più onesto di unsafeCoerce. Ed è per questo che è vacuousstato aggiunto a Data.Void...
Oppure scrivi un valutatore. Ecco i valori con variabili libere 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
Ho appena rappresentato i lambda come chiusure. Il valutatore è parametrizzato da un ambiente che associa le variabili libere aai valori superiori 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
Hai indovinato. Per valutare un termine chiuso a qualsiasi target
eval absurd :: Tm Void -> Val b
Più in generale, Voidè usato raramente da solo, ma è utile quando si desidera istanziare un parametro di tipo in un modo che indica una sorta di impossibilità (ad esempio, qui, utilizzando una variabile libera in un termine chiuso). Spesso questi tipi parametrizzati vengono con funzioni di ordine superiore operazioni di sollevamento sui parametri per operazioni del tipo complesso (ad esempio, qui, fmap, >>=, eval). Quindi passi absurdcome operazione generica Void.
Per un altro esempio, immagina di usare Either e vper acquisire calcoli che si spera ti diano un vma potrebbero sollevare un'eccezione di tipo e. È possibile utilizzare questo approccio per documentare in modo uniforme il rischio di comportamenti scorretti. Per calcoli perfettamente funzionanti in questa impostazione, prendi eper essere Void, quindi usa
either absurd id :: Either Void v -> v
per correre in sicurezza o
either absurd Right :: Either Void v -> Either e v
per incorporare componenti sicuri in un mondo non sicuro.
Oh, e un ultimo evviva, gestire un "non può succedere". Si presenta nella costruzione generica della cerniera, ovunque il cursore non possa essere.
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!
Ho deciso di non cancellare il resto, anche se non è esattamente rilevante.
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)
In realtà, forse è rilevante. Se ti senti avventuroso, questo articolo incompleto mostra come utilizzare Voidper comprimere la rappresentazione dei termini con variabili libere
data Term f x = Var x | Con (f (Term f x)) -- the Free monad, yet again
in qualsiasi sintassi generata liberamente da a Differentiablee Traversablefuntore f. Usiamo Term f Voidper rappresentare regioni senza variabili libere e [D f (Term f Void)]per rappresentare tubi che scorrono attraverso regioni senza variabili libere o su una variabile libera isolata o su una giunzione nei percorsi di due o più variabili libere. Devo finire quell'articolo prima o poi.
Per un tipo senza valori (o almeno, nessuno di cui valga la pena parlare in gentile compagnia), Voidè straordinariamente utile. Ed absurdè come lo usi.
absurdfunzione è stata utilizzata in questo articolo che tratta dellaContmonade: haskellforall.com/2012/12/the-continuation-monad.html