Я изучаю Haskell и делал простую программу DB-seed для Yesod, когда наткнулся на это поведение, которое мне трудно понять:
testFn :: Int -> Bool -> [Int]
testFn a b = if b then replicate 10 a else []
Сессия Йесод 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]
Каким-то образом ему удалось «вытащить» этот второй «Bool» из каждого отображения в один аргумент с каррированием.
Стандартный базовый сеанс Prelude GHCI отказывается даже компилировать это выражение:
$ :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]
Оказывается, Йесод использует моно-проходимую библиотеку, которая имеет свою собственную concatMap
:
$ :t concatMap
concatMap
:: (MonoFoldable mono, Monoid m) =>
(Element mono -> m) -> mono -> m
На моем нынешнем уровне понимания Haskell я не мог понять, как типы распределяются здесь. Может ли кто-нибудь объяснить мне (насколько это возможно для начинающих), как это делается? Какая часть testFn
выше соответствует Element mono
типу?