考虑由自由变量参数化的 lambda 项的这种表示。(参见 Bellegarde 和 Hook 1994 年、Bird 和 Paterson 1999 年、Altenkirch 和 Reus 1999 年的论文。)
data Tm a = Var a
| Tm a :$ Tm a
| Lam (Tm (Maybe a))
您当然可以将其设为 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
我刚刚将 lambdas 表示为闭包。评估器由将自由变量映射a
到值 over的环境参数化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
在从 a和Traversable
functor自由生成的任何语法中f
。我们Term f Void
用来表示没有自由变量的区域,并[D f (Term f Void)]
表示通过没有自由变量的区域的管子隧道到一个孤立的自由变量,或者到两个或多个自由变量的路径中的连接点。必须在某个时候完成那篇文章。
对于没有价值观的类型(或者至少在礼貌的公司中不值得一提),Void
非常有用。并且absurd
是你如何使用它。