简体   繁体   English

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

[英]Can GADTs be used to prove type inequalities in GHC?

So, in my ongoing attempts to half-understand Curry-Howard through small Haskell exercises, I've gotten stuck at this point: 因此,在我不断尝试通过小型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 a contradiction from a putative proof of @Equal Int Char@.
intIsNotChar :: Not (Equal Int Char)
intIsNotChar intIsChar = ???

Clearly the type Equal Int Char has no (non-bottom) inhabitants, and thus semantically there ought to be an absurdEquality :: Equal Int Char -> a function... but for the life of me I can't figure out any way to write one other than using undefined . 显然, Equal Int Char类型没有(非底层)居民,因此在语义上应该有一个absurdEquality :: Equal Int Char -> a函数......但对于我的生活我无法想出任何方式写一个而不是使用undefined

So either: 所以要么:

  1. I'm missing something, or 我错过了什么,或者
  2. There is some limitation of the language that makes this an impossible task, and I haven't managed to understand what it is. 这种语言存在一些限制,这使得这项任务变得不可能,而且我还没有理解它是什么。

I suspect the answer is something like this: the compiler is unable to exploit the fact that there are no Equal constructors that don't have a = b. 我怀疑答案是这样的:编译器无法利用没有没有a = b的Equal构造函数的事实。 But if that is so, what makes it true? 但如果是这样,那是什么让它成真呢?

Here's a shorter version of Philip JF's solution, which is the way dependent type theorists have been refuting equations for years. 这是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 ()

In order to show that things are different, you have to catch them behaving differently by providing a computational context which results in distinct observations. 为了表明事物是不同的,你必须通过提供导致不同观察的计算环境来捕捉它们表现不同行为 Discriminate provides exactly such a context: a type-level program which treats the two types differently. Discriminate提供了这样一个上下文:一个类型级别的程序,它以不同的方式处理这两种类型。

It is not necessary to resort to undefined to solve this problem. 没有必要求助于undefined来解决这个问题。 Total programming sometimes involves rejecting impossible inputs. 总编程有时涉及拒绝不可能的输入。 Even where undefined is available, I would recommend not using it where a total method suffices: the total method explains why something is impossible and the typechecker confirms; 即使在undefined的情况下,我也建议不要在总方法足够的情况下使用它:总方法解释了为什么某些东西是不可能的并且类型检查者确认了; undefined merely documents your promise . undefined只记录你的承诺 Indeed, this method of refutation is how Epigram dispenses with "impossible cases" whilst ensuring that a case analysis covers its domain. 实际上,这种反驳方法是Epigram如何免除“不可能的案例”,同时确保案例分析涵盖其领域。

As for computational behaviour, note that refute , via transport is necessarily strict in q and that q cannot compute to head normal form in the empty context, simply because no such head normal form exists (and because computation preserves type, of course). 至于计算行为,请注意,通过transport refuteq必然是严格的,并且q在空上下文中不能计算为正常形式,因为不存在这样的头部正规形式(并且因为计算保留类型,当然)。 In a total setting, we'd be sure that refute would never be invoked at run time. 在完整设置中,我们确信在运行时永远不会调用refute In Haskell, we're at least certain that its argument will diverge or throw an exception before we're obliged to respond to it. 在Haskell中,我们至少可以肯定,在我们不得不对它做出回应之前,它的论点会分歧或抛出异常。 A lazy version, such as 一个懒惰的版本,如

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

will ignore the toxicity of e and tell you that you have a type error when you don't. 将忽略e的毒性并告诉您,如果不这样做,则会出现类型错误。 I prefer 我更喜欢

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

if the honest refutation is too much like hard work. 如果诚实的反驳太过于艰苦的工作。

I don't understand the problem with using undefined every type is inhabited by bottom in Haskell. 我不明白使用undefined的问题每个类型都在Haskell的底部居住。 Our language is not strongly normalizing... You are looking for the wrong thing. 我们的语言没有强烈正常化......你正在寻找错误的东西。 Equal Int Char leads to type errors not nice well kept exceptions. Equal Int Char导致类型错误不是很好保存异常。 See 看到

{-# 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))

you could use this to create the function you want 你可以用它来创建你想要的功能

absurdEquality e = absurdCoerce e ()

but that will produce undefined behavior as its computation rule. 但这将产生未定义的行为作为其计算规则。 false should cause programs to abort, or at the very least run for ever. false应该导致程序中止,或者至少可以运行。 Aborting is the computation rule that is akin to turning minimal logic into intiutionistic logic by adding not. 中止是一种计算规则,类似于通过添加not而将最小逻辑转换为语言逻辑。 The correct definition is 正确的定义是

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

as to the question in the title: essentially no. 关于标题中的问题:基本上没有。 To the best of my knowledge, type inequality is not representable in a practical way in current Haskell. 据我所知,在当前的Haskell中,类型不等式在实际方面是不可表示的。 Coming changes to the type system may lead to this getting nicer, but as of right now, we have equalities but not inequalites. 类型系统的改变可能导致这种情况越来越好,但截至目前,我们有平等但不平等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM