Составление concat и map для получения concatMap: почему f?
Это мои первые исследования в Haskell, поэтому простите меня, если это будет очевидно.
Я играл весь день с Haskell, просеивая через учебник 99 вопросов по HaskellWiki, используя мой собственный тип списка (типичный минус). Я добавил "очевидные" функции по мере того, как я продолжал, и я постарался сделать их максимально лаконичными (с использованием точечной нотации, когда это возможно)
12-я проблема заключается в декодировании кодированного списка длины, который:
> decode [Multiple 5 'a', Single 'b', Multiple 2 'c']
"aaaaabcc"
И я подумал об использовании map
для декодирования каждого элемента, затем concat
результата (спасибо Google на этом) и, наконец, вспомнил, что в своих чтениях я видел что-то вроде concatMap
, которое GHCi быстро подтвердило:
> :t map
map :: (a -> b) -> [a] -> [b]
> :t concat
concat :: [[a]] -> [a]
> :t concatMap
concatMap :: (a -> [b]) -> [a] -> [b]
Было похоже, что было бы переопределить concatMap
:
concatMap :: (a -> [b]) -> [a] -> [b]
concatMap = concat . map
За исключением того, что GHCi вполне жалуется:
List.hs:110:15:
Couldn't match expected type `[a] -> [b]'
with actual type `[a0]'
Expected type: [[a0]] -> [a] -> [b]
Actual type: [[a0]] -> [[a0]]
In the first argument of `(.)', namely `concat'
In the expression: concat . map
Я не мог понять это, поэтому я посмотрел в Интернете, и на самом деле определение, указанное в Prelude:
concatMap f = concat . map f
И я не совсем понимаю, почему это f необходимо, так как это тип, очевидно, a -> [b]
, как указано сигнатурой...
Итак, зачем здесь f
?
Ответы
Ответ 1
Начиная со стандартного определения,
concat . map f
≡ concat . (map f)
≡ \x -> concat ((map f) x)
Ваше первое определение дает вам:
(concat . map) f
≡ (\x -> concat (map x)) f
≡ concat (map f)
который не устанавливает проверку, потому что (map f) :: [a] -> [b]
, а concat
принимает [[a]]
, список списков.
Обратите внимание, что конкретное сообщение об ошибке, указанное в вопросе, не описывает описанный выше сбой проверки типа. Данное сообщение возникает из объявления возвращаемого типа concatMap
как [a] -> [b]
, который не совместим с [a0]
, возвращаемым типом concat
. Если вы оставите подпись типа, ответ будет следующим:
Couldn't match type ‘[a1] -> [b]’ with ‘[[a]]’
Expected type: (a1 -> b) -> [[a]]
Actual type: (a1 -> b) -> [a1] -> [b]
Relevant bindings include
concatMap :: (a1 -> b) -> [a] (bound at :49:5)
Probable cause: ‘map’ is applied to too few arguments
In the second argument of ‘(.)’, namely ‘map’
In the expression: concat . map
Здесь ошибка типа возникает при согласовании возвращаемого типа map
с типом аргумента concat
. Оказывается, этот случай более полезен для отладки, поскольку содержит подсказку, почему проверка типа не удалась.