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
?
<<
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.
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)
.
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
(<<)
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
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:
f
à x
g
au résultat de l'étape précédenteh
au résultat de l'étape précédentePar 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.
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 (<|)
.
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.
<<
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 >>
.
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
.
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)
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.
<|
<|
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
|>
.<|
Et <<
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. <<
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.
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
.
hi a =
a + 2
hello a =
a * 2
bye =
hello << hi
c =
bye 3
c
renvoie une valeur 10
.
Explication pour les développeurs javascript
:
--Elm
(a << b) x
Sera similaire
//javasript
a(b(x))
<<
Ou >>
Est appelé composition de la fonction