Estoy aprendiendo Haskell y estaba haciendo un simple programa de semilla de base de datos para Yesod cuando me topé con este comportamiento que me resulta difícil de entender:
testFn :: Int -> Bool -> [Int]
testFn a b = if b then replicate 10 a else []
Sesión Yesod GHCI:
$ :t concatMap testFn [3]
concatMap testFn [3] :: Bool -> [Int]
$ (concatMap testFn [1,2,3]) True
[1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3]
De alguna manera fue capaz de "sacar" ese segundo "Bool" de cada una de las asignaciones en un solo argumento curioso.
La sesión base estándar Prelude GHCI se niega incluso a compilar esta expresión:
$ :t concatMap testFn [3]
error:
• Couldn't match type 'Bool -> [Int]' with '[b]'
Expected type: Int -> [b]
Actual type: Int -> Bool -> [Int]
• Probable cause: 'testFn' is applied to too few arguments
In the first argument of 'concatMap', namely 'testFn'
In the expression: concatMap testFn [3]
Resulta que Yesod usa una biblioteca mono-transitable que tiene su propia concatMap
:
$ :t concatMap
concatMap
:: (MonoFoldable mono, Monoid m) =>
(Element mono -> m) -> mono -> m
En mi nivel actual de comprensión de Haskell, no pude entender cómo se distribuyen los tipos aquí. ¿Podría alguien explicarme (tanto como sea posible para principiantes) cómo se hace este truco? ¿Qué parte de lo testFn
anterior se ajusta al Element mono
tipo?