Reputation: 21063
If the type of foldr
is
> :t foldr
forall a b. (a -> b -> b) -> b -> [a] -> b
and
> :t id
forall a. a -> a
then I would expect foldr (.) id
to have the same type as foldr
, rather than
> :t foldr (.) id
forall b. [b -> b] -> b -> b
It appears I'm mistaken about how composition works, because I had thought that for a function f
that f . id
would give f(id(x)) == f(x)
, preserving the type of f
. What am I misunderstanding that would clarify the meaning of foldr (.) id
and composition more generally?
Upvotes: 4
Views: 158
Reputation: 120741
This is not composition with id
, as you'd get from foldr . id
(notice the absence of parens). That would indeed be equivalent to foldr
alone, perhaps the most important equivalency in category theory and thus also fundamental to Haskell.
Instead, what you've done there is, passed both (.)
and id
as arguments to foldr
: putting .
in parens makes it just another expression so ordinary Haskell function parsing applies, i.e. greedily use consecutive terms as arguments to the first one. You're lucky that this makes for a good type at all, for instance succ (.) id
would have given the ridiculous signature Enum ((c -> c) -> (a -> c) -> a -> c) => (a -> c) -> a -> c
.
How exactly it works with foldr
can be seen by writing
(.) :: (y->z) -> (x->y) -> (x->z)
unify (x->y) = (x->z)
as in foldr
's argument, i.e. y = z
,
(.) :: (y->y) -> (x->y) -> (x->y)
foldr (.) :: (x->y) -> [y->y] -> (x->y)
Then id
also requires x = y
,
foldr (.) id :: [x->x] -> (x->x)
Upvotes: 11
Reputation: 19772
You are not composing foldr
with id
. That would be foldr . id
. What you are actually doing is applying foldr
to (.)
, and then applying the result of such application to id
.
Upvotes: 6
Reputation: 2167
What you are saying here is that you are going to be composing a list of functions who all have the same type (since they are in a list). id
is the initial function that you'll be folding from. When you are done composing all of them, you'll get back a function. It might be easier to see the result type as [b -> b] -> (b -> b)
.
By the way, when you use an operator in Haskell and you want to infix it, you omit the parantheses. Only when you are using it as any other function do you include the parantheses.
Upvotes: 1