Operador de avance de tubería Haskell(.) Vs F#(|>)
functional-programming composition (9)
Composición de izquierda a derecha en Haskell
Algunas personas también usan el estilo de izquierda a derecha (transmisión de mensajes) en Haskell. Ver, por ejemplo, la biblioteca de mps en Hackage. Un ejemplo:
euler_1 = ( [3,6..999] ++ [5,10..999] ).unique.sum
Creo que este estilo se ve bien en algunas situaciones, pero es más difícil de leer (uno necesita conocer la biblioteca y todos sus operadores, el redefinido (.)
También es perturbador).
También hay operadores de composición de izquierda a derecha y de derecha a izquierda en Control.Category , parte del paquete base. Compare >>>
y <<<
respectivamente:
ghci> :m + Control.Category
ghci> let f = (+2) ; g = (*3) in map ($1) [f >>> g, f <<< g]
[9,5]
Hay una buena razón para preferir la composición de izquierda a derecha a veces: la orden de evaluación sigue el orden de lectura.
En F #, el uso del operador pipe-forward, |>
, es bastante común. Sin embargo, en Haskell solo he visto composición de funciones, (.)
, Ser utilizado. Entiendo que están related , pero ¿hay algún motivo de lenguaje por el que no se use pipe-forward en Haskell o es otra cosa?
Aparte del estilo y la cultura, esto se reduce a optimizar el diseño del lenguaje para el código puro o impuro.
El operador |>
es común en F # principalmente porque ayuda a ocultar dos limitaciones que aparecen con un código predominantemente impuro:
- Inferencia de tipo de izquierda a derecha sin subtipos estructurales.
- La restricción de valor.
Tenga en cuenta que la limitación anterior no existe en OCaml porque la subtipificación es estructural en lugar de nominal, por lo que el tipo estructural se refina fácilmente mediante la unificación a medida que progresa la inferencia de tipos.
Haskell toma un compromiso diferente, optando por centrarse en el código predominantemente puro, donde estas limitaciones se pueden levantar.
Creo que estamos confundiendo las cosas. Haskell''s ( .
) Es equivalente a F # ''s ( >>
). No debe confundirse con F # ''s ( |>
) que es solo una aplicación de función invertida y es como Haskell''s ( $
) - invertida:
let (>>) f g x = g (f x)
let (|>) x f = f x
Creo que los programadores de Haskell usan $
menudo. Tal vez no tan a menudo como los programadores F # tienden a usar |>
. Por otro lado, algunos chicos F # usan un grado ridículo: http://blogs.msdn.com/b/ashleyf/archive/2011/04/21/programming-is-pointless.aspx
En F # (|>)
es importante debido a la comprobación de tipo de izquierda a derecha. Por ejemplo:
List.map (fun x -> x.Value) xs
generalmente no realizará la comprobación de tipos, porque incluso si se conoce el tipo de xs
, el tipo de argumento x
para la lambda no se conoce en el momento en que lo revisa el registrador de tipos, por lo que no sabe cómo resolver x.Value
.
A diferencia de
xs |> List.map (fun x -> x.Value)
funcionará bien, porque el tipo de xs
llevará al tipo de x
se conoce.
La comprobación de tipo de izquierda a derecha es necesaria debido a la resolución del nombre involucrada en construcciones como x.Value
. Simon Peyton Jones ha escrito una proposal para agregar un tipo similar de resolución de nombre a Haskell, pero sugiere usar restricciones locales para rastrear si un tipo admite o no una operación en particular. Por lo tanto, en la primera muestra, el requisito de que x
necesite una propiedad Value
mantendrá hasta que se vea xs
y este requisito se pueda resolver. Sin embargo, esto complica el sistema de tipos.
Este es mi primer día para probar Haskell (después de Rust y F #), y pude definir el operador de F # |>:
(|>) :: a -> (a -> b) -> b
(|>) x f = f x
infixl 0 |>
Y parece funcionar:
factorial x =
case x of
1 -> 1
_ -> x * factorial (x-1)
main =
5 |> factorial |> print
Apuesto a que un experto en Haskell puede darte una solución aún mejor.
Estoy siendo un poco especulativo ...
Cultura : pienso |>
es un operador importante en la "cultura" F #, y quizás de manera similar con .
para Haskell. F # tiene un operador de composición de funciones <<
pero creo que la comunidad F # tiende a usar estilos sin puntos menos que la comunidad Haskell.
Diferencias de idioma : no sé lo suficiente sobre ambos idiomas para comparar, pero tal vez las reglas para generalizar las vinculaciones de let son lo suficientemente diferentes como para afectar esto. Por ejemplo, sé que en F # algunas veces escribiendo
let f = exp
no se compilará, y usted necesita una conversión eta explícita:
let f x = (exp) x // or x |> exp
para hacerlo compilar. Esto también aleja a las personas del estilo libre de puntos / composición y hacia el estilo de canalización. Además, la inferencia de tipo F # a veces exige la canalización, por lo que aparece un tipo conocido a la izquierda (ver here ).
(Personalmente, encuentro que el estilo sin puntos es ilegible, pero supongo que todo lo nuevo / diferente parece ilegible hasta que te acostumbras).
Creo que ambos son potencialmente viables en cualquier idioma, y la historia / cultura / accidente puede definir por qué cada comunidad se asentó en un "atractor" diferente.
He visto >>>
se usa para flip (.)
, Y a menudo lo uso yo mismo, especialmente para cadenas largas que se entienden mejor de izquierda a derecha.
>>>
es en realidad de Control.Arrow, y funciona en más que solo funciones.
Más especulaciones, esta vez del lado predominantemente Haskell ...
($)
es el lanzamiento de (|>)
, y su uso es bastante común cuando no puede escribir código sin puntos. Entonces la razón principal que (|>)
no se usa en Haskell es que su lugar ya está ocupado por ($)
.
Además, hablando de un poco de experiencia F #, creo que (|>)
es tan popular en el código F # porque se asemeja a la Subject.Verb(Object)
de OO. Como F # apunta a una integración funcional / OO fluida, Subject |> Verb Object
es una transición bastante suave para los nuevos programadores funcionales.
Personalmente, me gusta pensar de izquierda a derecha también, así que uso (|>)
en Haskell, pero no creo que muchas otras personas lo hagan.
Si desea utilizar F # ''s |>
en Haskell, entonces en Data.Function es el operador &
(desde la base 4.8.0.0
).