Search code examples
haskellfiltertypestype-inferencemap-function

Generic type of (map . filter)


I don't understand why map . filter generic type is map . filter :: (a -> Bool) -> [[a]] -> [[a]].

I know that map and filter types are map :: (a -> b) -> [a] -> [b] and filter :: (a -> Bool) -> [a] -> [a]. And also (.) :: (b -> c) -> (a -> b) -> a -> c.

So my guess was that a = (a -> Bool) -> [a], b = [a] and since the output of filter is not a function, I thought that map . filter would return a function that expect a function (a -> b).

I don't understand why the type is a list of lists of a, since neither map nor filter has a list of lists. I also don't understand why it works with just one function since both need one.

Can someone explain how does it work, please?


Solution

  • First, lets use different letters for the types in the functions. That way we won't get confused.

    map :: (a -> b) -> [a] -> [b]
    filter :: (c -> Bool) -> [c] -> [c]
    (.) :: (e -> f) -> (d -> e) -> d -> f
    

    So now we consider map . filter. Doing the substitution we get the following (~ is used for type equality):

    d ~ (c -> Bool)
    e ~ ([c] -> [c])   -- Result of 'filter'
    e ~ (a -> b)       -- Argument of 'map'
    f ~ ([a] -> [b])
    

    Notice how we get two types for e. By substitution,

    a ~ b ~ [c]
    

    So therefore

    f ~ ([[c]] -> [[c])
    

    And so we can substitute for d and f in the definition of (.) and get

    (c -> Bool) -> [[c]] -> [[c]]
    

    Which is what GHCi was telling us.

    What this function actually does is apply the filter to each sublist; the function argument taken by map is the filter. So in GHCi,

    Prelude> import Data.Char
    Prelude Data.Char> (map.filter) isDigit ["foo123", "456bar"]
    ["123","456"]