64

下面的代码取自Elm Form Example第 122 行,<<运算符是什么意思?

Field.field Field.defaultStyle (Signal.send updateChan << toUpdate) "" content

在Elm 语法参考中找不到它。

这是否意味着,当字段更改时,而不是将其发送contentupdateChan,发送toUpdateupdateChan

4

6 回答 6

135

<<是一个函数组合运算符,在核心库中定义Basics。Basics 中的所有功能都被不合格地导入到 Elm 项目中。

Elm 的类型系统

让我们回顾一下 Elm 类型系统的基础知识。

Elm 是静态类型的。这意味着在 Elm 中每个变量或函数都有一个类型,并且这个类型永远不会改变。Elm 中的类型示例如下:

  • Int
  • String
  • Maybe Bool
  • { name : String, age : Int }
  • Int -> Int
  • Int -> String -> Maybe Char.

静态类型意味着编译器在编译过程中确保所有函数和变量的类型都是正确的,因此您不会出现运行时类型错误。换句话说,你永远不会有一个String -> String接收或返回类型的函数Int,允许这个的代码甚至不会编译。

您还可以通过将具体类型(例如String或)Maybe Int替换为类型变量来使您的函数具有多态性,该类型变量是任意小写字符串,例如a. 许多 Elm 核心函数是类型多态的,例如List.isEmpty具有 type List a -> Bool。它接受List某种类型的 a 并返回 type 的值Bool

如果您再次看到相同类型的变量,则该类型变量的实例必须是相同类型。例如List.reverse有类型List a -> List a。因此,如果您应用于List.reverse整数列表(即具有 type 的东西List Int),它将返回一个整数列表。这样的函数不可能接受一个整数列表,而是返回一个字符串列表。这是由编译器保证的。

Elm 中的所有函数都是默认柯里化的。这意味着如果您有一个有 2 个参数的函数,它将被转换为一个有 1 个参数的函数,该函数返回一个有 1 个参数的函数。这就是为什么 Elm 的函数应用程序语法与 Java、C++、C#、Python 等其他语言中的函数应用程序如此不同的原因。没有理由写someFunction(arg1, arg2),当你可以写的时候someFunction arg1 arg2。为什么?因为在现实someFunction arg1 arg2中其实是((someFunction arg1) arg2)

柯里化使部分应用成为可能。假设您要部分应用List.member. List.member有一个类型a -> List a -> Bool。我们可以将类型解读为“<code>List.member 接受 2 个参数,分别为 typea和 type List a”。但我们也可以将类型解读为“<code>List.member 接受 1 个类型的参数a。它返回一个类型为List a -> Bool”的函数。因此我们可以创建一个函数isOneMemberOf = List.member 1,它的类型为List Int -> Bool.

这意味着->函数的类型注释是右关联的。换句话说,a -> List a -> Bool与 相同a -> (List a -> Bool)

中缀和前缀表示法

任何中缀运算符实际上都是幕后的普通函数。只是当函数名称仅由非字母数字符号(如 $、<|、<< 等)组成时,它被放置在 2 个参数之间,而不是在它们前面(如普通函数)。

但是您仍然可以将二元运算符放在+2 个参数的前面,方法是将其括在括号中,因此下面的 2 个函数应用程序是等效的:

2 + 3 -- returns 5
(+) 2 3 -- returns 5, just like the previous one

中缀运算符只是普通函数。他们没有什么特别的。您可以像任何其他功能一样部分应用它们:

addTwo : Int -> Int
addTwo = (+) 2

addTwo 3 -- returns 5

功能组成

(<<)是一个函数组合运算符,在核心库中定义Basics。所有基础功能都被导入到 Elm 项目中,这意味着您不必编写import Basics exposing (..),默认情况下已经完成。

因此,就像任何其他运算符一样,(<<)它只是一个函数,就像任何其他运算符一样。它的类型是什么?

(<<) : (b -> c) -> (a -> b) -> a -> c

因为->是右结合的,这相当于:

(<<) : (b -> c) -> (a -> b) -> (a -> c)

换句话说,分别(<<)接受两个类型的函数b -> ca -> b,并返回一个类型的函数a -> c。它将2个功能合二为一。这是如何运作的?为简单起见,让我们看一个人为的例子。假设我们有 2 个简单的函数:

addOne = (+) 1
multTwo = (*) 2

假设我们没有(+), only addOne,我们将如何创建一个加 3 而不是 1 的函数?很简单,我们addOne一起作曲 3 次:

addThree : Int -> Int
addThree = addOne << addOne << addOne

如果我们想创建一个加 2 然后乘以 4 的函数怎么办?

ourFunction : Int -> Int
ourFunction = multTwo << multTwo << addOne << addOne

(<<)从右到左组成函数。但是上面的例子很简单,因为所有的类型都是一样的。我们如何找到列表中所有偶数立方的总和?

isEven : Int -> Bool
isEven n = n % 2 == 0

cube : Int -> Int
cube n = n * n * n

ourFunction2 : List Int -> Int
ourFunction2 = List.sum << filter isEven << map cube

