为什么函数组合有时需要两个“。” 结合两个功能

Bry*_*and 5 haskell functional-programming function-composition

所以这个问题很简单,但我似乎无法理解这个概念。

要组成普通函数,可以执行以下操作:

lowerNoSpaces = filter (/= ' ') . map toLower
Run Code Online (Sandbox Code Playgroud)

但是,有时候,这有时行不通:

myConcatMap = concat . map
Run Code Online (Sandbox Code Playgroud)

它给出了错误:

<interactive>:236:1: error:
    * Non type-variable argument
        in the constraint: Foldable ((->) [a1])
      (Use FlexibleContexts to permit this)
    * When checking the inferred type
        concattMap :: forall a1 a2.
                      Foldable ((->) [a1]) =>
                      (a1 -> a2) -> [a2]
Run Code Online (Sandbox Code Playgroud)

但是当相同的函数表示为:

myConcatMap = (concat .) . map
Run Code Online (Sandbox Code Playgroud)

它完全按预期工作。

我知道这是有原因的,但是我一直盯着它看了一阵子,但仍然不太明白为什么原版不起作用而本原版却起作用。

为什么会有两个“。” 的?

che*_*ner 12

(.)Haskell语法的定义和知识中很容易得出这一点。

您从的更明确定义开始myConcatMap,即

\f -> \xs -> concat (map f xs)
Run Code Online (Sandbox Code Playgroud)

根据组合运算符的定义,您可以将其写为

\f -> concat . (map f)
Run Code Online (Sandbox Code Playgroud)

使用.前缀位置而不是中缀运算符来重写它。

\f -> (.) concat (map f)
Run Code Online (Sandbox Code Playgroud)

并添加一些多余的括号,因为函数应用是左关联的。

\f -> ((.) concat) (map f)
Run Code Online (Sandbox Code Playgroud)

使用部分语法重写此代码以.再次使中缀运算符

\f -> (concat .) (map f)
Run Code Online (Sandbox Code Playgroud)

并应用的定义,(.)一个更多的时间,使用功能(concat .)map

(concat .) . map
Run Code Online (Sandbox Code Playgroud)


lef*_*out 5

这是因为map是两个参数的函数,并且您只想concat在提供两个参数后才应用。请记住,Haskell多参数函数是经过咖喱处理的,即实际上

map :: (a->b) -> ([a]->[b])
Run Code Online (Sandbox Code Playgroud)

因此,如果您编写构图c . map,则的参数c必须为type [a]->[b]。但是,的参数concat应该是一个列表,即type [b]或实际上[[e]]

解决方案:

  • 显式传递第一个参数。

    myConcatMap f = concat . map f
    
    Run Code Online (Sandbox Code Playgroud)

    之所以map f可行[a] -> [b],是因为它不再是单参数函数,因此您可以concat在其前面进行编写。

  • concat在函数前面编写,这是应用map到其第一个参数的结果。这就是您在示例中所做的。