如何"阅读"榆树 - >操作员

clo*_*ach 8 elm

我真的很喜欢榆树,直到我遇到一个我以前从未见过的功能,想要了解它的输入和输出.

以声明foldl为例:

foldl : (a -> b -> b) -> b -> List a -> b
Run Code Online (Sandbox Code Playgroud)

我看着这个,不禁感觉好像有一组我遗漏的括号,或者其他一些关于这个操作符的关联性的细微之处(我找不到任何明确的文档).也许这只是一个使用语言的问题,直到我对它有一种"感觉",但我想有一种方法可以用英语"阅读"这个定义.

文档中查看示例...

foldl (::) [] [1,2,3] == [3,2,1]
Run Code Online (Sandbox Code Playgroud)

我希望函数签名读取如下内容:

给定一个函数,它接受一个ab并返回一个b,一个额外的b,和List,foldl返回b.

那是对的吗?

你有什么建议可以给像我这样的人,他们迫切希望输入被逗号描述,输入/输出更清楚地分开?

jac*_*obm 10

简短的回答

您正在寻找的缺失括号是由于->右关联的事实:类型(a -> b -> b) -> b -> List a -> b相当于(a -> b -> b) -> (b -> (List a -> b)).非正式地,在一个->s 链中,在最后->一个之前读取所有内容作为参数,并且只读取最右边的内容.

答案很长

您可能缺少的关键见解是currying - 如果您有一个带有两个参数的函数,您可以使用一个函数来表示它,该函数接受第一个参数并返回一个接受第二个参数然后返回结果的函数.

例如,假设您有一个带有add两个整数并将它们加在一起的函数.在Elm中,您可以编写一个函数,将两个元素作为元组并添加它们:

add : (Int, Int) -> Int
add (x, y) = x+y
Run Code Online (Sandbox Code Playgroud)

你可以称之为

add (1, 2)  -- evaluates to 3
Run Code Online (Sandbox Code Playgroud)

但是假设你没有元组.您可能认为没有办法编写此函数,但实际上使用currying可以将其写为:

add : Int -> (Int -> Int)
add x =
  let addx : Int -> Int
      addx y = x+y
  in
    addx
Run Code Online (Sandbox Code Playgroud)

也就是说,你编写一个函数来获取x并返回另一个函数,y并将其添加到原始函数中x.你可以用它来调用它

((add 1) 2)  -- evaluates to 3
Run Code Online (Sandbox Code Playgroud)

您现在可以add通过两种方式来思考:作为一个函数,它接受一个x和一个y并添加它们,或者作为一个"工厂"函数,它接受x值并生成addx只需一个参数并将其添加到的新的专用函数x.

"工厂"思考事物的方式每隔一段时间就会派上用场.例如,如果您有一个号码列表,numbers并且您希望为每个号码添加3,则可以直接呼叫List.map (add 3) numbers; 如果你写了元组版本而不是你必须写一些List.map (\y -> add (3,y)) numbers更尴尬的东西.

Elm来自编程语言的传统,它非常喜欢这种思考函数的方式并尽可能地鼓励它,所以Elm的函数语法旨在使其变得简单.为此,->右关联:a -> b -> c相当于a -> (b -> c).这意味着如果你没有括号,你所定义的是一个函数,它接受a并返回一个b -> c,我们可以将其视为一个函数,它接受一个a和一个b并返回一个c,或者等效一个函数,它取一个a并返回一个b -> c.

还有另一种语法准确可以帮助调用这些函数:函数应用程序是对联的.这样,((add 1) 2)上面的丑陋可以写成add 1 2.通过这种语法调整,除非你想部分应用一个函数,否则你根本不需要考虑currying - 只需用所有参数调用它,语法就可以解决了.