Подтвердить что ты не робот

Что означает оператор `<<` в вязе?

В следующем коде, взятом из Пример формы Elm, строка 122, что означает оператор <<?

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

Не удалось найти его в ссылка синтаксиса Elm.

Значит ли это, когда поле изменяется вместо отправки content в updateChan, отправьте toUpdate в updateChan?

4b9b3361

Ответ 1

<< является оператором композиции функций, определенным в базовой библиотеке 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 имеет тип List a -> Bool. Он принимает List некоторого типа и возвращает значение типа Bool.

Если вы снова видите одну и ту же переменную типа, то экземпляры этой переменной типа должны быть одного типа. Например List.reverse имеет тип List a -> List a. Поэтому, если вы примените List.reverse к списку целых чисел (например, к типу List Int), он вернет список целых чисел назад. Ни в коем случае такая функция не может принимать список целых чисел, но возвращает список строк. Это гарантируется компилятором.

По умолчанию все функции в Elm curries. Это означает, что если у вас есть функция из 2 аргументов, она преобразуется в функцию из 1 аргумента, которая возвращает функцию из 1 аргумента. Вот почему вы используете синтаксис приложения Elm настолько отличается от функционального приложения на других языках, таких как Java, С++, С#, Python и т.д. Нет причин писать someFunction(arg1, arg2), когда вы можете написать someFunction arg1 arg2. Зачем? Потому что на самом деле someFunction arg1 arg2 на самом деле ((someFunction arg1) arg2).

Currying делает частичное приложение возможным. Предположим, вы хотите частично применить List.member. List.member имеет тип a -> List a -> Bool. Мы можем прочитать тип, поскольку "List.member принимает 2 аргумента типа a и тип List a". Но мы также можем прочитать тип, поскольку "List.member принимает 1 аргумент типа a. Он возвращает функцию типа List a -> Bool". Поэтому мы можем создать функцию isOneMemberOf = List.member 1, которая будет иметь тип List Int -> Bool.

Это означает, что -> в аннотации типов функций является право-ассоциативным. Другими словами, a -> List a -> Bool совпадает с a -> (List a -> Bool).

Инфиксная и префиксная нотация

Любой оператор infix фактически является обычной функцией за шторами. Это просто, когда имя функции состоит исключительно из не буквенно-цифровых символов (таких как $, < |, < <, и т.д.), Оно помещается между двумя аргументами, а не перед ними (например, обычные функции).

Но вы все равно можете поместить двоичный оператор, например +, перед двумя аргументами, заключая его в круглые скобки, поэтому приведенные ниже 2 приложения функций эквивалентны:

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

Операторы Infix - это просто обычные функции. В них нет ничего особенного. Вы можете частично применить их так же, как и любую другую функцию:

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)

Другими словами, (<<) выполняет 2 функции типов b -> c и a -> b соответственно и возвращает функцию типа a -> c. Он объединяет 2 функции в одну. Как это работает? Давайте посмотрим на надуманный пример для простоты. Предположим, что у нас есть две простые функции:

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

Предположим, что у нас нет (+), только 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 применяется к значению x, вы знаете:

  • Сначала Elm применяет f к x
  • затем применяет g к результату предыдущего шага
  • затем применяет h к результату предыдущего шага

Поэтому (negate << (*) 10 << sqrt) 25 равно -50.0, потому что сначала вы берете квадратный корень из 25 и получаете 5, затем умножаете 5 на 10 и получаете 50, затем вы отрицаете 50 и получаете -50.

Почему < < и не.

До Elm 0.13 (см. объявление) оператор композиции функции был (.), и его поведение было идентично текущему (<<). (<<) был принят в Elm 0.13 с языка F # (см. вопрос Github). Elm 0.13 также добавил (>>) как эквивалент flip (<<) и (<|) в качестве замены для оператора приложения функций ($) и (|>) как эквивалент flip (<|).

вызов функции Infix

Вам может показаться, что вы можете превратить обычное алфавитно-цифровое имя функции в бинарный оператор infix. До Elm 0.18 вы использовали бы обратные элементы для создания функции infix, поэтому ниже 2 будет эквивалентно:

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

Elm 0.18 удалила эту функцию. Вы больше не можете делать это в Elm, но такие языки, как Haskell и PureScript все еще есть.

Ответ 2

<< - это композиция функции - возвращает функцию.

Композиция создает канал вычислений, цепочку функций. Этот канал ожидает ввода, и при наличии первая функция начинает вычисление, отправляет вывод следующей и т.д.

import Html

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

add9 =
    add 4 << add 5

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

Примечание: в приведенном выше примере я использую частичное применение. Это означает, что я не предоставляю все параметры для работы, и в результате я получаю функцию.

Если вы запустите приведенный выше пример в веб-браузере и посмотрите на вывод консоли, вы увидите:

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)

Инфиксный оператор

Здесь я использую << как инфиксный оператор, но мы также можем использовать его как префиксный оператор, заключив его в скобки: (<<) (b → c) (a → b) или (<|) (add 4) (add 5).

Вяз <0,18 используется для позволяешь принимать нормальные функции и использовать их в качестве операторов инфиксных.

Слово о <| оператор

<| является приложением функции - возвращает значение

Мы в основном используем его вместо скобок.

text (something++ something)

можно записать как

text <| something++ something

Итак, посмотрев на подпись этого оператора:

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

мы можем видеть, что для <| оператор, есть функция a → b в качестве первого параметра и значение a в качестве второго:

(a → b) <| a

и он возвращает b.

Мы можем получить то же значение с помощью функции приложения <| :

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

Ответ 3

Это композиция функции. Для вашего конкретного примера это означает

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

В elm это не часть синтаксиса, а часть стандартной библиотеки: Основы. <

Ответ 4

Моя вторая попытка: D

<< против <|

Разница между << и <| является то, что << используется для создания функций и <| используется, чтобы опустить скобки.

Почему это работает так

Давайте посмотрим на аннотацию типа, найденную здесь:

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

Это определение говорит нам, что когда вы передадите две функции в функцию <<, вы получите функцию a → c.

Пример с демо

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

c возвращает значение 10.

Узнайте больше о:

Ответ 5

Объяснение для разработчиков javascript:

--elm

(a << b) x

Будет похоже

//javasript

a(b(x))

<< или >> называется функцией композиции