web-dev-qa-db-fra.com

Que signifie l'opérateur `<<` dans l'orme?

Dans le code suivant tiré de Elm Form Example , ligne 122, qu'est-ce que le << opérateur signifie?

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

Impossible de le trouver dans référence de syntaxe Elm .

Cela signifie-t-il que lorsque le champ change, au lieu d'envoyer son content à updateChan, envoyer toUpdate à updateChan?

54
Not an ID

<< est un opérateur de composition de fonction, défini dans la bibliothèque principale Basics . Toutes les fonctions de Basics sont importées dans les projets Elm sans réserve.

Système de type Elm

Rappelons les bases du système de type Elm.

L'orme est typé statiquement . Cela signifie que dans Elm chaque variable ou fonction a un type, et ce type ne change jamais. Des exemples de types dans Elm sont:

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

Le typage statique signifie que le compilateur garantit que les types de toutes les fonctions et variables sont corrects pendant la compilation, vous n'avez donc pas d'erreurs de type à l'exécution. En d'autres termes, vous n'aurez jamais de fonction de type String -> String Recevant ou renvoyant Int, le code qui permet que cela ne compile même pas.

Vous pouvez également rendre vos fonctions polymorphes en remplaçant un type concret tel que String ou Maybe Int Par une variable de type, qui est une chaîne arbitraire en minuscules, telle que a. De nombreuses fonctions de base Elm sont de type polymorphe, par exemple List.isEmpty a le type List a -> Bool. Il prend un List d'un certain type et renvoie une valeur de type Bool.

Si vous voyez à nouveau la même variable de type, les instances de cette variable de type doivent être du même type. Par exemple List.reverse a le type List a -> List a. Donc, si vous appliquez List.reverse À une liste d'entiers (c'est-à-dire à quelque chose qui a le type List Int), Il sera renvoyer une liste d'entiers. Aucune telle fonction ne peut prendre une liste d'entiers, mais retourner une liste de chaînes. Ceci est garanti par le compilateur.

Toutes les fonctions dans Elm sont curry par défaut. Cela signifie que si vous avez une fonction de 2 arguments, elle est transformée en fonction de 1 argument qui renvoie une fonction de 1 argument. C'est pourquoi vous utilisez la syntaxe d'application d'Elm si différente de l'application de fonction dans d'autres langages tels que Java, C++, C #, Python, etc. Il n'y a aucune raison d'écrire someFunction(arg1, arg2), quand vous pouvez écrire someFunction arg1 arg2. Pourquoi? Parce qu'en réalité, someFunction arg1 arg2 Est en réalité ((someFunction arg1) arg2).

Le curry rend application partielle possible. Supposons que vous souhaitiez appliquer partiellement List.member . List.member A un type a -> List a -> Bool. Nous pouvons lire le type comme "List.member Prend 2 arguments, de type a et de type List a". Mais nous pouvons également lire le type comme "List.member Prend 1 argument de type a. Il renvoie une fonction de type List a -> Bool ". Par conséquent, nous pouvons créer une fonction isOneMemberOf = List.member 1, Qui aura le type List Int -> Bool.

Cela signifie que -> Dans les annotations de type des fonctions est associative à droite. En d'autres termes, a -> List a -> Bool Est identique à a -> (List a -> Bool).

Infixe et notation de préfixe

N'importe quel opérateur infixe est en fait une fonction ordinaire derrière les rideaux. C'est juste lorsqu'un nom de fonction se compose uniquement de symboles non alphanumériques (tels que $, <|, <<, etc.), il est placé entre 2 arguments, pas devant eux (comme les fonctions ordinaires).

Mais vous pouvez toujours mettre un opérateur binaire comme + Devant les 2 arguments, en le mettant entre parenthèses, donc les 2 applications de fonction ci-dessous sont équivalentes:

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

Les opérateurs Infix ne sont que des fonctions ordinaires. Ils n'ont rien de spécial. Vous pouvez les appliquer partiellement comme n'importe quelle autre fonction:

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

addTwo 3 -- returns 5

Composition des fonctions

(<<) est un opérateur de composition de fonction, défini dans la bibliothèque principale Basics . Toutes les fonctions des bases sont importées dans les projets Elm sans réserve, ce qui signifie que vous n'avez pas à écrire import Basics exposing (..), c'est déjà fait par défaut.

Donc, comme tout autre opérateur, (<<) Est juste une fonction, comme n'importe quel autre. Quel est son type?

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

Parce que -> Est associatif à droite, cela équivaut à:

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

En d'autres termes, (<<) Prend respectivement 2 fonctions de types b -> c Et a -> b Et renvoie une fonction de type a -> c. Il compose 2 fonctions en une seule. Comment ça marche? Regardons un exemple artificiel pour des raisons de simplicité. Supposons que nous ayons 2 fonctions simples:

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

Supposons que nous n'ayons pas (+), Seulement addOne, comment pourrions-nous créer une fonction qui ajoute 3, pas 1? Très simple, on composerait addOne ensemble 3 fois:

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

Et si nous voulons créer une fonction qui ajoute 2, puis multiplie par 4?

ourFunction : Int -> Int
ourFunction = multTwo << multTwo << addOne << addOne

(<<) Compose les fonctions de droite à gauche. Mais l'exemple ci-dessus est simple, car tous les types sont les mêmes. Comment trouverions-nous une somme de tous les cubes pairs de la liste?

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

