Haskell(类型声明)中的“a”是什么?

6

这可能是一个非常基本的问题,但似乎还未在SO上涉及。

最近我开始学Haskell,到目前为止类型声明主要包括以下内容:

Int
Bool
Float
etc, etc

现在我要介绍列表,看到了使用a的类型声明,比如下面这个函数遍历关联列表:

contains :: Int -> [(Int,a)] -> [a]
contains x list = [values | (key,values)<-list, x==key]

有人能解释一下这个 a 是什么以及它是如何工作的吗?观察后发现它似乎代表了每一种类型。这是否意味着我可以将任何类型的列表作为参数输入?

2个回答

13

是的,你说得对,它代表“任意类型” - 限制在于类型签名中的所有a必须解析为相同的类型。因此,您可以输入任何类型的列表,但是当您使用contains在列表中查找值时,您查找的值必须与列表的元素具有相同的类型 - 当然这是有道理的。


9
同样重要的是,a 并不是特殊的:任何小写标识符都意味着它。它是一个类型 变量,在同一个签名中可以存在多个不同的类型变量。例如,const :: a -> b -> a 接受两个参数,每个参数的类型可以是任意类型,并返回与其第一个输入相同类型的值。 - amalloy
3
我明白了,这很像通用的样子。所以,我猜,在大多数情况下,“a”通常代表着我的函数并不会太多地使用或更改元素。 - buydadip
2
@Bolboa:是的,它与其他语言中的“泛型”概念密切相关。而且是的,如果类型是完全泛型的,则直接对值本身的处理有限。Haskell确实有一种方法可以提供更多关于类型的信息(例如,“可进行相等比较”,“可打印”,“可像数字一样处理”),这允许您在值上执行更多处理;如果您对此感兴趣,请查阅文档或教程中的“类”。 - psmears
4
@Bolboa 确实,例如一个函数 f :: [a] -> [a] 不能在其输出中产生任何不在其输入中的东西,因为它是“通用的”。例如 f [1,2] 不能产生 [2,3]。同样地,你的 contains 函数也无法输出任何新的内容,你可以从类型上看出来,甚至不需要查看代码。 - chi
如果你想要在谷歌上搜索相关内容,@chi所描述的被称为“参数性质”。 - Benjamin Hodgson

1
在 Haskell 中,大写字母开头的类型是具体类型(IntBool)或类型构造器(MaybeEither),而小写字母开头的类型是 类型变量。一个函数在使用所有类型变量时都是隐式泛型的,因此这个例子:
contains :: Int -> [(Int, a)] -> [a]

这个是“*”的速记:

contains :: forall a. Int -> [(Int, a)] -> [a]

在C++中,forall的拼写是template
template<typename a>
list<a> contains(int, list<pair<int, a>>);

在Java和C#中,它用尖括号拼写:
list<a> contains<a>(int, list<pair<int, a>>);

当然,在这些编程语言中,泛型类型变量通常被称为 TUV,而在 Haskell 中则经常被称为 abc。这只是一种约定的差异。
* 这种语法是通过 GHC 中的 -XExplicitForAll 标志以及其他扩展启用的。

更加有用的是:-XScopedTypeVariables。此外,C++ 模板似乎本质上与 Haskell/ML/... 类型变量或甚至 Java 泛型有所不同。 - dfeuer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接