(>>)是同一个函数,但是参数翻转了,所以我们可以从左到右编写相同的组合:

ourFunction2 = map cube >> filter isEven >> List.sum

回顾

当你看到类似的东西时h << g << f,你就知道f, g,h是函数。当将此构造h << g << f应用于 valuex时,您会知道:

  • 榆树首先适用fx
  • 然后应用于g上一步的结果
  • 然后应用于h上一步的结果

因此(negate << (*) 10 << sqrt) 25等于-50.0,因为首先取 25 的平方根得到 5,然后将 5 乘以 10 得到 50,然后将 50 取反得到 -50。

为什么 << 而不是 .

在 Elm 0.13(见公告)之前,函数组合运算符是(.),其行为与 current 相同(<<)(<<)在 Elm 0.13 中采用 F# 语言(参见Github issue)。Elm 0.13 还添加(>>)了等价于flip (<<),并(<|)作为函数应用运算符的替代品($),以及(|>)等价于flip (<|).

中缀函数调用

您可能想知道是否可以将普通的字母数字函数名转换为中缀二元运算符。在 Elm 0.18 之前,您会使用反引号来制作函数中缀,因此低于 2 将是等效的:

max 1 2 -- returns 2
1 `max` 2 -- returns 2

Elm 0.18删除了这个特性。你不能在 Elm 中做到这一点,但HaskellPureScript等语言仍然拥有它。

于 2015-08-18T16:50:55.760 回答
25

<< is a function composition - returns function.

Composition creates a pipe of computations, chain of functions. This pipe waits for input, and when provided, first function starts computation, sends output to next etc.

import Html

add x y =
    Debug.log "x" x + Debug.log "y" y

add9 =
    add 4 << add 5

main =
    Html.text <| toString <| add9 2

Note: In the above example I use partial application. It means that I don't provide all parameters to function and as a result I get function.

If you run above example in web browser and look at the console output, you will see:

x: 5
y: 2
x: 4
y: 7

If we write it as math operations it will look like this:

4 + (5 + 2)
4 + 7

Note: We can also use forward version >>.

Reading signatures

Looking at signature of this operator:

(<<) : (b -> c) -> (a -> b) -> a -> c

For << operator, there is a function b -> c as the first parameter, and a function a -> b as the second:

(b -> c) << (a -> b)

But there is also a third parameter a. Because -> is right-associative, then

(<<) : (b -> c) -> (a -> b) -> a -> c

is equivalent to:

(<<) : (b -> c) -> (a -> b) -> (a -> c).

So that << returns function a -> c.

Associativity

In programming languages, the associativity (or fixity) of an operator is a property that determines how operators of the same precedence are grouped in the absence of parentheses; i.e. in what order each operator is evaluated:

a = b = c is parsed as a = (b = c)

Infix operator

Here I use << as infix operator, but we can also use it as a prefix operator enclosing it with parenthesis: (<<) (b -> c) (a -> b) or (<|) (add 4) (add 5).

elm < 0.18 used to let you take normal functions and use them as infix operators.

A word about <| operator

<| is a function application - returns value

We basically use it instead of parentheses.

text (something ++ something)

can be written as

text <| something ++ something

So looking at signature of this operator:

(<|) : (a -> b) -> a -> b

we can see that for <| operator, there is a function a -> b as the first parameter, and value a as the second:

(a -> b) <| a

and it returns b.

We can get the same value with function application <|:

v1 = add 4 <| add 5 <| 4
v2 = (add 4 << add 5) 4
于 2016-05-29T12:41:39.337 回答
11

是功能组合。对于您的具体示例,这意味着

\x -> (Signal.send updateChan (toUpdate x))

在 elm 中,它不是语法的一部分,而是标准库的一部分:Basics.<<

于 2014-12-12T10:46:46.490 回答
4

我的第二次尝试:D

<<对比<|

<<和之间的区别在于<|用于<<组合函数和<|用于省略括号。

为什么它会这样工作

让我们看看在这里找到的类型注释:

<< : (b -> c) -> (a -> b) -> a -> c

这个定义告诉我们,当你将两个函数传递给 function<<时,你会得到 function a -> c

带有演示的示例

hi a =
    a + 2
hello a =
    a * 2
bye =
    hello << hi
c =
    bye 3

c返回值10

阅读更多关于:

  • 中缀运算符- 函数左侧的第一个参数,
  • 部分应用- 当您将一个参数传递给需要两个参数的函数时,您会得到需要一个参数的函数。
于 2018-07-29T21:05:27.153 回答
4

开发者说明javascript

--elm

(a << b) x

会类似

//javasript

a(b(x))

<<或者>>称为函数组合

于 2019-04-16T05:00:06.873 回答
0

<|在这个回复中,我展示了和之间的区别<<

> addOne a = a + 1
<function> : number -> number

代码示例<|

> addThree a = addOne <| addOne <| addOne a
<function> : number -> number
> addThree 4
7 : number

addThree基于功能组合的替代版本<<

> addThreeComposition = addOne << addOne << addOne
<function> : number -> number
> addThreeComposition 4
7 : number
于 2021-05-23T06:48:01.897 回答