Uczę się Haskell i robiłem prosty program DB-seed dla Yesod, kiedy natknąłem się na takie zachowanie, które trudno mi zrozumieć:
testFn :: Int -> Bool -> [Int]
testFn a b = if b then replicate 10 a else []
Sesja GHCI Yesod:
$ :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]
W jakiś sposób udało mu się „wyciągnąć” ten drugi „Bool” z każdego z mapowań w jeden curry argument.
Standardowa podstawowa sesja GHCI Prelude nie kompiluje nawet tego wyrażenia:
$ :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]
Okazuje się, że Yesod korzysta z biblioteki mono-przejezdnej, która ma swoją własną concatMap
:
$ :t concatMap
concatMap
:: (MonoFoldable mono, Monoid m) =>
(Element mono -> m) -> mono -> m
Na obecnym poziomie rozumienia Haskell nie mogłem zrozumieć, w jaki sposób typy są tutaj dystrybuowane. Czy ktoś mógłby mi wyjaśnić (jak najbardziej zorientowany na początkującego), jak to się robi? Która część testFn
powyższego jest zgodna z Element mono
typem?