在编写about how to do subtyping in Haskell时,我想到能够“使用”诸如True ~ False
之类的矛盾证据来告知编译器死分支是非常方便的。使用另一个标准空类型Void
,EmptyCase
扩展名允许您以这种方式标记死分支(即包含类型Void
的值):
use :: Void -> a
use x = case x of
我想为不可满足的Constraint
做类似的事情。
是否有一个术语可以赋予True ~ False => a
类型但不能给出类型a
?
答案 0 :(得分:8)
您通常可以通过将证据的确切性质与您计划使用它的方式分开来实现。如果类型检查器看到你引入了一个荒谬的约束,它就会咆哮你。因此,诀窍是将这种平等推迟到:~:
之后,然后使用通常合理的函数来操纵平等证据。
{-# LANGUAGE GADTs, TypeOperators, ScopedTypeVariables, DataKinds,
PolyKinds, RankNTypes #-}
{-# OPTIONS_GHC -Wall #-}
module TrueFalse where
import Data.Type.Equality
data Foo (a :: Bool) where
Can :: Foo 'False
Can't :: (forall x . x) -> Foo 'True
extr :: Foo 'True -> a
extr (Can't x) = x
subst :: a :~: b -> f a -> f b
subst Refl x = x
whoop :: 'False :~: 'True -> a
whoop pf = extr $ subst pf Can
whoop
函数似乎与您正在寻找的大致相同。
正如AndrásKovács评论的那样,您甚至可以在EmptyCase
值上使用'False :~: 'True
。不幸的是,目前(7.10.3)EmptyCase
doesn't warn about non-exhaustive matches。希望很快就能修复。
答案 1 :(得分:4)
如果这种约束作为给定约束出现,则会导致类型错误。一般来说,这适用于typechecker认为不可能的任何约束。
甚至编写功能
f :: ('True ~ 'False) => x
f = undefined
没有类型检查,因为函数的上下文是函数体中的给定约束 - 而'True ~ 'False
根本不能作为给定的约束出现。
充其量你可以有例如
import Data.Type.Equality ((:~:)(..))
type family (==) (a :: k) (b :: k) :: Bool where
a == a = 'True
a == b = 'False
f :: ((x == y) ~ 'False) => x :~: y -> a
-- f Refl = undefined -- Inaccessible code
f = \case{}
再次回到EmptyCase
,这次是:~:
。注意
f :: ((x == y) ~ 'False, x ~ y) => a
也会减少到一个非常不可能的约束,因为x == x
会减少到True
。你可以编写一个等级谓词,它不会减少平凡的相等类型(例如Data.Type.Equality
中的那个),它允许你写:
import Data.Type.Equality
f :: ((x == y) ~ 'False, x ~ y) => Proxy '(x,y) -> a
f = undefined
可能有一种方法可以在不使用undefined
的情况下编写此函数,但无论如何它都是没有意义的,因为GHC会立即减少此类型:
>:t f f :: forall (k :: BOX) (y :: k) a. ((y == y) ~ 'False) => Proxy '(y, y) -> a
即使没有约束,在定义上也不可能用两种不同类型调用函数Proxy '(y,y) -> a
。无法从类型检查器中隐藏等式约束~
- 您必须使用不同的同等形式,一种不会减少到~
的等式。