`<<<`运算符在elm中意味着什么?

时间:2014-12-12 10:29:36

标签: elm

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

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

无法在Elm syntax reference中找到它。

是否意味着,当字段更改时,而不是将content发送到updateChan,将toUpdate发送到updateChan

6 个答案:

答案 0 :(得分:108)

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

榆树型系统

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

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

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

静态类型意味着编译器确保编译期间所有函数和变量的类型都是正确的,因此您不会遇到运行时类型错误。换句话说,您永远不会有String -> String类型的函数接收或返回Int,这样的代码可以让它甚至无法编译。

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

如果再次看到相同的类型变量,则此类型变量的实例必须属于同一类型。例如,List.reverse的类型为List a -> List a。因此,如果您将List.reverse应用于整数列表(即类型为List Int的内容),则返回整数列表。这种函数无法获取整数列表,但返回字符串列表。这是由编译器保证的。

默认情况下,Elm中的所有函数均为curried。这意味着如果你有一个2参数的函数,它将被转换为1参数的函数,该函数返回1参数的函数。这就是为什么你运行Elm的应用程序语法与其他语言(如Java,C ++,C#,Python等)中的函数应用程序有很大不同。当你使用someFunction(arg1, arg2)时没有理由可以写someFunction arg1 arg2。为什么?因为实际上someFunction arg1 arg2实际上是((someFunction arg1) arg2)

Currying使partial application成为可能。假设您想部分应用List.memberList.member的类型为a -> List a -> Bool。我们可以将类型读作“List.member接受2个参数,类型为a并输入List a”。但我们也可以将类型读作“List.member取{type 1}}类型的1个参数。它返回类型a“的函数。因此,我们可以创建一个函数List a -> Bool,其类型为isOneMemberOf = List.member 1

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

中缀和前缀表示法

任何infix operator实际上是幕后的普通功能。只是当函数名只包含非字母数字符号(例如$,&lt; |,&lt;&lt;&lt;等)时,它放在2个参数之间,而不是在它们之前(就像普通函数一样) )。

但你仍然可以在{2}参数前放置一个像a -> (List a -> Bool)这样的二元运算符,方法是将它括在括号中,所以下面的2个函数应用程序是等价的:

+

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

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

功能组合

(<<)是一个函数组合运算符,在核心库Basics中定义。基础知识中的所有函数都被导入到不合格的Elm项目中,这意味着您不必编写addTwo : Int -> Int addTwo = (+) 2 addTwo 3 -- returns 5 ,默认情况下已经完成。

就像任何其他运算符一样,import Basics exposing (..)只是一个函数,就像任何其他运算符一样。它的类型是什么?

(<<)

因为(<<) : (b -> c) -> (a -> b) -> a -> c 是右关联的,所以这相当于:

->

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

a -> c

假设我们没有addOne = (+) 1 multTwo = (*) 2 ,只有(+),我们如何创建一个增加3而不是1的函数?很简单,我们将addOne组合3次:

addOne

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

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

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 << ffg是函数。当此构造h应用于值h << g << f时,您就会知道:

  • Elm首先将x应用于f
  • 然后将x应用于上一步的结果
  • 然后将g应用于上一步的结果

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

为什么&lt;&lt;而不是。

在Elm 0.13(参见announcement)之前,函数组合运算符为-50.0,其行为与当前(.)相同。来自F#语言的Elm 0.13采用了(<<)(参见Github issue)。 Elm 0.13还将(<<)添加为(>>)(<|)替换为函数应用程序运算符flip (<<)(|>)等效于($)

中缀函数调用

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

flip (<|)

榆树0.18 removed this feature。您不能再在Elm中执行此操作,但HaskellPureScript等语言仍然可以使用它。

答案 1 :(得分:22)

<<是一个函数组合 - 返回函数。

组合创建了一系列计算,功能链。该管道等待输入,当提供时,第一个函数开始计算,将输出发送到下一个等。

import Html

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

add9 =
    add 4 << add 5

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

注意在上面的示例中,我使用了partial application。这意味着我没有提供所有参数来实现功能,因此我得到了功能。

如果您在网络浏览器中运行以上示例并查看控制台输出,您将看到:

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

如果我们把它写成数学运算,它将如下所示:

4 + (5 + 2)
4 + 7

注意我们也可以使用转发版>>

阅读签名

查看此运营商的签名:

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

对于<<运算符,有一个函数b -> c作为第一个参数,函数a -> b作为第二个参数:

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

但是还有第三个参数a。因为->是右关联的,所以

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

相当于:

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

以便<<返回函数a -> c

结合性

在编程语言中,运算符的关联性(或固定性)是一个属性,用于确定在没有括号的情况下如何对相同优先级的运算符进行分组;即按每个运营商评估的顺序:

a = b = c被解析为a = (b = c)

中缀运营商

此处我将<<用作infix operator,但我们也可以将其用作括号括起来的前缀运算符:(<<) (b -> c) (a -> b)(<|) (add 4) (add 5)

elm&lt; 0.18 used to允许你使用普通函数并将它们用作中缀运算符。

关于<|运算符

的单词

<|是一个函数应用程序 - 返回值

我们基本上使用它而不是括号。

text (something ++ something)

可以写成

text <| something ++ something

所以看一下这个算子的signature

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

我们可以看到,对于<|运算符,第一个参数有一个函数a -> b,第二个参数有值a

(a -> b) <| a

然后返回b

我们可以使用函数应用程序<|获得相同的值:

v1 = add 4 <| add 5 <| 4
v2 = (add 4 << add 5) 4

答案 2 :(得分:11)

是功能构成。对于您的具体示例,它意味着

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

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

答案 3 :(得分:2)

第二次尝试:D

<<<|

<<<|之间的区别在于,<<用于编写函数,而<|用于省略括号。

为什么这样工作

让我们看一下here找到的类型注释:

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

此定义告诉我们,当您将两个函数传递给函数<<时,您将获得函数a -> c

带有demo

的示例
hi a =
    a + 2
hello a =
    a * 2
bye =
    hello << hi
c =
    bye 3

c返回值10

详细了解:

  • infix operators-函数左侧的第一个参数,
  • partial application-当您将一个参数传递给需要两个参数的函数时,您会得到一个期望一个参数的函数。

答案 4 :(得分:0)

javascript开发人员的移植:

--elm

(a << b) x

将会相似

//javasript

a(b(x))

<<>>被称为function composition

答案 5 :(得分: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