包装/展开通用量化类型

Wrapping / Unwrapping Universally Quantified Types

我导入了一个数据类型,X,定义为

data X a = X a

在本地,我定义了一个通用量化的数据类型,Y

type Y = forall a. X a

现在我需要定义两个函数,toYfromY。对于 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 被定义为同义词,而不是新类型,因此两个 as 在定义中不匹配。

但如果是这样,那为什么 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 的详细信息感兴趣。