Considere esta representación para términos lambda parametrizados por sus variables libres. (Véanse los artículos de Bellegarde y Hook 1994, Bird y Paterson 1999, Altenkirch y Reus 1999.)
data Tm a = Var a
| Tm a :$ Tm a
| Lam (Tm (Maybe a))
Ciertamente puede hacer que esto Functorcapture la noción de cambio de nombre y Monadcapture la noción de sustitución.
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))
Ahora considere los términos cerrados : estos son los habitantes de Tm Void. Debería poder incrustar los términos cerrados en términos con variables libres arbitrarias. ¿Cómo?
fmap absurd :: Tm Void -> Tm a
El problema, por supuesto, es que esta función atravesará el término sin hacer nada precisamente. Pero es un toque más honesto que unsafeCoerce. Y es por eso que vacuousse agregó a Data.Void...
O escribe un evaluador. Aquí hay valores con variables libres en formato b.
data Val b
= b :$$ [Val b] -- a stuck application
| forall a. LV (a -> Val b) (Tm (Maybe a)) -- we have an incomplete environment
Acabo de representar lambdas como cierres. El evaluador está parametrizado por un entorno que asigna variables libres aa valores superiores 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
Lo adivinaste. Para evaluar un plazo cerrado en cualquier objetivo
eval absurd :: Tm Void -> Val b
De manera más general, Voidrara vez se usa por sí solo, pero es útil cuando desea instanciar un parámetro de tipo de una manera que indique algún tipo de imposibilidad (por ejemplo, aquí, usando una variable libre en un término cerrado). A menudo, estos tipos parametrizadas vienen con funciones de orden superior operaciones de elevación de los parámetros a las operaciones en todo el tipo (por ejemplo, aquí, fmap, >>=, eval). Así que pasa absurdcomo la operación de propósito general Void.
Para otro ejemplo, imagínese usar Either e vpara capturar cálculos que con suerte le den un vpero podrían generar una excepción de tipo e. Puede utilizar este enfoque para documentar el riesgo de mal comportamiento de manera uniforme. Para cálculos que se comportan perfectamente en esta configuración, tómatelo ey Voidluego usa
either absurd id :: Either Void v -> v
para correr con seguridad o
either absurd Right :: Either Void v -> Either e v
para integrar componentes seguros en un mundo inseguro.
Ah, y un último hurra, manejar un "no puede suceder". Aparece en la construcción de cremallera genérica, en todos los lugares donde el cursor no puede estar.
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!
Decidí no borrar el resto, aunque no es exactamente relevante.
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)
De hecho, tal vez sea relevante. Si se siente aventurero, este artículo inacabado muestra cómo utilizar Voidpara comprimir la representación de términos con variables libres
data Term f x = Var x | Con (f (Term f x)) -- the Free monad, yet again
en cualquier sintaxis generada libremente desde a Differentiabley Traversablefunctor f. Usamos Term f Voidpara representar regiones sin variables libres y [D f (Term f Void)]para representar tubos que hacen un túnel a través de regiones sin variables libres, ya sea a una variable libre aislada o a una unión en las rutas a dos o más variables libres. Debo terminar ese artículo en algún momento.
Para un tipo sin valores (o al menos, ninguno de los que valga la pena hablar en compañía cortés), Voides muy útil. Y así absurdes como lo usas.
absurdfunción se ha utilizado en este artículo sobre laContmónada: haskellforall.com/2012/12/the-continuation-monad.html