Collections “哈斯克尔”;“收藏”;语言设计

Collections “哈斯克尔”;“收藏”;语言设计,collections,haskell,language-design,Collections,Haskell,Language Design,为什么Haskell实现如此关注链表 例如,我知道数据。顺序更有效 与大多数列表操作(除了cons操作)一起使用,并且被大量使用; 然而,从句法上讲,它“几乎不受支持”。Haskell在函数抽象方面做了很多工作,例如Functor和Foldable类,但是它们的语法与默认列表的语法不兼容 如果在一个项目中,我想优化列表并用序列替换,或者如果我突然想要支持无限集合,并用列表替换序列,那么结果代码的更改是令人憎恶的 因此,我想我的疑惑可以具体化为以下问题: 为什么map的类型不等于(函子f)=>(a

为什么Haskell实现如此关注链表

例如,我知道数据。顺序更有效 与大多数列表操作(除了
cons
操作)一起使用,并且被大量使用; 然而,从句法上讲,它“几乎不受支持”。Haskell在函数抽象方面做了很多工作,例如Functor和Foldable类,但是它们的语法与默认列表的语法不兼容

如果在一个项目中,我想优化列表并用序列替换,或者如果我突然想要支持无限集合,并用列表替换序列,那么结果代码的更改是令人憎恶的

因此,我想我的疑惑可以具体化为以下问题:

  • 为什么
    map
    的类型不等于
    (函子f)=>(a->b)->fa->fb
  • 为什么不能将
    []
    (:)
    函数用于例如Data.Sequence中的类型

  • 我真的希望对此有一些解释,其中不包括“向后兼容性”或“它只是以这种方式增长”,但如果您认为没有,请让我知道。任何相关的语言扩展都是受欢迎的。

    我记得在某个地方读到,
    map
    默认用于列表,因为Haskell的新手如果犯了一个错误并看到了一个关于“functor”的复杂错误(他们对此一无所知),就会被推迟。因此,它们既有
    map
    又有
    fmap
    ,而不仅仅是
    map

    编辑:“某处”是《单子阅读器》第13期,第20页,脚注3:

    3您可能会问,为什么我们需要单独的映射功能。为什么不干脆消除电流呢 仅列出映射函数,并将fmap重命名为映射?这是个好问题。这个 通常的观点是,当不正确地使用map时,刚学习Haskell的人会更容易理解 宁可看到关于列表的错误,也不要看到关于函子的错误


    对于
    (:)
    (我很确定这不会回答您的问题,但仍然是

    我希望Haskell有更自由的函数名(mixfix!)。那么,列表构造函数(
    []
    )的语法就不会那么神奇了;它至少允许我们隐藏列表类型并对自己的类型使用相同的标记

    在列表类型和自定义序列类型之间迁移时,代码更改的数量将是最小的

    关于
    map
    ,您比较幸运。您可以随时隐藏map,并将其设置为与fmap相同

    import Prelude hiding(map)
    
    map :: (Functor f) => (a -> b) -> f a -> f b
    map = fmap
    

    前奏曲很棒,但它不是Haskell最好的部分。

    在讨论原因之前,这里是问题的摘要以及您可以做些什么。构造函数
    []
    (:)
    是为列表保留的,无法重新定义。如果计划对多个数据类型使用同一代码,请定义或选择表示要支持的接口的类型类,并使用该类中的方法。 下面是一些既适用于列表又适用于序列的通用函数。我不知道
    (:)
    ,但您可以自己编写

    • fmap
      而不是
      map
    • mempty
      而不是
      []
    • mappend
      而不是
      (++)
    如果您计划进行一次性数据类型替换,那么您可以为事物定义自己的名称,并在以后重新定义它们

    -- 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
    -}
    
    ——现在,使用列表
    类型列表a=[a]
    零=[]
    cons x xs=x:xs
    {-将来切换到Seq
    --类型列表a=序列a
    --零=空
    
    --cons x xs=x一个吹毛求疵的数据。序列对于“列表操作”来说并不更有效,对于序列操作来说更有效。也就是说,Data.list中的许多函数实际上是序列操作。数据中的手指树。序列必须为cons做更多的工作(对于7.8版,ghc支持重载列表文本,比较。例如,给定适当的
    IsList
    实例,您可以编写

    ['0' .. '9']             :: Set Char
    [1 .. 10]                :: Vector Int
    [("default",0), (k1,v1)] :: Map String Int
    ['a' .. 'z']             :: Text
    

    (引用自文档)。

    你所说的#2是什么意思?我可以想出两种合理的解释:(a)“为什么不能使现有的
    []
    具有更多态的类型?”;(b)“为什么
    []
    用于
    序曲。[
    而不是
    数据.Sequence.Seq
    ?”。“它只是以这种方式增长”非常接近。@Antal:我的问题是(a),因为我想知道为什么这些函数/构造函数没有更多多态性。我的推理是,使用的实际数据类型可以从键入中推断出来(例如,使用
    列表a
    Seq a
    )并且可以默认为列表。我怀疑您真正希望的是类似于GHC的东西,但应用于列表文字而不是字符串。不幸的是,这不仅不存在,而且似乎在我能找到的任何地方都没有建议过……这确实不是对我问题的回答,但更多的是澄清;我是aware我可以隐藏
    map
    ,并将其设置为等于
    fmap
    。我的问题是,为什么我必须这样做?(上面已经给出了充分的答案。)关于
    Data.Sequence.empty
    ?或者
    Control.Monad.mempty
    ?他们应该这样做。虽然我同意这会增加Haskell已经很陡峭的学习曲线,但在学习了基础知识之后,是否应该有一种方法来平衡这一点?难道没有
    前奏曲吗将所有这些仅列出的函数修复为它们的通用对应项?@Pepijn:您不必使用前奏,只需放入
    import Prelude()
    在您的代码中。对于我的大多数爱好/实验性Haskell编程,我使用了一个经过修改的前奏曲,除其他外,它将Headsink提到的列表函数替换为它们的通用等价物。对于更剧烈的更改(例如,为desugaring
    do
    符号替换
    Monad
    ),有