Ich lerne Haskell und habe ein einfaches DB-Seed-Programm für Yesod durchgeführt, als ich auf dieses Verhalten gestoßen bin, das ich schwer zu verstehen finde:
testFn :: Int -> Bool -> [Int]
testFn a b = if b then replicate 10 a else []
Jessod GHCI-Sitzung:
$ :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]
Irgendwie war es möglich, diesen zweiten "Bool" aus jeder Zuordnung in ein einziges Curry-Argument zu "ziehen".
Die Standard-Prelude-GHCI-Basissitzung weigert sich, diesen Ausdruck überhaupt zu kompilieren:
$ :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]
Es stellt sich heraus, dass Jessod eine monotraversierbare Bibliothek verwendet, die eine eigene hat concatMap
:
$ :t concatMap
concatMap
:: (MonoFoldable mono, Monoid m) =>
(Element mono -> m) -> mono -> m
Bei meinem derzeitigen Verständnis von Haskell konnte ich nicht herausfinden, wie die Typen hier verteilt sind. Könnte mir jemand erklären (so viel wie möglich für Anfänger), wie dieser Trick gemacht wird? Welcher Teil von testFn
oben entspricht dem Element mono
Typ?