Составление 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. Оказывается, этот случай более полезен для отладки, поскольку содержит подсказку, почему проверка типа не удалась.