Haskell GADT可以用来证明GHC中的类型不等式吗?

Haskell GADT可以用来证明GHC中的类型不等式吗?,haskell,curry-howard,Haskell,Curry Howard,因此,在我不断尝试通过哈斯克尔的小练习来半了解库里·霍华德的过程中,我被困在了这一点上: {-# LANGUAGE GADTs #-} import Data.Void type Not a = a -> Void -- | The type of type equality proofs, which can only be instantiated if a = b. data Equal a b where Refl :: Equal a a -- | Derive

因此,在我不断尝试通过哈斯克尔的小练习来半了解库里·霍华德的过程中,我被困在了这一点上:

{-# LANGUAGE GADTs #-}

import Data.Void

type Not a = a -> Void

-- | The type of type equality proofs, which can only be instantiated if a = b.
data Equal a b where
    Refl :: Equal a a

-- | Derive a contradiction from a putative proof of @Equal Int Char@.
intIsNotChar :: Not (Equal Int Char)
intIsNotChar intIsChar = ???
显然,类型
Equal Int Char
没有(非底部)居民,因此语义上应该有一个
absurdEquality::Equal Int Char->a
函数。。。但就我的一生而言,除了使用
undefined
,我想不出任何方法来编写一个

因此,要么:

  • 我错过了什么,或者
  • 由于语言的局限性,这是一项不可能完成的任务,我还没能理解它是什么

  • 我怀疑答案是这样的:编译器无法利用没有a=b的
    Equal
    构造函数这一事实。但是如果是这样的话,是什么让它成为现实呢?

    我不理解使用
    未定义的
    的问题,每种类型都有Haskell中的bottom。我们的语言没有很强的规范化。。。你找错东西了
    Equal Int Char
    导致类型错误异常保存不好。看

    {-# LANGUAGE GADTs, TypeFamilies #-}
    
    data Equal a b where
        Refl :: Equal a a
    
    type family Pick cond a b
    type instance Pick Char a b = a
    type instance Pick Int a b = b
    
    newtype Picker cond a b = Picker (Pick cond a b)
    
    pick :: b -> Picker Int a b
    pick = Picker
    
    unpick :: Picker Char a b -> a
    unpick (Picker x) = x
    
    samePicker :: Equal t1 t2 -> Picker t1 a b -> Picker t2 a b
    samePicker Refl x = x
    
    absurdCoerce :: Equal Int Char -> a -> b
    absurdCoerce e x = unpick (samePicker e (pick x))
    
    您可以使用它来创建所需的函数

    absurdEquality e = absurdCoerce e ()
    
    但这将产生未定义的行为作为其计算规则
    false
    应导致程序中止,或至少永远运行。中止是一种计算规则,类似于通过添加not将最小逻辑转化为初始逻辑。正确的定义是

    absurdEquality e = error "you have a type error likely to cause big problems"
    

    至于标题中的问题:基本上不是。据我所知,类型不等式在当前的Haskell中是不可实际表示的。类型系统即将发生的变化可能会导致这种情况变得更好,但到目前为止,我们有平等,但没有不平等

    这是Philip JF解决方案的一个简短版本,这是依赖型理论家多年来驳斥方程的方式

    type family Discriminate x
    type instance Discriminate Int  = ()
    type instance Discriminate Char = Void
    
    transport :: Equal a b -> Discriminate a -> Discriminate b
    transport Refl d = d
    
    refute :: Equal Int Char -> Void
    refute q = transport q ()
    
    为了证明事物是不同的,您必须通过提供一个计算上下文来捕捉它们的不同行为,从而产生不同的观察结果
    discrime
    正好提供了这样一个上下文:一个类型级程序,它以不同的方式处理这两种类型

    没有必要求助于
    未定义
    来解决此问题。总编程有时包括拒绝不可能的输入。即使在
    未定义的
    可用的情况下,我也建议不要在total方法足够的情况下使用它:total方法解释了为什么有些事情不可能,typechecker会确认<代码>未定义
    仅记录您的承诺。事实上,这种反驳方法是警句在确保案例分析涵盖其领域的同时,如何省去“不可能的案例”

    至于计算行为,请注意,
    反驳
    ,通过
    传输
    q
    中必须严格,并且
    q
    不能在空上下文中计算到head范式,这仅仅是因为不存在这样的head范式(当然也因为计算保留了类型)。在total设置中,我们可以确保在运行时永远不会调用
    returne
    。在Haskell中,我们至少可以肯定,在我们必须对其作出回应之前,它的论点会出现分歧或抛出异常。懒惰的版本,如

    absurdEquality e = error "you have a type error likely to cause big problems"
    
    将忽略
    e
    的毒性,并告诉您有类型错误,而您没有。我更喜欢

    absurdEquality e = e `seq` error "sue me if this happens"
    

    如果诚实的反驳太像艰苦的工作。

    请看:@dbaupp:不是重复的——这个问题并不是为了证明不平等。@C.a.McCann,啊,是的,我想在我开始胡乱指责之前,我应该仔细阅读。:)请注意,
    seq e(错误“如果发生这种情况请告我”)
    可以在第一个参数之前计算第二个参数,如果这是一个问题,您可以使用
    pseq
    ()。