包装/展开通用量化类型
Wrapping / Unwrapping Universally Quantified Types
我导入了一个数据类型,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
?
之外,还有什么办法可以解决这个问题吗?
你声称定义了一个存在类型,但你没有。
type Y = forall a. X a
定义了一个通用量化类型。对于具有类型 Y
的东西,它必须具有类型 X a
for every a
。要创建存在量化类型,您总是需要使用 data
,而且我发现 GADT 语法比传统的存在性语法更容易理解。
data Y where
Y :: forall a . X a -> Y
那里的 forall
实际上是可选的,但我认为澄清了一些事情。
我现在太困了,无法解决您的其他问题,但如果没有其他人解决,我明天会再试一次。
备注:
这更像是一条评论,但我不能真的把它放在那里,因为它会变得不可读;请原谅我这一次。
除了 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
——一个来自构造函数 Y
,一个来自 X a
——实际上,如果你剥离定义并尝试编译:fromY (Y a) = a
编译器会告诉你 type a
escapes:
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
但这可能不会太有用。
问题是您通常应该将 forall a
限制得更多一些(使用 type-类)以获得任何有意义的行为——当然我在这里帮不上什么忙。
您可能会对这个 wiki article 的详细信息感兴趣。
我导入了一个数据类型,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
?
你声称定义了一个存在类型,但你没有。
type Y = forall a. X a
定义了一个通用量化类型。对于具有类型 Y
的东西,它必须具有类型 X a
for every a
。要创建存在量化类型,您总是需要使用 data
,而且我发现 GADT 语法比传统的存在性语法更容易理解。
data Y where
Y :: forall a . X a -> Y
那里的 forall
实际上是可选的,但我认为澄清了一些事情。
我现在太困了,无法解决您的其他问题,但如果没有其他人解决,我明天会再试一次。
备注:
这更像是一条评论,但我不能真的把它放在那里,因为它会变得不可读;请原谅我这一次。
除了 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
——一个来自构造函数 Y
,一个来自 X a
——实际上,如果你剥离定义并尝试编译:fromY (Y a) = a
编译器会告诉你 type a
escapes:
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
但这可能不会太有用。
问题是您通常应该将 forall a
限制得更多一些(使用 type-类)以获得任何有意义的行为——当然我在这里帮不上什么忙。
您可能会对这个 wiki article 的详细信息感兴趣。