Розглянемо це подання для лямбда-членів, параметризованих їх вільними змінними. (Див. Статті Bellegarde and Hook 1994, Bird and Paterson 1999, Altenkirch and Reus 1999.)
data Tm a = Var a
| Tm a :$ Tm a
| Lam (Tm (Maybe a))
Ви, безумовно, можете зробити це Functor
, захопивши поняття перейменування та Monad
захопивши поняття заміни.
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))
А тепер розглянемо закриті терміни: це жителі Росії Tm Void
. Ви повинні мати можливість вставляти закриті терміни в терміни з довільними вільними змінними. Як?
fmap absurd :: Tm Void -> Tm a
Суть, звичайно, полягає в тому, що ця функція буде перетинати термін, не роблячи абсолютно нічого. Але це штрих більш чесний, ніж unsafeCoerce
. І ось чому vacuous
було додано до Data.Void
...
Або напишіть оцінювача. Ось значення з вільними змінними в b
.
data Val b
= b :$$ [Val b] -- a stuck application
| forall a. LV (a -> Val b) (Tm (Maybe a)) -- we have an incomplete environment
Я щойно представляв лямбди як закриття. Оцінювач параметризується середовищем, що відображає вільні змінні у a
значення над 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
Ви здогадалися. Оцінити закритий термін для будь-якої цілі
eval absurd :: Tm Void -> Val b
Більш загально, Void
він рідко використовується самостійно, але є зручним, коли потрібно створити екземпляр параметра типу таким чином, що вказує на якусь неможливість (наприклад, тут, використовуючи вільну змінну в закритому терміні). Часто ці параметризрвані типи поставляються з функціями вищого порядку підйомних операцій за параметрами операцій по всьому типу (наприклад, тут, fmap
, >>=
, eval
). Отже, ви переходите absurd
як операцію загального призначення Void
.
Для іншого прикладу, уявіть, як використовувати Either e v
для захоплення обчислень, які, сподіваємось, дають вам, v
але можуть викликати виняток типу e
. Ви можете використовувати цей підхід для рівномірного документування ризику поганої поведінки. Для ідеально оброблених обчислень у цій обстановці візьміть e
за Void
, а потім використовуйте
either absurd id :: Either Void v -> v
бігти безпечно або
either absurd Right :: Either Void v -> Either e v
вбудовувати безпечні компоненти в небезпечний світ.
О, і останній ура, обробка "не може статися". Це відображається у загальній конструкції блискавки скрізь, де курсором бути не може.
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!
Я вирішив не видаляти решту, хоча це не зовсім актуально.
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)
Насправді, можливо, це актуально. Якщо ви відчуваєте авантюру, ця незакінчена стаття показує, як використовувати Void
стиснення подання термінів із вільними змінними
data Term f x = Var x | Con (f (Term f x)) -- the Free monad, yet again
у будь-якому синтаксисі, вільно сформованому з а Differentiable
та Traversable
функтора f
. Ми використовуємо Term f Void
для представлення областей без вільних змінних та [D f (Term f Void)]
для представлення труб, що проходять через області без вільних змінних, або до ізольованої вільної змінної, або до місця з'єднання на шляхах до двох або більше вільних змінних. Потрібно колись закінчити цю статтю.
Для типу без цінностей (або, принаймні, нічого, про що не варто говорити у чемній компанії), Void
надзвичайно корисно. І absurd
як ви ним користуєтесь.
absurd
функція була використана в цій статті, що стосуєтьсяCont
монади: haskellforall.com/2012/12/the-continuation-monad.html