(>>) Est la même fonction, mais avec des arguments inversés, nous pouvons donc écrire la même composition de gauche à droite à la place:

ourFunction2 = map cube >> filter isEven >> List.sum

Résumer

Lorsque vous voyez quelque chose comme h << g << f, Alors vous savez que f, g, h sont des fonctions. Lorsque cette construction h << g << f Est appliquée à une valeur x, alors vous savez:

  • Elm applique d'abord f à x
  • applique ensuite g au résultat de l'étape précédente
  • applique ensuite h au résultat de l'étape précédente

Par conséquent, (negate << (*) 10 << sqrt) 25 Est égal à -50.0, Car vous prenez d'abord une racine carrée de 25 et obtenez 5, puis vous multipliez 5 par 10 et obtenez 50, puis vous annulez 50 et obtenez -50.

Pourquoi << et non.

Avant Elm 0.13 (voir annonce ) l'opérateur de composition de fonction était (.), Et son comportement était identique à (<<) Actuel. (<<) A été adopté en orme 0.13 à partir du langage F # (voir problème Github ). L'orme 0,13 a également ajouté (>>) Comme équivalent à flip (<<), et (<|) comme remplacement pour l'opérateur d'application de fonction ($), Et - (|>) équivalent à flip (<|).

Appel de fonction Infix

Vous vous demandez peut-être si vous pouvez transformer un nom de fonction alphanumérique ordinaire en un opérateur binaire infixé. Avant Elm 0.18, vous utilisiez des backticks pour créer un infixe de fonction, donc en dessous de 2 serait équivalent:

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

Orme 0,18 a supprimé cette fonctionnalité . Vous ne pouvez plus le faire dans Elm, mais des langages comme Haskell et PureScript l'ont toujours.

112
Mirzhan Irkegulov

<< est une composition de fonction - renvoie une fonction.

La composition crée un tuyau de calculs, une chaîne de fonctions. Ce canal attend l'entrée, et lorsqu'il est fourni, la première fonction démarre le calcul, envoie la sortie à la suivante, etc.

import Html

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

add9 =
    add 4 << add 5

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

Remarque : Dans l'exemple ci-dessus, j'utilise application partielle . Cela signifie que je ne fournis pas tous les paramètres pour fonctionner et par conséquent j'obtiens la fonction.

Si vous exécutez l'exemple ci-dessus dans un navigateur Web et regardez la sortie de la console, vous verrez:

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

Si nous l'écrivons sous forme d'opérations mathématiques, cela ressemblera à ceci:

4 + (5 + 2)
4 + 7

Remarque : Nous pouvons également utiliser la version avancée >>.

Lecture des signatures

En regardant la signature de cet opérateur:

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

Pour l'opérateur <<, Il y a une fonction b -> c Comme premier paramètre, et une fonction a -> b Comme deuxième:

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

Mais il y a aussi un troisième paramètre a. Parce que -> Est associatif à droite, alors

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

est équivalent à:

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

Pour que << Renvoie la fonction a -> c.

Associativité

Dans les langages de programmation, l'associativité (ou fixité) d'un opérateur est une propriété qui détermine comment les opérateurs de même priorité sont regroupés en l'absence de parenthèses; c'est-à-dire dans quel ordre chaque opérateur est évalué:

a = b = c Est analysé comme a = (b = c)

Opérateur Infix

Ici, j'utilise << Comme opérateur infixe , mais nous pouvons également l'utiliser comme opérateur préfixe en le mettant entre parenthèses: (<<) (b -> c) (a -> b) Ou (<|) (add 4) (add 5).

Elm <0,18 tilisé pour vous permet de prendre des fonctions normales et de les utiliser comme opérateurs infixes.

Un mot sur l'opérateur <|

<| est une application de fonction - retourne une valeur

Nous l'utilisons essentiellement au lieu de parenthèses.

text (something ++ something)

peut être écrit comme

text <| something ++ something

En regardant donc signature de cet opérateur:

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

nous pouvons voir que pour l'opérateur <|, il y a une fonction a -> b comme premier paramètre, et une valeur a comme deuxième:

(a -> b) <| a

et il renvoie b.

Nous pouvons obtenir la même valeur avec l'application de fonction <|:

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

C'est la composition des fonctions. Pour votre exemple concret, cela signifie

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

Dans Elm, ce n'est pas une partie de la syntaxe mais une partie de la bibliothèque standard: Basics. <<

11
CheatEx

Ma deuxième tentative: D

<< contre <|

La différence entre << et <| est-ce << est utilisé pour composer des fonctions et <| est utilisé pour omettre les parenthèses.

Pourquoi ça marche comme ça

Regardons l'annotation de type trouvée ici :

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

Cette définition nous dit que lorsque vous passez deux fonctions à la fonction <<, vous obtiendrez la fonction a -> c.

Exemple avec démo

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

c renvoie une valeur 10.

En savoir plus sur:

  • opérateurs infixes - premier argument à gauche de la fonction,
  • application partielle - lorsque vous passez un argument à une fonction qui attend deux arguments, vous obtenez une fonction qui attend un argument.
3
rofrol

Explication pour les développeurs javascript:

--Elm

(a << b) x

Sera similaire

//javasript

a(b(x))

<< Ou >> Est appelé composition de la fonction

0
hien