我想做的事情如下: (在伪代码中;这不能编译)
type Num a => Vector2d = (a,a)
或可能
type Num a => Vector2d a = (a,a)
但我不能这样做。
阅读后我觉得要实现这一目标,我需要RankNTypes
扩展程序或forall
关键字,但我无法理解这个...
有人可以帮忙吗?
编辑:
我管理,而是通过"猜测语法":
解决方案确实是RankNTypes
:
type Vec = forall a. Num a => (a,a)
这样可行,但RankNTypes
扩展程序
type Vec = Num a => (a,a)
同样有效。差异是什么,Num a =>
约束(看起来很自然)与排名 n 类型有什么关系?
所以问题仍然存在,但我正在寻找解释,而不是解决方案。
答案 0 :(得分:6)
type Vec = forall a. Num a => (a, a)
与
相同 type Vec = Num a => (a, a)
。
原因是GHC在最顶层的类型变量范围内隐式引入了没有相应forall
的每个类型变量,例如:
const :: a -> b -> a
const :: forall a b. a -> b -> a -- the same thing
在大多数情况下,类型同义词只是语法上的便利,因此每当您在类型签名中看到Vec
时,您只需将括号括在其定义周围并替换:
Vec -> Vec -> Integer
-- equals:
(forall a. Num a => (a, a)) -> (forall a. Num a => (a, a)) -> Integer
有一个奇怪的例外,你不能盲目地在类型中替换。如果您有类似的同义词:
type Vec a = Num a => (a, a)
然后Num
约束在替换后浮动到最外层范围:
vec = (100, 100) :: Vec Integer
-- vec has now type "(Integer, Integer)"
和相同类型变量的多个约束合并:
addVec :: Vec a -> Vec a -> Vec a
addVec (a, b) (c, d) = (a + c, b + d)
-- addVec has now effectively type "Num a => (a, a) -> (a, a) -> (a, a)
在上述情况下,类约束没有引入更高排名,因为约束绑定到外部范围中的变量。但是,当我们在数据构造函数中使用同义词时,约束将变为隐式(存在)字典:
type Foo a = Num a => a
data Bar a = Bar (Foo a) -- equals "data Bar a = Bar (Num a => a)"
-- requires ExistentialQuantifiaction or GADTs to compile
-- and of course, any data constructor blocks the outwards floating of the constraint:
type Baz = a -> (Foo a, ()) -- requires ImpredicativeTypes.
因此,总而言之,类型同义词中这些约束的行为相当不稳定。我们需要RankNTypes
或Rank2Types
来编写它们,但这似乎更像是一个实现工件而不是其他任何东西。我们可以争辩说语法可以用来向类型引入量词,这种证明RankNType
要求是合理的,但我们同样可以说编译器应该检测是否合理是否是新的量词,并相应地进行(就像它已经完成了引入的存在...)。