“<<”运算符在榆树中意味着什么?
在下面的代码取自Elm Form Example 122行, <<
运算符是什么意思?
Field.field Field.defaultStyle (Signal.send updateChan << toUpdate) "" content
无法在Elm语法参考中find它。
这是否意味着,当字段发生变化时,而不是将其content
发送到updateChan
,请发送toUpdate
到updateChan
?
<<
是一个函数组合运算符,在核心库Basics
定义。 基础的所有function都被导入到不合格的Elm项目中。
榆木的types系统
让我们回顾一下Elmtypes系统的基础知识。
榆木是静态types的 。 这意味着在Elm中,每个variables或者函数都有一个types,这个types永远不会改变。 Elmtypes的例子是:
-
Int
-
String
-
Maybe Bool
-
{ name : String, age : Int }
-
Int -> Int
-
Int -> String -> Maybe Char
。
静态types意味着编译器确保在编译期间所有函数和variables的types都是正确的,所以你没有运行时types错误。 换句话说,你将永远不会有一个String -> String
types的函数String -> String
接收或返回Int
,允许这个代码甚至不会编译。
你也可以通过用一个typesvariables(如一个任意的小写string,如a
replace一个具体types(比如String
或Maybe Int
来使得你的函数具有多态性。 许多Elm核心函数都是types多态的,例如List.isEmpty
的types为List a -> Bool
。 它需要一个types的List
并返回一个Bool
types的值。
如果再次看到相同的typesvariables,则此typesvariables的实例必须是相同的types。 例如List.reverse
有typesList a -> List a
。 所以,如果你将List.reverse
应用到整数列表(例如,types为List Int
东西),它将返回一个整数列表。 没有办法这样的函数可以采取一个整数列表,但返回一个string列表。 这是由编译器保证的。
Elm中的所有函数都默认为curry 。 这意味着如果你有一个2个参数的函数,它将被转换成1个参数的函数,返回1个参数的函数。 这就是为什么你的函数的应用程序语法与Java,C ++,C#,Python等其他语言的函数应用程序是如此不同。当你写一些someFunction arg1 arg2
时someFunction(arg1, arg2)
没有必要写一些someFunction(arg1, arg2)
。 为什么? 因为实际上一些someFunction arg1 arg2
实际上是((someFunction arg1) arg2)
。
柯里化使部分应用成为可能。 假设你想部分应用List.member
。 List.member
有一个typesa -> List a -> Bool
。 我们可以读取types为“ List.member
需要2个参数,typesa
和typesList a
”。 但是我们也可以将这个types读作“ List.member
”, List.member
需要a
types为a
参数。 它返回一个List a -> Bool
types的函数。 因此我们可以创build一个函数isOneMemberOf = List.member 1
,它的types是List Int -> Bool
。
这意味着->
在函数的types注释中是右关联的。 换句话说, a -> List a -> Bool
与a -> (List a -> Bool)
。
中缀和前缀符号
任何中缀操作者实际上都是窗帘背后的普通function。 只是当一个函数名只包含非字母数字符号(如$,<|,<<等)时,它被放置在2个参数之间,而不是在它们的前面(像普通函数一样)。
但是你仍然可以在2个参数前面加上一个二元运算符,把它括在括号中,所以下面的2个函数应用程序是等价的:
2 + 3 -- returns 5 (+) 2 3 -- returns 5, just like the previous one
中缀操作符只是普通的function。 他们没有什么特别的。 您可以像其他任何function一样部分应用它们:
addTwo : Int -> Int addTwo = (+) 2 addTwo 3 -- returns 5
function组成
(<<)
是一个函数组合运算符,在核心库Basics
定义。 从基础的所有function都被导入到不合格的Elm项目中,这意味着您不必编写import Basics exposing (..)
basics import Basics exposing (..)
,它已经默认完成了。
所以就像任何其他操作符一样, (<<)
只是一个函数,就像其他的一样。 它的types是什么?
(<<) : (b -> c) -> (a -> b) -> a -> c
因为->
是正确关联的,所以相当于:
(<<) : (b -> c) -> (a -> b) -> (a -> c)
换句话说, (<<)
采用b -> c
和a -> b
两个函数,并返回typesa -> c
的函数。 它将2个function组合成一个。 这是如何运作的? 为了简单起见,我们来看一个人为的例子。 假设我们有两个简单的函数:
addOne = (+) 1 multTwo = (*) 2
假设我们没有(+)
,只有addOne
,我们将如何创build一个加3而不是1的函数? 很简单,我们将3次组合addOne
:
addThree : Int -> Int addThree = addOne << addOne << addOne
如果我们想创build一个加2的函数,然后再乘以4,那该怎么办?
ourFunction : Int -> Int ourFunction = multTwo << multTwo << addOne << addOne
(<<)
从右到左组成函数。 但是上面的例子很简单,因为所有的types都是一样的。 我们怎样才能find这个列表的所有甚至是立方体的总和?
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
(>>)
是相同的function,但参数翻转,所以我们可以从左到右写相同的组成:
ourFunction2 = map cube >> filter isEven >> List.sum
概括
当你看到像h << g << f
那样的东西h << g << f
,你就知道f
, g
, h
是函数。 当这个构造h << g << f
被应用于一个值x
,你知道:
- 榆树首先将
f
应用于x
- 然后将
g
应用于上一步的结果 - 然后将
h
应用于上一步的结果
因此(negate << (*) 10 << sqrt) 25
等于-50.0
,因为你先取25的平方根得到5,那么你乘以10乘50,那么你否定50,得到-50。
为什么“而不是。
在Elm 0.13之前(见公告 )函数组合运算符为(.)
,其行为与当前(<<)
相同。 (<<)
是从F#语言的Elm 0.13中通过的(见Github issue )。 Elm 0.13还添加(>>)
等同于flip (<<)
和(<|)
作为函数应用运算符($)
replace, (|>)
等同于flip (<|)
。
中缀函数调用
您可能想知道是否可以将普通的字母数字函数名称转换为中缀二元运算符。 在Elm 0.18之前,你会使用反引号来实现函数中缀,所以下面的2将是等价的:
max 1 2 -- returns 2 1 `max` 2 -- returns 2
榆木0.18 删除了这个function 。 你不能再在Elm里面做,但是像Haskell和PureScript这样的语言仍然有它。
<<
是一个函数组合 – 返回函数。
你也可以阅读这个 。
组合创build一个计算pipe道,懒惰的链function。 这个pipe道等待input,当提供时,第一个函数开始计算,发送输出到下一个等等。
import Html add xy = Debug.log "x" x + Debug.log "y" y add9 = add 4 << add 5 main = Html.text <| toString <| add9 2
注意 : 在上面的例子中我使用了部分应用程序 。 这意味着我不提供所有的参数来运行,因此我得到了function。
如果您在Web浏览器中运行以上示例并查看控制台输出,您将看到:
x: 5 y: 2 x: 4 y: 7
如果我们把它写成math运算,它将如下所示:
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
被parsing为a = (b = c)
- 什么是运营商的关联性,为什么重要?
- https://www.quora.com/How-does-one-explain-the-right-to-left-associativity-of-the-conditional-operator-in-C
中缀操作员
在这里,我使用<<
作为infix运算符 ,但我们也可以用它作为前缀运算符,用括号括起来: (<<) (b -> c) (a -> b)
或(<|) (add 4) (add 5)
。
elm <0.18 用于让你把正常的函数作为中缀运算符来使用。
关于<|
的一句话 操作者
<|
是一个函数应用程序 – 返回值
我们基本上用它来代替圆括号。
text (something ++ something)
可以写成
text <| something ++ something
所以看着这个算子的签名 :
(<|) : (a -> b) -> a -> b
我们可以看到<|
有一个函数a -> b
作为第一个参数,值a
作为第二个参数:
(a -> b) <| a
并返回b
。
我们可以通过函数application <|
获得相同的值 :
v1 = add 4 <| add 5 <| 4 v2 = (add 4 << add 5) 4
- 这个运算符也有前向版本。
- https://elm-community.github.io/elm-faq/#what-good-is-the–operator-if-it-is-just-function-application
- 为了清晰起见,不要混合
<|
和<<
这是function组成。 这是你的具体例子
\x -> (Signal.send updateChan (toUpdate x))
在榆树中,它不是语法的一部分,而是标准库的一部分: 基础