Haskell“collections”语言设计 [英] Haskell "collections" language design

查看:127
本文介绍了Haskell“collections”语言设计的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么Haskell的实现关注链表呢?



例如,我知道Data.Sequence是更高效的
与大多数列表操作(除了 cons 操作),并且使用很多;
在语法上,虽然,它是几乎不支持。 Haskell对功能抽象做了大量的工作,例如Functor和Foldable类,但是它们的语法与默认列表的语法不兼容。



如果,在项目中,我想优化并用序列替换我的列表 - 或者如果我突然想要对无限集合的支持,并用列表替换我的序列 - 结果代码更改是可憎的。



所以我想我的疑问可以在具体的问题,如:


  1. 为什么不是 map 等于(Functor f)=> (a→b)→ f a - > fb

  2. 为什么 []
    <$>


  3. 我真的希望有是一些解释,这不包括单词向后兼容性或它只是增长那种方式,虽然如果你认为没有,请让我知道。欢迎任何相关的语言扩展。

    解决方案

    在介绍为什么之前,这里是一个问题的总结,关于它。构造函数 [] (:)是为列表保留的,不能重新定义。如果您打算对多个数据类型使用相同的代码,那么请定义或选择一个类型类来表示您要支持的接口,并使用该类中的方法。
    下面是一些在列表和序列上同时工作的通用函数。我不知道(:)的概括,但你可以自己写。




    • fmap 而不是映射

    • mempty 而不是 []

    • mappend 而不是(++)



       - 现在,您可以为自己定义自己的名称,然后重新定义。 ,使用列表
    type List a = [a]
    nil = []
    cons x xs = x:xs

    { - 在未来切换到Seq b $ b - type List a = Seq a
    - nil = empty
    - cons x xs = x< | xs
    - }

    注意 [] (:)是构造函数:您还可以使用它们进行模式匹配。模式匹配特定于一个类型构造函数,因此您无法扩展模式以处理新的数据类型,而无需重写pattern-matchign代码。






    为什么Haskell中有很多列表特定的内容



    列表通常用于表示连续的计算比数据。在命令式语言中,您可以使用循环构建一个集合,创建元素并将它们逐个插入集合中。在Haskell中,通过创建一个列表,然后将列表传递给 Set.fromList ,可以做同样的事情。由于列表如此紧密地匹配这种计算抽象,它们有一个不太可能被另一个数据结构取代的地方。



    事实上,一些函数是列表特定的当他们可以是通用的。一些常用的函数,如 map 是特定于列表的,这样新用户可以少学习。特别地,它们提供更简单和(决定的)更容易理解的错误消息。由于可以使用通用函数,问题实际上只是一个语法不便。值得注意的是,Haskell语言的实现有非常少的列表特定的代码,所以新的数据结构和方法可以像内置一样高效。



    有几个类是列表的有用泛化:




    • code> fmap ,映射的泛化

    • Monoid 用于具有列表式结构的集合的用法。空列表 [] 通过 mempty 推广到其他容器,列表连接(+ + Monad 提供了用于将集合解释为计算的方法。

    • Traversable 用于在集合上运行计算。



    其中,只有Functor和Monad在有影响力的Haskell 98规范中,在不同程度上由图书馆作者,取决于图书馆何时被书写和如何积极地维护。核心库已经很好地支持新的接口。


    Why is the Haskell implementation so focused on linked lists?

    For example, I know Data.Sequence is more efficient with most of the list operations (except for the cons operation), and is used a lot; syntactically, though, it is "hardly supported". Haskell has put a lot of effort into functional abstractions, such as the Functor and the Foldable class, but their syntax is not compatible with that of the default list.

    If, in a project I want to optimize and replace my lists with sequences - or if I suddenly want support for infinite collections, and replace my sequences with lists - the resulting code changes are abhorrent.

    So I guess my wondering can be made concrete in questions such as:

    1. Why isn't the type of map equal to (Functor f) => (a -> b) -> f a -> f b?
    2. Why can't the [] and (:) functions be used for, for example, the type in Data.Sequence?

    I am really hoping there is some explanation for this, that doesn't include the words "backwards compatibility" or "it just grew that way", though if you think there isn't, please let me know. Any relevant language extensions are welcome as well.

    解决方案

    Before getting into why, here's a summary of the problem and what you can do about it. The constructors [] and (:) are reserved for lists and cannot be redefined. If you plan to use the same code with multiple data types, then define or choose a type class representing the interface you want to support, and use methods from that class. Here are some generalized functions that work on both lists and sequences. I don't know of a generalization of (:), but you could write your own.

    • fmap instead of map
    • mempty instead of []
    • mappend instead of (++)

    If you plan to do a one-off data type replacement, then you can define your own names for things, and redefine them later.

    -- For now, use lists
    type List a = [a]
    nil = []
    cons x xs = x : xs
    
    {- Switch to Seq in the future
    -- type List a = Seq a
    -- nil = empty
    -- cons x xs = x <| xs
    -}
    

    Note that [] and (:) are constructors: you can also use them for pattern matching. Pattern matching is specific to one type constructor, so you can't extend a pattern to work on a new data type without rewriting the pattern-matchign code.


    Why there's so much list-specific stuff in Haskell

    Lists are commonly used to represent sequential computations, rather than data. In an imperative language, you might build a Set with a loop that creates elements and inserts them into the set one by one. In Haskell, you do the same thing by creating a list and then passing the list to Set.fromList. Since lists so closely match this abstraction of computation, they have a place that's unlikely to ever be superseded by another data structure.

    The fact remains that some functions are list-specific when they could have been generic. Some common functions like map were made list-specific so that new users would have less to learn. In particular, they provide simpler and (it was decided) more understandable error messages. Since it's possible to use generic functions instead, the problem is really just a syntactic inconvenience. It's worth noting that Haskell language implementations have very little list-speficic code, so new data structures and methods can be just as efficient as the "built-in" ones.

    There are several classes that are useful generalizations of lists:

    • Functor supplies fmap, a generalization of map.
    • Monoid supplies methods useful for collections with list-like structure. The empty list [] is generalized to other containers by mempty, and list concatenation (++) is generalized to other containers by mappend.
    • Applicative and Monad supply methods that are useful for interpreting collections as computations.
    • Traversable and Foldable supply useful methods for running computations over collections.

    Of these, only Functor and Monad were in the influential Haskell 98 spec, so the others have been overlooked to varying degrees by library writers, depending on when the library was written and how actively it was maintained. The core libraries have been good about supporting new interfaces.

    这篇关于Haskell“collections”语言设计的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