Розглянемо це подання для лямбда-членів, параметризованих їх вільними змінними. (Див. Статті 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