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

发布于 2024-07-11 07:15:21 字数 261 浏览 9 评论 0原文

来自 C++ 的我发现泛型编程是不可或缺的。 我想知道人们在 Haskell 中如何处理这个问题?

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

Haskell 中是否有等效的部分专业化概念?

在 C++ 中,我可以将通用交换函数部分专门化为通用 map/hash_map 容器的特殊交换函数,该容器具有用于 O(1) 容器交换的特殊交换方法。 你如何在 Haskell 中做到这一点,或者 Haskell 中泛型编程的典型示例是什么?

Coming from C++, I find generic programming indispensable. I wonder how people approach that in Haskell?

Say how do write generic swap function in Haskell?

Is there an equivalent concept of partial specialization in Haskell?

In C++, I can partially specialize the generic swap function with a special one for a generic map/hash_map container that has a special swap method for O(1) container swap. How do you do that in Haskell or what's the canonical example of generic programming in Haskell?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

甜味超标? 2024-07-18 07:15:22

正如 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")

As Earwicker sais, the example is not as meaningful in Haskell. If you absolutely want to have it anyway, here is something similar (swapping the two parts of a pair), c&p from an interactive session:

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")
太阳公公是暖光 2024-07-18 07:15:22

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

*Main> 让交换 (a,b) = (b,a)
*主要> :t 交换
交换 :: (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 中的“泛型”通常意味着多型:
http://haskell.readscheme.org/generic.html
但是,首先我使用泛型的 C++ 含义。

In Haskell, functions are as generic (polymorphic) as possible - the compiler will infer the "Most general type". For example, TheMarko's example swap is polymorphic by default in the absence of a type signature:

*Main> let swap (a,b) = (b,a)
*Main> :t swap
swap :: (t, t1) -> (t1, t)

As for partial specialization, ghc has a non-98 extension:
file:///C:/ghc/ghc-6.10.1/doc/users_guide/pragmas.html#specialize-pragma

Also, note that there's a mismatch in terminology. What's called generic in c++, Java, and C# is called polymorphic in Haskell. "Generic" in Haskell usually means polytypic:
http://haskell.readscheme.org/generic.html
But, aboe i use the c++ meaning of generic.

許願樹丅啲祈禱 2024-07-18 07:15:22

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

假设您希望能够 foo 整数和字符串。 然后你可以将 Int 和 String 声明为
类型类 Foo 的实例。 现在,无论您在哪里看到类型 (Foo a),都可以使用 Int 或 String。

之所以不能直接将int和float相加是因为add的类型是(Numeric a) a ->; 一个-> aa 是一个类型变量,就像常规变量一样,它只能绑定一次,因此一旦将其绑定到 Int,列表中的每个 a 都必须是 Int。

In Haskell you would create type classes. Type classes are not like classes in OO languages. Take the Numeric type class It says that anything that is an instance of the class can perform certain operations(+ - * /) so Integer is a member of Numeric and provides implementations of the functions necessary to be considered Numeric and can be used anywhere a Numeric is expected.

Say you want to be able to foo Ints and Strings. Then you would declare Int and String to be
instances of the type class Foo. Now anywhere you see the type (Foo a) you can now use Int or String.

The reason why you can't add ints and floats directly is because add has the type (Numeric a) a -> a -> a a is a type variable and just like regular variables it can only be bound once so as soon as you bind it to Int every a in the list must be Int.

最舍不得你 2024-07-18 07:15:22

在阅读了足够的 Haskell 书籍以真正理解 Earwicker 的答案之后,我建议您还阅读有关类型类的内容。 我不确定“部分专业化”是什么意思,但听起来它们可能很接近。

After reading enough in a Haskell book to really understand Earwicker's answer I'd suggest you also read about type classes. I'm not sure what “partial specialization” means, but it sounds like they could come close.

等风来 2024-07-18 07:15:21

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

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

你可以在 Haskell 中编写一个通用交换,但它可能需要一对值并返回一个包含相同值但位置相反的新对,或者类似的东西。 不是完全相同的东西,也没有相同的用途。 尝试通过挖掘该映射并交换其各个成员变量来将其专门化为映射是没有任何意义的,因为您不允许在 Haskell 中执行类似的操作(您可以进行专门化,但不能变量的修改)。

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

measure :: [a] -> Integer

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

我们现在可以通过为每个有趣的情况提供专门化来定义它:

measure [] = 0

即测量空列表,您会得到零。

这是一个非常通用的定义,涵盖了所有其他情况:

measure (h:r) = 1 + measure r

LHS 括号中的位是一种模式。 意思是:拿一个列表,把头去掉,称为h,剩下的部分称为r。 这些名称就是我们可以使用的参数。 这将匹配其中至少包含一项的任何列表。

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

This is closely related to your other question about Haskell and quicksort. I think you probably need to read at least the introduction of a book about Haskell. It sounds as if you haven't yet grasped the key point about it which is that it bans you from modifying the values of existing variables.

Swap (as understood and used in C++) is, by its very nature, all about modifying existing values. It's so we can use a name to refer to a container, and replace that container with completely different contents, and specialize that operation to be fast (and exception-free) for specific containers, allowing us to implement a modify-and-publish approach (crucial for writing exception-safe code or attempting to write lock-free code).

You can write a generic swap in Haskell, but it would probably take a pair of values and return a new pair containing the same values with their positions reversed, or something like that. Not really the same thing, and not having the same uses. It wouldn't make any sense to try and specialise it for a map by digging inside that map and swapping its individual member variables, because you're just not allowed to do things like that in Haskell (you can do the specialization, but not the modifying of variables).

Suppose we wanted to "measure" a list in Haskell:

measure :: [a] -> Integer

That's a type declaration. It means that the function measure takes a list of anything (a is a generic type parameter because it starts with a lowercase letter) and returns an Integer. So this works for a list of any element type - it's what would be called a function template in C++, or a polymorphic function in Haskell (not the same as a polymorphic class in C++).

We can now define that by providing specializations for each interesting case:

measure [] = 0

i.e. measure the empty list and you get zero.

Here's a very general definition that covers all other cases:

measure (h:r) = 1 + measure r

The bit in parentheses on the LHS is a pattern. It means: take a list, break off the head and call it h, call the remaining part r. Those names are then parameters we can use. This will match any list with at least one item on it.

If you've tried template metaprogramming in C++ this will all be old hat to you, because it involves exactly the same style - recursion to do loops, specialization to make the recursion terminate. Except that in Haskell it works at runtime (specialization of the function for particular values or patterns of values).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文