elm - que - operadores relacionales ejemplos
¿Qué significa el operador `<<` en olmo? (5)
Es función composición. Para su ejemplo concreto significa
/x -> (Signal.send updateChan (toUpdate x))
En elm no es parte de la sintaxis sino parte de la biblioteca estándar: Basics.<<
En el siguiente código tomado de
Elm Form Example
, línea 122, ¿qué significa el operador
<<
?
Field.field Field.defaultStyle (Signal.send updateChan << toUpdate) "" content
No se pudo encontrar en la referencia de sintaxis de Elm .
¿Significa que, cuando el campo cambia, en lugar de enviar su
content
a
updateChan
, enviar a
toUpdate
a
updateChan
?
Explantion para desarrolladores de
javascript
:
--elm
(a << b) x
Será similar
//javasript
a(b(x))
<<
o
>>
se llama
composición de funciones
Mi segundo intento: D
<<
vs
<|
La diferencia entre
<<
y
<|
es que
<<
se usa para componer funciones y
<|
se usa para omitir paréntesis.
Por qué funciona así
Veamos la anotación de tipo que se encuentra here :
<< : (b -> c) -> (a -> b) -> a -> c
Esta definición nos dice que cuando pasa dos funciones a la función
<<
, obtendrá la función
a -> c
.
Ejemplo con demo
hi a =
a + 2
hello a =
a * 2
bye =
hello << hi
c =
bye 3
c
devuelve el valor
10
.
Leer más sobre:
- operadores infijos - primer argumento a la izquierda de la función,
- aplicación parcial : cuando pasa un argumento a la función esperando dos argumentos, obtiene la función esperando un argumento.
<<
es un operador de composición de funciones, definido en los conceptos
Basics
biblioteca principal.
Todas las funciones de Basics se importan a proyectos de Elm sin calificar.
Sistema de tipos de Elm
Recordemos los conceptos básicos del sistema de tipo Elm.
Elm está estáticamente escrito . Esto significa que en Elm cada variable o función tiene un tipo, y este tipo nunca cambia. Ejemplos de tipos en Elm son:
-
Int
-
String
-
Maybe Bool
-
{ name : String, age : Int }
-
Int -> Int
-
Int -> String -> Maybe Char
.
La escritura estática significa que el compilador garantiza que los tipos de todas las funciones y variables son correctas durante la compilación, por lo que no tiene errores de tipo de tiempo de ejecución.
En otras palabras, nunca tendrá una función de tipo
String -> String
recibe o devuelve
Int
, código que permite que esto ni siquiera se compile.
También puede hacer que sus funciones sean polimórficas reemplazando un tipo concreto como
String
o
Maybe Int
con una variable de tipo, que es una cadena minúscula arbitraria, como
a
.
Muchas funciones principales de Elm son de tipo polimórfico, por ejemplo
List.isEmpty
tiene el tipo
List a -> Bool
.
Toma una
List
de algún tipo y devuelve un valor de tipo
Bool
.
Si vuelve a ver la misma variable de tipo, las instancias de esta variable de tipo deben ser del mismo tipo.
Por ejemplo,
List.reverse
tiene el tipo
List a -> List a
.
Entonces, si aplica
List.reverse
a una lista de enteros (es decir, a algo que tiene el tipo
List Int
), devolverá una lista de enteros.
De ninguna manera dicha función puede tomar una lista de enteros, sino devolver una lista de cadenas.
Esto está garantizado por el compilador.
Todas las funciones en Elm se
curried
por defecto.
Esto significa que si tiene una función de 2 argumentos, se transforma en una función de 1 argumento que devuelve una función de 1 argumento.
Es por eso que la sintaxis de aplicación de función de Elm es tan diferente de la aplicación de función en otros lenguajes como Java, C ++, C #, Python, etc. No hay razón para escribir
someFunction(arg1, arg2)
, cuando puede escribir
someFunction arg1 arg2
.
¿Por qué?
Porque en realidad
someFunction arg1 arg2
es en realidad
((someFunction arg1) arg2)
.
El curry hace posible la
aplicación parcial
.
Suponga que desea aplicar parcialmente
List.member
.
List.member
tiene un tipo
a -> List a -> Bool
.
Podemos leer el tipo como "
List.member
toma 2 argumentos, de tipo
a
y tipo
List a
".
Pero también podemos leer el tipo como "
List.member
toma 1 argumento de tipo
a
.
Devuelve una función de tipo
List a -> Bool
”.
Por lo tanto, podemos crear una función
isOneMemberOf = List.member 1
, que tendrá el tipo
List Int -> Bool
.
Esto significa que
->
en el tipo de anotaciones de funciones es derecho-asociativo.
En otras palabras,
a -> List a -> Bool
es lo mismo que
a -> (List a -> Bool)
.
Notación de infijo y prefijo
Cualquier operador infijo es en realidad una función ordinaria detrás de las cortinas. Es justo cuando el nombre de una función consiste únicamente en símbolos no alfanuméricos (como $, <|, <<, etc.), se coloca entre 2 argumentos, no delante de ellos (como las funciones ordinarias).
Pero aún puede poner un operador binario como
+
delante de los 2 argumentos, encerrándolo entre paréntesis, por lo que las 2 aplicaciones de funciones a continuación son equivalentes:
2 + 3 -- returns 5
(+) 2 3 -- returns 5, just like the previous one
Los operadores de infijo son solo funciones ordinarias. No hay nada especial en ellos. Puede aplicarlos parcialmente como cualquier otra función:
addTwo : Int -> Int
addTwo = (+) 2
addTwo 3 -- returns 5
Composición de la función
<<
es un operador de composición de funciones, definido en los conceptos
Basics
biblioteca principal.
Todas las funciones de los elementos básicos se importan en proyectos de Elm sin calificar, lo que significa que no tiene que escribir
import Basics exposing (..)
, ya está hecho de forma predeterminada.
Entonces, como cualquier otro operador,
(<<)
es solo una función, como cualquier otra.
Cual es su tipo
(<<) : (b -> c) -> (a -> b) -> a -> c
Porque
->
es asociativo a la derecha, esto es equivalente a:
(<<) : (b -> c) -> (a -> b) -> (a -> c)
En otras palabras,
(<<)
toma 2 funciones de los tipos
b -> c
y
a -> b
respectivamente, y devuelve una función del tipo
a -> c
.
Compone 2 funciones en una.
¿Cómo funciona?
Veamos un ejemplo artificial por simplicidad.
Supongamos que tenemos 2 funciones simples:
addOne = (+) 1
multTwo = (*) 2
Supongamos que no tenemos
(+)
, solo
addOne
, ¿cómo crearíamos una función que agregue 3, no 1?
Muy simple, compondríamos
addOne
juntos 3 veces:
addThree : Int -> Int
addThree = addOne << addOne << addOne
¿Qué sucede si queremos crear una función que agregue 2 y luego se multiplique por 4?
ourFunction : Int -> Int
ourFunction = multTwo << multTwo << addOne << addOne
(<<)
compone funciones de derecha a izquierda.
Pero el ejemplo anterior es simple, porque todos los tipos son iguales.
¿Cómo encontraríamos una suma de todos los cubos pares de la lista?
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
(>>)
es la misma función, pero con los argumentos invertidos, por lo que podemos escribir la misma composición de izquierda a derecha:
ourFunction2 = map cube >> filter isEven >> List.sum
Resumen
Cuando ves algo como
h << g << f
, entonces sabes que
f
,
g
,
h
son funciones.
Cuando esta construcción
h << g << f
se aplica a un valor
x
, entonces sabes:
-
Elm primero aplica
f
ax
-
luego aplica
g
al resultado del paso anterior -
luego aplica
h
al resultado del paso anterior
Por lo tanto
(negate << (*) 10 << sqrt) 25
es igual a
-50.0
, porque primero toma una raíz cuadrada de 25 y obtiene 5, luego multiplica 5 por 10 y obtiene 50, luego niega 50 y obtiene -50.
¿Por qué << y no?
Antes de Elm 0.13 (ver
announcement
), el operador de composición de funciones era
(.)
, Y su comportamiento era idéntico al actual
(<<)
.
(<<)
se adoptó en Elm 0.13 del lenguaje F # (vea el
tema de Github
).
Elm 0.13 también agregó
(>>)
como equivalente a
flip (<<)
y
(<|)
como reemplazo del operador de aplicación de función
($)
, y
(|>)
como equivalente a
flip (<|)
.
Llamada de función infija
Tal vez se pregunte si puede convertir un nombre de función alfanumérico ordinario en un operador binario infijo. Antes de Elm 0.18, usaría los backticks para hacer una función infija, por lo que debajo de 2 sería equivalente:
max 1 2 -- returns 2
1 `max` 2 -- returns 2
Elm 0.18 eliminó esta característica . Ya no puedes hacerlo en Elm, pero los lenguajes como Haskell y PureScript todavía lo tienen.
<<
es una composición de función - devuelve función.
La composición crea una tubería de cálculos, una cadena de funciones. Esta tubería espera la entrada, y cuando se proporciona, la primera función comienza el cálculo, envía la salida a la siguiente, 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
Nota : En el ejemplo anterior, uso una aplicación parcial . Significa que no proporciono todos los parámetros para funcionar y, como resultado, obtengo la función.
Si ejecuta el ejemplo anterior en el navegador web y mira la salida de la consola, verá:
x: 5
y: 2
x: 4
y: 7
Si lo escribimos como operaciones matemáticas, se verá así:
4 + (5 + 2)
4 + 7
Nota
:
También podemos usar la versión directa
>>
.
Lectura de firmas
Mirando la firma de este operador:
(<<) : (b -> c) -> (a -> b) -> a -> c
Para el operador
<<
, hay una función
b -> c
como primer parámetro, y una función
a -> b
como segundo:
(b -> c) << (a -> b)
Pero también hay un tercer parámetro
a
.
Porque
->
es derecho-asociativo, entonces
(<<) : (b -> c) -> (a -> b) -> a -> c
es equivalente a:
(<<) : (b -> c) -> (a -> b) -> (a -> c)
.
De modo que
<<
devuelve la función
a -> c
.
Asociatividad
En lenguajes de programación, la asociatividad (o fijeza) de un operador es una propiedad que determina cómo se agrupan los operadores de la misma precedencia en ausencia de paréntesis; es decir, en qué orden se evalúa cada operador:
a = b = c
se analiza como
a = (b = c)
- ¿Qué es la asociatividad de los operadores y por qué es importante?
- https://www.quora.com/How-does-one-explain-the-right-to-left-associativity-of-the-conditional-operator-in-C
Operador infijo
Aquí uso
<<
como
operador de infijo
, pero también podemos usarlo como operador de prefijo encerrándolo entre paréntesis:
(<<) (b -> c) (a -> b)
o
(<|) (add 4) (add 5)
.
elm <0.18 solía permitirte tomar funciones normales y usarlas como operadores infijos.
Una palabra sobre
<|
operador
<|
es una aplicación de función - devuelve valor
Básicamente lo usamos en lugar de paréntesis.
text (something ++ something)
Se puede escribir como
text <| something ++ something
Mirando la firma de este operador:
(<|) : (a -> b) -> a -> b
podemos ver eso para
<|
operador, hay una función
a -> b
como primer parámetro, y el valor
a
como segundo:
(a -> b) <| a
y vuelve
b
.
Podemos obtener el mismo valor con la aplicación de función
<|
:
v1 = add 4 <| add 5 <| 4
v2 = (add 4 << add 5) 4
-
También hay una versión avanzada de este operador
|>
. - https://elm-community.github.io/elm-faq/#what-good-is-the--operator-if-it-is-just-function-application
-
Para mayor claridad
, no mezcle
<|
y<<