Haskell 中的异构引用相等性
Heterogeneous reference equality in Haskell
在 GHC 中,IORef
和 STRef
的相等实例基于以下原始操作:
sameMutVar# :: MutVar# s a -> MutVar# s a -> Int#
我希望能够计算异构引用相等性,
sameReference :: IORef a -> IORef b -> Bool
(或类似的 STRef
s 具有可能不同的类型)。是否可以只使用 unsafeCoerce
来检查引用相等性? sameMutVar#
没有给出异构类型签名是有原因的吗?
编辑:为了添加一些上下文,我想要这种异类指针相等性,因为我想使用相等性方法从 IORef
的列表中删除特定的 IORef a
,其类型存在量化超过。
写起来绝对安全
sameReference :: IORef a -> IORef b -> Bool
sameReference = unsafeCoerce ((==) :: IORef a -> IORef a -> Bool)
primop 被赋予类型是完全合理的
sameMutVar# :: MutVar# s a -> MutVar# s b -> Int#
但设计者显然认为在不同类型的引用上使用该函数比其他方式更容易出错。
你不能安全地得出结论sameReference (r1 :: IORef a) (r2 :: IORef b) = True
意味着a
和b
是相同的。假设你有
sameRefSameType :: IORef a -> IORef b -> Maybe (a :~: b)
那你就可以轻松写
oops :: Coercible a b => IORef a -> a :~: b
oops r = fromJust (sameRefSameType r (coerce r))
产生任何两个可强制类型相等的虚假证据。您应该能够弄清楚如何使用 GADT 从那里到达 mkUC :: IO (a -> b)
.
我相信写
是安全的
sameRefCoercibleTypes :: IORef a -> IORef b -> Maybe (Coercion a b)
既然 Daniel Wagner 提到了稳定的名字,我应该指出,在这种情况下,那些人的情况更糟。我需要先了解一些背景知识。假设你写
f :: Either x Int -> Either x Bool
f (Left x) = Left x
f (Right _) = Right False
在第一种情况下,分配一个新的 Left
构造函数只是为了改变类型是一种耻辱。所以 GHC 有一个低级优化(在核心到核心优化管道之后)试图把它变成(本质上)
f p@(Left x) = unsafeCoerce p
f (Right _) = Right False
这意味着您可以有 m :: Either x a
和 n :: Either x b
,其中 m
和 n
指的是同一个堆对象,尽管 a
和 b
具有完全不相关的类型。如果您为 m
创建一个稳定名称并为 n
创建一个稳定名称,那么这些稳定名称将比较相等!如果你存入甚至高达
sameSNCoercibleTypes
:: StableName a
-> StableName b
-> Maybe (Coercion a b)
然后您可以使用 m
和 n
到 "prove" Coercible (Either x a) (Either x b)
,您可以从中将任何 a
转换为任何 b
。这有点微妙,但因为它是可能的,否则假设是相当不安全的。
在 GHC 中,IORef
和 STRef
的相等实例基于以下原始操作:
sameMutVar# :: MutVar# s a -> MutVar# s a -> Int#
我希望能够计算异构引用相等性,
sameReference :: IORef a -> IORef b -> Bool
(或类似的 STRef
s 具有可能不同的类型)。是否可以只使用 unsafeCoerce
来检查引用相等性? sameMutVar#
没有给出异构类型签名是有原因的吗?
编辑:为了添加一些上下文,我想要这种异类指针相等性,因为我想使用相等性方法从 IORef
的列表中删除特定的 IORef a
,其类型存在量化超过。
写起来绝对安全
sameReference :: IORef a -> IORef b -> Bool
sameReference = unsafeCoerce ((==) :: IORef a -> IORef a -> Bool)
primop 被赋予类型是完全合理的
sameMutVar# :: MutVar# s a -> MutVar# s b -> Int#
但设计者显然认为在不同类型的引用上使用该函数比其他方式更容易出错。
你不能安全地得出结论sameReference (r1 :: IORef a) (r2 :: IORef b) = True
意味着a
和b
是相同的。假设你有
sameRefSameType :: IORef a -> IORef b -> Maybe (a :~: b)
那你就可以轻松写
oops :: Coercible a b => IORef a -> a :~: b
oops r = fromJust (sameRefSameType r (coerce r))
产生任何两个可强制类型相等的虚假证据。您应该能够弄清楚如何使用 GADT 从那里到达 mkUC :: IO (a -> b)
.
我相信写
是安全的sameRefCoercibleTypes :: IORef a -> IORef b -> Maybe (Coercion a b)
既然 Daniel Wagner 提到了稳定的名字,我应该指出,在这种情况下,那些人的情况更糟。我需要先了解一些背景知识。假设你写
f :: Either x Int -> Either x Bool
f (Left x) = Left x
f (Right _) = Right False
在第一种情况下,分配一个新的 Left
构造函数只是为了改变类型是一种耻辱。所以 GHC 有一个低级优化(在核心到核心优化管道之后)试图把它变成(本质上)
f p@(Left x) = unsafeCoerce p
f (Right _) = Right False
这意味着您可以有 m :: Either x a
和 n :: Either x b
,其中 m
和 n
指的是同一个堆对象,尽管 a
和 b
具有完全不相关的类型。如果您为 m
创建一个稳定名称并为 n
创建一个稳定名称,那么这些稳定名称将比较相等!如果你存入甚至高达
sameSNCoercibleTypes
:: StableName a
-> StableName b
-> Maybe (Coercion a b)
然后您可以使用 m
和 n
到 "prove" Coercible (Either x a) (Either x b)
,您可以从中将任何 a
转换为任何 b
。这有点微妙,但因为它是可能的,否则假设是相当不安全的。