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?
CodePudding user response:
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"]
CodePudding user response:
The type of (.)
,
(.) :: (b -> c) -> (a -> b) -> a -> c
-- f . g :: a -> c when g :: a->b, f :: b->c
means that
g :: a -> b
f :: t -> c
-------------------- , b ~ t
f . g :: a -> c
Then following this same schema we get right away that
filter :: (a->Bool) -> ([a] -> [a])
map :: ( s -> t ) -> [ s ] -> [ t ]
-----------------------------------------------------------
s ~ [a] t ~ [a]
-----------------------------------------------------------
map . filter :: (a->Bool) -> [[a]] -> [[a]]
It makes sense, too. (map . filter) p = map (filter p)
, so this map
applies filter p
to each element of a list (as map
s do).
Since p :: a -> Bool
, then filter p :: [a] -> [a]
.
Since this function is applied to each element of a list, this means that each element of that list has the type [a]
, and is transformed to another [a]
.
Thus the whole list was [[a]]
, and the result of the transformation is also [[a]]
.