在 Haskell 中是否可能有健忘的类型同义词?
Is it possible to have forgetful type synonyms in Haskell?
如果我有一个我只是偶尔关心的带有虚参数的类型,比如这个:
data Foo p a b = Bar a b
是否有任何 hack 方法来编写类型同义词 Baz
,使得 Baz a b
对于某些我忘记的 p
是 Foo p a b
?
你不能这样做:
type Baz a b = Foo p a b
虽然你可以这样做(有适当的扩展):
type Baz a b = forall p.Foo p a b
这似乎不像我想要的那样,因为我无法将类型 Foo P1 a b
的值转换为类型 Baz a b
,并显示有关 "rigid type variable" 的消息.
是否需要另外一层构造函数来实现这个效果,如下所示?你能简单解释一下为什么吗?
data Baz' a b = forall p.Baz' (Foo p a b)
目前没有办法将其作为类型同义词来执行。但是,如果您有 GHC 7.10,则可以打开 PartialTypeSignatures
扩展并改写 Foo _ a b
。使用 -fno-warn-partial-type-signatures
要求 GHC 不要警告您以这种方式留下的每个漏洞。
这不能用类型同义词可靠地完成。您需要存在类型或等级 n 类型。
问题是 Haskell 允许类型同义词完全互换。也就是说,当你定义 type Baz a b = Foo p a b
时,那么在你有 Foo p a b
的每个上下文中,你都可以使用 Baz a b
,反之亦然。因此,例如,如果您有这种类型的函数:
f1 :: Foo Something a b -> Whatever Something b a
然后由于可替代性,那将是与此相同的类型:
f1 :: Baz a b -> Whatever Something b a
像这样:
f1 :: Foo p a b -> Whatever Something b a
...然后你可以专注于此:
f1 :: Foo SomethingElse a b -> Whatever Something b a
那么,你能做什么?一种是定义存在性包装器类型:
{-# LANGUAGE ExistentialTypes #-}
data Baz a b = forall p. Baz (Foo p a b)
做同样事情的替代方法:
{-# LANGUAGE GADTs #-}
data Baz a b where
Baz :: Foo p a b -> Baz a b
第二种方法:rank-n 类型和连续传递样式:
{-# LANGUAGE RankNTypes #-}
-- To consume one of these, you pass a "callback" function to `runBaz`,
-- which is not allowed to restrict the type variable `p`.
newtype Baz a b = Baz { runBaz :: forall p r. (Foo p a b -> r) -> r }
makeBaz :: Foo p a -> Baz a b
makeBaz foo = Baz ($foo)
第三种方法,你已经尝试过并且(我被告知)效果不佳:键入同义词 + 指示类型(需要 forall
同义词作为类型参数出现在许多案例)。
如果我有一个我只是偶尔关心的带有虚参数的类型,比如这个:
data Foo p a b = Bar a b
是否有任何 hack 方法来编写类型同义词 Baz
,使得 Baz a b
对于某些我忘记的 p
是 Foo p a b
?
你不能这样做:
type Baz a b = Foo p a b
虽然你可以这样做(有适当的扩展):
type Baz a b = forall p.Foo p a b
这似乎不像我想要的那样,因为我无法将类型 Foo P1 a b
的值转换为类型 Baz a b
,并显示有关 "rigid type variable" 的消息.
是否需要另外一层构造函数来实现这个效果,如下所示?你能简单解释一下为什么吗?
data Baz' a b = forall p.Baz' (Foo p a b)
目前没有办法将其作为类型同义词来执行。但是,如果您有 GHC 7.10,则可以打开 PartialTypeSignatures
扩展并改写 Foo _ a b
。使用 -fno-warn-partial-type-signatures
要求 GHC 不要警告您以这种方式留下的每个漏洞。
这不能用类型同义词可靠地完成。您需要存在类型或等级 n 类型。
问题是 Haskell 允许类型同义词完全互换。也就是说,当你定义 type Baz a b = Foo p a b
时,那么在你有 Foo p a b
的每个上下文中,你都可以使用 Baz a b
,反之亦然。因此,例如,如果您有这种类型的函数:
f1 :: Foo Something a b -> Whatever Something b a
然后由于可替代性,那将是与此相同的类型:
f1 :: Baz a b -> Whatever Something b a
像这样:
f1 :: Foo p a b -> Whatever Something b a
...然后你可以专注于此:
f1 :: Foo SomethingElse a b -> Whatever Something b a
那么,你能做什么?一种是定义存在性包装器类型:
{-# LANGUAGE ExistentialTypes #-}
data Baz a b = forall p. Baz (Foo p a b)
做同样事情的替代方法:
{-# LANGUAGE GADTs #-}
data Baz a b where
Baz :: Foo p a b -> Baz a b
第二种方法:rank-n 类型和连续传递样式:
{-# LANGUAGE RankNTypes #-}
-- To consume one of these, you pass a "callback" function to `runBaz`,
-- which is not allowed to restrict the type variable `p`.
newtype Baz a b = Baz { runBaz :: forall p r. (Foo p a b -> r) -> r }
makeBaz :: Foo p a -> Baz a b
makeBaz foo = Baz ($foo)
第三种方法,你已经尝试过并且(我被告知)效果不佳:键入同义词 + 指示类型(需要 forall
同义词作为类型参数出现在许多案例)。