以下代码来自Elm Form Example第122行,<<
运算符是什么意思?
Field.field Field.defaultStyle (Signal.send updateChan << toUpdate) "" content
无法在Elm syntax reference中找到它。
是否意味着,当字段更改时,而不是将content
发送到updateChan
,将toUpdate
发送到updateChan
?
答案 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
,这样的代码可以让它甚至无法编译。
您还可以通过使用类型变量替换具体类型(如String
或Maybe 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.member
。 List.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 << f
,f
,g
是函数。当此构造h
应用于值h << g << f
时,您就会知道:
x
应用于f
x
应用于上一步的结果g
应用于上一步的结果因此h
等于(negate << (*) 10 << sqrt) 25
,因为你首先取25的平方根得到5,然后你将5乘以10得到50,然后取消50得到-50。
在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中执行此操作,但Haskell和PureScript等语言仍然可以使用它。
答案 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)
答案 3 :(得分:2)
第二次尝试:D
<<
与<|
<<
和<|
之间的区别在于,<<
用于编写函数,而<|
用于省略括号。
让我们看一下here找到的类型注释:
<< : (b -> c) -> (a -> b) -> a -> c
此定义告诉我们,当您将两个函数传递给函数<<
时,您将获得函数a -> c
。
hi a =
a + 2
hello a =
a * 2
bye =
hello << hi
c =
bye 3
c
返回值10
。
答案 4 :(得分:0)
答案 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