假设单位类型是否相等是否安全?

时间:2016-04-04 02:04:37

标签: haskell types

假设所有类型的()都相同是否安全?也就是说,可以使用以下方法来打破类型安全吗?

-- Bad postulate
unitsEqual :: (x :: ()) :~: (y :: ())
unitsEqual = unsafeCoerce (Refl :: '() :~: '())

2 个答案:

答案 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 ~ 'FalseInt ~ 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}}是不同的类型,xy都不是Any

voidEqual同样不健全,因为Any :: VoidAny Any :: VoidAny Any Any :: Void等。