列为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来引用类型。


以上是列为Haskell类型签名中的约束变量?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>