你如何在Haskell中进行泛型编程?

时间:2008-12-18 07:09:32

标签: haskell generic-programming

来自C ++,我觉得通用编程是不可或缺的。我想知道人们如何在Haskell中接近它?

如何在Haskell中编写通用交换函数?

Haskell中是否存在部分特化的等效概念?

在C ++中,我可以部分地使用特殊的泛型交换函数来处理泛型map / hash_map容器,该容器具有O(1)容器交换的特殊交换方法。你如何在Haskell中做到这一点,或者Haskell中泛型编程的典型例子是什么?

5 个答案:

答案 0 :(得分:29)

这与您关于Haskell和quicksort的其他问题密切相关。我想你可能至少需要阅读一本关于Haskell的书的介绍。听起来好像你还没有掌握关于它的关键点,即它禁止你修改现有变量的值。

交换(正如在C ++中所理解和使用的)就其本质而言,都是关于修改现有值的。我们可以使用名称来引用容器,并用完全不同的内容替换该容器,并将该操作专门用于特定容器的快速(和无异常),从而允许我们实现修改和发布方法(对于编写异常安全代码或尝试编写无锁代码至关重要。)

你可以在Haskell中编写一个通用交换,但它可能需要一对值并返回一个包含相同值的新对,其位置相反,或类似的东西。不是一回事,也没有相同的用途。通过挖掘内部地图并交换其各个成员变量来尝试将其专门化为地图是没有任何意义的,因为你不能在Haskell中做这样的事情(你可以做专业化,但不能修改变量)。

假设我们想在Haskell中“测量”一个列表:

measure :: [a] -> Integer

这是一种类型声明。这意味着函数measure获取任何内容的列表(a是泛型类型参数,因为它以小写字母开头)并返回一个Integer。因此,这适用于任何元素类型的列表 - 它在C ++中称为函数模板,或Haskell中的多态函数(与C ++中的多态类不同)。

我们现在可以通过为每个有趣的案例提供专业化来定义:

measure [] = 0

即。测量空列表,你得零。

这是一个涵盖所有其他案例的非常一般的定义:

measure (h:r) = 1 + measure r

LHS中括号中的位是一种模式。这意味着:拿一个清单,中断并调用它,然后调用剩下的部分r。这些名称是我们可以使用的参数。这将匹配任何列表上至少有一个项目。

如果你在C ++中尝试过模板元编程,这对你来说都是旧的,因为它涉及完全相同的样式 - 递归循环,专门化使递归终止。除了在Haskell中,它在运行时工作(特定值或模式的函数的特化)。

答案 1 :(得分:9)

正如Earwicker所说,这个例子在Haskell中并没有那么有意义。无论如何你绝对想拥有它,这里有类似的东西(交换一对的两部分),c& p来自交互式会话:

GHCi, version 6.8.2: http://www.haskell.org/ghc/  :? for help
Loading package base ... linking ... done.
Prelude> let swap (a,b) = (b,a)
Prelude> swap("hello", "world")
("world","hello")
Prelude> swap(1,2)
(2,1)
Prelude> swap("hello",2)
(2,"hello")

答案 2 :(得分:6)

在Haskell中,函数尽可能是通用的(多态的) - 编译器会推断出“最常见的类型”。例如,默认情况下,在没有类型签名的情况下,TheMarko的示例交换是多态的:

*主>让swap(a,b)=(b,a)
*主> :t swap
swap ::(t,t1) - > (t1,t)

至于部分专业化,ghc具有非98扩展名:
  file:///C:/ghc/ghc-6.10.1/doc/users_guide/pragmas.html#specialize-pragma

另外,请注意术语不匹配。在C ++,Java和C#中所谓的泛型在Haskell中称为多态。 Haskell中的“Generic”通常意味着polytypic:   http://haskell.readscheme.org/generic.html
但是,我使用泛型的c ++含义。

答案 3 :(得分:5)

在Haskell中,您将创建类型类。类型类与OO语言中的类不同。取Numeric类型它说任何作为类实例的东西都可以执行某些操作(+ - * /),因此Integer是Numeric的成员,并提供了被认为是Numeric所必需的函数的实现,可以在任何地方使用数字是预期的。

假设您希望能够吸引Ints和Strings。然后你将声明Int和String 类Foo的实例。现在,只要您看到类型(Foo a),就可以使用Int或String。

您无法直接添加整数和浮点数的原因是因为add具有类型(数字a)a - > a - > a是一个类型变量,就像常规变量一样,它只能绑定一次,所以一旦你将它绑定到Int,列表中的每一个都必须是Int。

答案 4 :(得分:2)

在Haskell书中读到足够的内容后,我真的了解了Earwicker的答案,我建议你也阅读类型类。我不确定“部分专业化”是什么意思,但听起来它们可能会接近。