Haskell 什么';这是数据中荒谬的函数。Void对我们有用吗?
Haskell 什么';这是数据中荒谬的函数。Void对我们有用吗?,haskell,type-theory,curry-howard,Haskell,Type Theory,Curry Howard,Data.Void中的函数具有以下签名,其中Void是该包导出的逻辑上未占用的类型: -- | Since 'Void' values logically don't exist, this witnesses the logical -- reasoning tool of \"ex falso quodlibet\". absurd :: Void -> a 我确实知道足够的逻辑,可以得到文档中的注释,即通过类型对应的命题,这对应于有效的公式⊥ → a 我感到困惑和好奇的是:在什么样
Data.Void
中的函数具有以下签名,其中Void
是该包导出的逻辑上未占用的类型:
-- | Since 'Void' values logically don't exist, this witnesses the logical
-- reasoning tool of \"ex falso quodlibet\".
absurd :: Void -> a
我确实知道足够的逻辑,可以得到文档中的注释,即通过类型对应的命题,这对应于有效的公式⊥ → a
我感到困惑和好奇的是:在什么样的实际编程问题中,这个函数有用?我在想,在某些情况下,它作为一种类型安全的方式来彻底处理“不可能发生”的案例可能是有用的,但我对Curry Howard的实际用途了解不够,无法判断这个想法是否在正确的轨道上
编辑:最好是Haskell中的示例,但如果有人想使用依赖类型的语言,我不会抱怨
我在想,在某些情况下,作为一种类型安全的方式,它可能是非常有用的,可以彻底地处理“不可能发生”的情况
这是完全正确的
你可以说,荒谬
并不比常量(错误“不可能”)
更有用。但是,它是类型受限的,因此它的唯一输入可以是Void
类型的内容,这是一种故意不被占用的数据类型。这意味着没有可以传递给的实际值。如果你最终进入了一个代码分支,类型检查器认为你有权访问类型为Void
的东西,那么,好吧,你处于一个荒谬的境地。因此,您只需使用荒谬
,基本上就可以标记此代码分支永远不会被访问
“Ex falso quodlibet”字面意思是“从[a]错误的[proposition],任何事情都会随之而来”。因此,当您发现您持有的数据类型为Void
,您就知道手中有虚假证据。因此,你可以填补任何你想要的漏洞(通过荒谬的),因为从一个错误的命题,任何事情都会随之而来
我写了一篇关于导管背后的想法的博客,其中有一个使用荒谬的的例子
通常,您可以使用它来避免明显的部分模式匹配。例如,从以下位置获取数据类型声明的近似值:
然后你可以像这样使用荒谬的,例如:
handleLOARules :: (String -> a) -> LinesOfActionsRuleSet -> a
handleLOARules f r = case r of
Known a -> absurd a
Unknown s -> f s
生活有点艰难,因为哈斯克尔并不严格。一般用例是处理不可能的路径。比如说
simple :: Either Void a -> a
simple (Left x) = absurd x
simple (Right y) = y
这在某种程度上是有用的。考虑一个简单的类型:<代码>管道< /代码>
data Pipe a b r
= Pure r
| Await (a -> Pipe a b r)
| Yield !b (Pipe a b r)
这是Gabriel Gonzales的pipes
库中标准管道类型的严格简化版本。现在,我们可以将从不产生(即消费者)的管道编码为
这真的永远不会让步。这意味着消费者的正确折叠规则是
foldConsumer :: (r -> s) -> ((a -> s) -> s) -> Consumer a r -> s
foldConsumer onPure onAwait p
= case p of
Pure x -> onPure x
Await f -> onAwait $ \x -> foldConsumer onPure onAwait (f x)
Yield x _ -> absurd x
或者,在与消费者打交道时,您可以忽略收益率案例。这是此设计模式的通用版本:使用多态数据类型和Void
,在需要时消除各种可能性
Void
最经典的用法可能是在CPS中
type Continuation a = a -> Void
也就是说,延续
是一个永远不会返回的函数Continuation
是“not”的类型版本。从中我们得到了CPS的单子(对应于经典逻辑)
因为Haskell是纯的,所以我们不能从这种类型中得到任何东西。考虑由自由变量参数化的lambda项的这种表示。(见Bellegarde和Hook 1994、Bird和Paterson 1999、Altenkirch和Reus 1999的论文。)
您当然可以将其设置为函子
,捕获重命名的概念,以及单子
捕获替换的概念
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虚空< /代码>的居民。您应该能够将封闭项嵌入具有任意自由变量的项中。怎么做
fmap absurd :: Tm Void -> Tm a
当然,问题是这个函数将遍历这个术语,完全不做任何事情。但这比不完美的感觉更诚实。这就是为什么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
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
我刚刚将lambdas表示为闭包。通过将a
中的自由变量映射到b
上的值来参数化计算器
data Val b
= b :$$ [Val b] -- a stuck application
| forall a. LV (a -> Val b) (Tm (Maybe a)) -- we have an incomplete environment
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
)。因此,您将作为Void
上的通用操作传递
再举一个例子,想象一下使用或EV
捕获计算,这些计算可能会给您一个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
在由可微和T自由生成的任何语法中
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)
data Term f x = Var x | Con (f (Term f x)) -- the Free monad, yet again
type Void' = forall a . a
type Sink i m r = Pipe i i Void () m r
type Continuation r a = a -> Cont r Void
shrink : (xs : Vect (S n) a) -> Elem x xs -> Vect n a
shrink (x :: ys) Here = ys
shrink (y :: []) (There p) = absurd p
shrink (y :: (x :: xs)) (There p) = y :: shrink (x :: xs) p