假设所有类型的()
都相同是否安全?也就是说,可以使用以下方法来打破类型安全吗?
-- Bad postulate
unitsEqual :: (x :: ()) :~: (y :: ())
unitsEqual = unsafeCoerce (Refl :: '() :~: '())
答案 0 :(得分:5)
根据GHC 7.8.3,这是不安全的(见下面的代码)。如用户2407038所述,GHC 7.10.3拒绝以下内容。不出所料,这个数据系列足够邪恶,类型检查器被更改为禁止它。我仍然试图看看是否有办法在7.10.3下实现这一目标。
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
module UnitsEqual where
import Unsafe.Coerce
import Data.Type.Equality
data family Yeah (a :: ()) b c
data instance Yeah '() b c = Yeah { yeah :: b }
data instance Yeah a b c = Nope c
-- Entirely valid
castYeah :: x :~: y -> Yeah x p q -> Yeah y p q
castYeah Refl x = x
-- Bad postulate
unitsEqual :: (x :: ()) :~: (y :: ())
unitsEqual = unsafeCoerce (Refl :: '() :~: '())
-- Oh no! This doesn't actually cast, but
-- it's horrible enough. It consistently produces
-- either segmentation faults or nonsense,
-- whether the types are the same or not.
uc :: a -> b
uc a = yeah $ castYeah unitsEqual (Nope a)
我相信, 可以安全地假设
voidsEqual :: (a :: Void) :~: (b :: Void)
因为没有明显的方法可以区分任何陷入 Void
的卡住/伪造类型。
答案 1 :(得分:1)
免责声明:这个答案主要是猜测,因为这种行为让我很困惑,而且我仍然不太确定typechecker中Any
的语义。
在您的示例中,忽略Yeah
应该被拒绝,当它计算Nope a
的类型时,它会找到一个不明确的类型变量并将其实例化为Any
。这与制造例如length []
工作。
你可以用GADT做同样的事情,这在GHC 7.10上被接受:
data Yeah (a :: ()) b c where
Yeah :: b -> Yeah '() b c
Nope :: c -> Yeah Any b c
要编写uc
,您需要一个功能
yeah :: Yeah '() b c -> b
yeah (Yeah a) = a
yeah (Nope _) = error "???"
在第二种情况下,给出了等式'() ~ Any
,我认为这是平等的错误,例如'True ~ 'False
,Int ~ Bool
等。类型检查员知道这些事实:
okay :: 'True :~: 'False -> x
-- okay Refl = error "???" -- Compiler rejects this case
okay x = case x of
但不是这个!
really :: Any :~: '() -> x
really Refl = error "???" -- Perfectly valid pattern match
您实际上无法调用此函数:
>:t really Refl
<interactive>:1:8: Warning:
Couldn't match type `Any' with '()
Expected type: Any :~: '()
Actual type: '() :~: '()
In the first argument of `really', namely `Refl'
In the expression: really Refl
虽然uc
类型检查(定义未更改)但它不再中断:
>uc 'a' :: Int
*** Exception: ???
似乎typechecker不相信'() :~: Any
无人居住,因为它不是,因为typechecker在内部被允许产生这样的证据,但是用户仍被禁止自己编写。
所有这些都说:我认为如果你假装Any
不存在,那么unitsEqual
就是健全的,而Any
的存在则产生了不健全。反例似乎明确错误 - Any :~: '()
- 但似乎Any
有一个特殊的类型检查器规则,x ~ y
只有在x
和{y
时才会显得非常虚假{1}}是不同的类型,x
或y
都不是Any
。
voidEqual
同样不健全,因为Any :: Void
,Any Any :: Void
,Any Any Any :: Void
等。