¿Cómo se usa Control.Applicative para escribir Haskell más limpio?
coding-style (3)
En una respuesta reciente a una pregunta de estilo , escribí
main = untilM (isCorrect 42) (read `liftM` getLine)
y
isCorrect num guess =
case compare num guess of
EQ -> putStrLn "You Win!" >> return True
...
Martijn sugirió amablemente alternativas:
main = untilM (isCorrect 42) (read <$> getLine)
EQ -> True <$ putStrLn "You Win!"
¿Qué patrones comunes en el código Haskell se pueden aclarar usando abstracciones de Control.Applicative ¿ Control.Applicative ? ¿Cuáles son las reglas útiles para tener en cuenta para usar Control.Applicative effective?
Básicamente, las mónadas también son funcionadores aplicativos [1]. Entonces, cada vez que te encuentres usando liftM
, liftM2
, etc., puedes encadenar los cálculos usando <*>
. En cierto sentido, puede pensar en funtores aplicativos como análogos a las funciones. Una función pura f
puede levantarse haciendo f <$> x <*> y <*> z
.
En comparación con las mónadas, los funtores aplicativos no pueden ejecutar sus argumentos de forma selectiva. Los efectos secundarios de todos los argumentos se llevarán a cabo.
import Control.Applicative
ifte condition trueClause falseClause = do
c <- condition
if c then trueClause else falseClause
x = ifte (return True) (putStrLn "True") (putStrLn "False")
ifte'' condition trueClause falseClause =
if condition then trueClause else falseClause
y = ifte'' <$> (pure True) <*> (putStrLn "True") <*> (putStrLn "False")
x
solo muestra True
, mientras que y
produce True
y False
secuencialmente.
[1] La Typeclassopedia . Muy recomendable.
[2] http://www.soi.city.ac.uk/~ross/papers/Applicative.html . Aunque este es un trabajo académico, no es difícil de seguir.
[3] http://learnyouahaskell.com/functors-applicative-functors-and-monoids#applicative-functors . Explica el trato muy bien.
[4] http://book.realworldhaskell.org/read/using-parsec.html#id652399 . Muestra cómo la biblioteca monárquica de Parsec
también puede usarse de forma aplicativa.
Hay mucho que decir en respuesta a su pregunta, sin embargo, dado que usted lo hizo, ofreceré esta "regla de oro".
Si está utilizando do
-notation y sus valores generados [1] no se utilizan en las expresiones que está secuenciando [2], entonces ese código puede transformarse en un estilo Applicative. De manera similar, si usa uno o más de los valores generados en una expresión que está secuenciada, entonces debe usar Monad
y Applicative
no es lo suficientemente fuerte para lograr el mismo código.
Por ejemplo, veamos el siguiente código:
do a <- e1
b <- e2
c <- e3
return (f a b c)
Vemos que en ninguna de las expresiones a la derecha de <-
aparece alguno de los valores generados ( a
, b
, c
). Por lo tanto, podemos transformarlo utilizando el código Aplicativo. Aquí hay una posible transformación:
f <$> e1 <*> e2 <*> e3
y otro:
liftA3 f e1 e2 e3
Por otro lado, tome este fragmento de código por ejemplo:
do a <- e1
b <- e2 a
c <- e3
return (f b c)
Este código no puede usar Applicative
[3] porque el valor generado a
se usa más adelante en una expresión en la comprensión. Esto debe usar Monad
para llegar a su resultado: intente factorizarlo en Applicative
para tener una idea de por qué.
Hay algunos detalles más interesantes y útiles sobre este tema, sin embargo, solo pretendo darle esta regla de oro por la cual puede pasar por alto una dificultad de comprensión y determinar muy rápidamente si se puede tener en cuenta en el código de estilo Applicative
.
[1] Los que aparecen a la izquierda de <-
.
[2] Expresiones que aparecen a la derecha de <-
.
[3] estrictamente hablando, algunas partes podrían, al factorizar e2 a
.
Vea Los fundamentos de los funtores aplicativos, puestos a la práctica por Bryan O''Sullivan.