我不想在Haskell中禁用检查伴随绑定的函数。
我想这样做的原因是能够通过矛盾来实现证明。以下类型签名没有任何绑定,因此不应该这样。
zeroDoesNotEqualOne :: Refl Z (S Z) -> Bottom
Refl Z (S Z)
类型没有居民,因此不应该有任何约束力。
在上面的代码段中,类型意味着您的期望,S Z
是Peano自然为1而Refl
只有一个Refl a a
答案 0 :(得分:3)
您不需要:使用EmptyCase
语言扩展名,此语句实际上是可证明的。这是一个展示它的独立文件:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE EmptyCase #-}
module ZeroNeqOne where
data (==) a b where
Refl :: a == a
data Nat where
Z :: Nat
S :: Nat -> Nat
zeroNeqOne :: Z == S Z -> a
zeroNeqOne p = case p of {}
鉴于你在评论中谈论定理证明,它让我思考,事实证明我们可以玩一个小游戏Coq用户很喜欢:在类型级别使用对角函数。参看JF Monin的Proof Trick: Small inversions。这次我们将使用TypeFamilies
扩展名。抛弃矛盾的a == b
的想法是使用类型级函数,它将要求我们在提出a
时证明一个微不足道的目标,而在提出b
时则不可能。然后使用相等证明将琐碎的结果传递给不可能的结果:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
module ZeroNeqOneDiag where
import Data.Void
data (==) a b where
Refl :: a == a
subst :: a == b -> p a -> p b
subst Refl pa = pa
data Nat where
Z :: Nat
S :: Nat -> Nat
type family Diag (n :: Nat) :: * where
Diag 'Z = ()
Diag ('S n) = Void
newtype Diagonal n = Diagonal { runDiagonal :: Diag n }
zeroNeqOneDiag :: 'Z == 'S 'Z -> Void
zeroNeqOneDiag p = runDiagonal $ subst p (Diagonal ())