haskell coding-style

¿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 .