列为Haskell类型签名中的约束变量?
而不是声明这个类型签名:
g :: Enum a => [a] -> [a]
我想使用这种风格:
h :: (Enum a, Functor f) => f a -> f a
除了换Functor出[]. (例如,因为我可能很高兴暂时在我的实施中不那么笼统)。
虽然我很想,但我不能使用以下内容,
i :: (Enum a, [] f) => f a -> f a
-- compilation error: Expected kind ‘* -> *’, but ‘f’ has kind ‘*’
因为[] f可能被解释为 的列表f,而不是f被解释为某物列表的约束变量。
以下工作,但我想知道是否可以在 Haskell 类型签名中将 List 声明为实际的约束变量。
j :: (Enum a) => [] a -> [] a
j :: (Enum a) => [] a -> [] a
回答
您可以将等式约束声明为
g :: (Enum a, [] ~ f) => f a -> f a
在合适的语言扩展下。将运算符读(~)作相等或全等。
-
在 GHC 命令行中设置
-XGADTs或之一-XTypeFamilies。 -
或者,放置
{-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-}在文件的顶部。
不过,真的值得吗?
- @dfeuer I just meant that the symbol `~` resembles the mathematical symbol for congruence.
回答
[]不是一个约束,它永远不会是。在 Haskell 中,每个类型都有一个kind,这有点像一个类型。又上一层了。
诸如[Int],String和之类的东西Double -> Double是具体类型。我们说他们有恩Type。喜欢[]或Maybe有种类的东西Type -> Type;它接受一个类型作为参数,然后产生一个类型本身。
另一方面,类似的东西Functor根本不同。Functor有种
Functor :: (Type -> Type) -> Constraint
因此,首先,它采用参数化类型,例如[]或Maybe作为参数。但即使这样做,它也会产生一个与Constrainta 根本不同的东西Type。现在,当我们写一个类型签名时
对的左边的东西=>是永远的约束和右面的东西=>是永远一个Type。放在[] a左侧=>比放在Functor a右侧没有任何意义。
听起来您想要表达的是“这件事必须是一个列表”,在这种情况下,以下任一方法就足够了
j :: (Enum a) => [a] -> [a]
j :: (Enum a) => [] a -> [] a
这两者是等价的,但前者是多少更地道。如果你后来决定“哦等等,我可以为任何函子做这个工作,你可以将类型签名更改为
j :: (Enum a, Functor f) => f a -> f a
并且这不会破坏j正在调用的任何代码,因为之前使用列表调用它的任何代码仍将使用有效Functor实例调用它。
注意:您会看到一些提到 kind 的旧文献*。一些文献(甚至某些地方的 GHC)会说Maybe有 kind * -> *。出于这些目的,*相当于Type。能够引用Typeas*是一个不幸的历史怪癖,并使 Haskell 解析器复杂化(Foo * Int相当于Foo Type Int还是乘法之类的(*) Foo Int?),所以现在的一般建议是使用Type来引用类型。