
On Tue, 2007-09-25 at 11:40 +0100, Andrew Coppin wrote:
Aaron Denney wrote:
On 2007-09-25, Andrew Coppin
wrote: OK, *now* I'm puzzled... Why does map . map type-check?
(map . map) = (.) map map
(.) :: (a -> b) -> (b -> c) -> a -> c = (a -> b) -> (b -> c) -> (a -> c)
The first two arguments of (.) are 1-argument functions.
map :: (d -> e) -> [d] -> [e] = (d -> e) -> ([d] -> [e])
map is either a two argument function _or_ a function that takes one argument (a function) and returns a function.
In this latter view, for the first argument, of (.), we need:
a = d -> e b = [d] -> [e]
And for the second we know b = [d] -> [e] so c = [[d]] -> [[e]]
for everything to be consistent.
It's much clearer when you think of map not as "running this function over this list", but rather "turning this function that operates on elements into a function that operates on lists". Doing that twice (by composing) turns a function that operates on elements into a function that operates on lists of lists.
I just found it rather surprising. Every time *I* try to compose with functions of more than 1 argument, the type checker complains. Specifically, suppose you have
foo = f3 . f2 . f1
Assuming those are all 1-argument functions, it works great. But if f1 is a *two* argument function (like map is), the type checker refuses to allow it, and I have to rewrite it as
foo x y = f3 $ f2 $ f1 x y
which is really extremely annoying...
I'm just curiose as to why the type checker won't let *me* do it, but it will let *you* do it. (Maybe it hates me?)
In f . g, if g takes two arguments, f has to take a function as the first argument (because that's what g returns). jcc