什么是`<

问题描述 投票:52回答:5

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

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

Elm syntax reference找不到它。

这是否意味着,当该领域发生变化时,不是将其content发送到updateChan,而是将toUpdate发送给updateChan

elm
5个回答
111
投票

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

Elm's type system

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

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

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

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

您还可以通过将类型变量(如String)替换为Maybe Inta这样的具体变量来使您的函数具有多态性。许多Elm核心函数都是类型多态的,例如List.isEmpty的类型为List a -> Bool。它需要某种类型的List并返回Bool类型的值。

如果再次看到相同的类型变量,则此类型变量的实例必须属于同一类型。例如,List.reverseList 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采用a类型的1个参数。它返回类型为List a -> Bool的函数“。因此,我们可以创建一个函数isOneMemberOf = List.member 1,其类型为List Int -> Bool

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

Infix and prefix notation

任何infix operator实际上是窗帘背后的普通功能。只是当函数名称仅包含非字母数字符号(例如$,

但是你仍然可以将+这样的二元运算符放在2个参数的前面,将它括在括号中,所以下面的2个函数应用程序是等价的:

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

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

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

addTwo 3 -- returns 5

Function composition

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

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

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

因为->是右关联的,所以这相当于:

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

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

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

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

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

Recap

当你看到像h << g << f这样的东西时,你知道qazxsw poi,qazxsw poi,qazxsw poi是函数。当这个构造f应用于值g时,你知道:

  • 榆树首先将h应用于h << g << f
  • 然后将x应用于上一步的结果
  • 然后将f应用于上一步的结果

因此x等于g,因为你首先取25的平方根得到5,然后你将5乘以10得到50,然后你否定50得到-50。

Why << and not .

在Elm 0.13(参见h)之前,函数组合运算符是(negate << (*) 10 << sqrt) 25,其行为与当前的-50.0相同。 announcement被F#语言中的Elm 0.13采用(参见(.))。榆木0.13还添加(<<)等同于(<<)Github issue作为功能应用操作员(>>)的替代品,flip (<<)等同于(<|)

Infix function call

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

($)

榆树0.18 (|>)。你不能再在Elm中做到这一点,但是像flip (<|)max 1 2 -- returns 2 1 `max` 2 -- returns 2 这样的语言仍然有它。


23
投票

removed this feature是一个函数组合 - 返回函数。

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

Haskell

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

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

<<

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

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

Reading signatures

看看这个运营商的签名:

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

Associativity

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

(<<) : (b -> c) -> (a -> b) -> (a -> c)被解析为<<

  • a -> c
  • a = b = c

Infix operator

这里我使用a = (b = c)作为What is associativity of operators and why is it important?,但我们也可以将它用作括号括起来的前缀运算符:https://www.quora.com/How-does-one-explain-the-right-to-left-associativity-of-the-conditional-operator-in-C<<

elm <0.18 infix operator让你使用普通函数并将它们用作中缀运算符。

A word about (<<) (b -> c) (a -> b) operator

(<|) (add 4) (add 5)是一个函数应用程序 - 返回值

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

used to

可写成

<|

所以看看这个运算符的<|


text (something ++ something)

我们可以看到,对于 text <| something ++ something 运算符,有一个函数signature作为第一个参数,值(<|) : (a -> b) -> a -> b 作为第二个参数:

<|

它返回a -> b

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

(a -> b) <| a

11
投票

它是功能组合。对于你的具体例子,它意味着

don't mix

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


3
投票

我的第二次尝试:D

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

Basics.<<<<之间的区别在于<|用于组成函数,<<用于省略括号。

为什么它会这样

让我们看一下找到的类型注释<|

<<

这个定义告诉我们,当你传递两个函数来运行<|时,你将获得函数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 - 当你将一个参数传递给函数期望两个参数时,你会得到一个期望一个参数的函数。

0
投票

10开发人员的说明:

infix operators

会是类似的

partial application

javascript--elm (a << b) x 被称为//javasript a(b(x))

© www.soinside.com 2019 - 2024. All rights reserved.