我导入了一个数据类型X
,定义为
data X a = X a
在本地,我已经定义了一个通用量化的数据类型Y
type Y = forall a. X a
现在我需要定义两个函数toY
和fromY
。对于fromY
,此定义可以正常工作:
fromY :: Y -> X a
fromY a = a
但如果我为toY
尝试相同的操作,我会收到错误
Couldn't match type 'a' with 'a0'
'a' is a rigid type variable bound by the type signature for 'toY :: X a -> y'
'a0' is a rigid type variable bound by the type signature for 'toY :: X a -> X a0'
Expected type: X a0
Actual type: X a
如果我理解正确,toY
的类型签名将扩展为forall a. X a -> (forall a0. X a0)
,因为Y
被定义为同义词,而不是新类型,因此两个a
定义中的s不匹配。
但如果是这种情况,为什么fromY
成功检查?除了使用unsafeCoerce
之外,还有什么方法可以解决这个问题吗?
答案 0 :(得分:7)
您声称要定义存在类型,但您没有。
type Y = forall a. X a
定义了一种通用量化类型。对于类型为Y
的内容,对于每个 X a
,其类型必须为a
。要创建一个存在量化的类型,您总是需要使用data
,并且我发现GADT语法比传统的存在语法更容易理解。
data Y where
Y :: forall a . X a -> Y
forall
实际上是可选的,但我认为澄清了事情。
我现在太困了以解决你的其他问题,但如果没有其他人的话,明天我会再试一次。
答案 1 :(得分:6)
这更像是一个评论,但我无法真正把它放在那里,因为它本来是不可读的;请原谅我一次。
除了dfeuer已经告诉过你的内容之外,你可能会看到(当你使用他的答案时)toY
现在很容易做到,但你可能无法定义fromY
- 因为你基本上失去了类型信息,因此无效:
{-# LANGUAGE GADTs #-}
module ExTypes where
data X a = X a
data Y where
Y :: X a -> Y
fromY :: Y -> X a
fromY (Y a) = a
在这里你有两个不同的a
s - 一个来自构造函数Y
,一个来自X a
- 实际上如果你去掉定义并尝试编译:{{1}}编译器会告诉你类型fromY (Y a) = a
转义:
a
我认为你现在唯一能做的就是这样:
Couldn't match expected type `t' with actual type `X a'
because type variable `a' would escape its scope
This (rigid, skolem) type variable is bound by
a pattern with constructor
Y :: forall a. X a -> Y,
in an equation for `fromY'
但这可能不会太有用。
问题是你通常应该约束useY :: (forall a . X a -> b) -> Y -> b
useY f (Y x) = f x
那里(使用类型类)以获得任何有意义的行为 - 但当然我在这里无能为力。
这个wiki article可能会对您有所了解。