Search code examples
haskelltypesghciunification

Manually deriving the type `(.) (foldr(++)) (map (:))`


I'm trying to derive the type of (.) (foldr(++)) (map (:))

I start by deriving the type of foldr (++)

foldr :: (a1 -> b1 -> b1) -> b1 -> [a1] -> b1
(++)  :: [a2] -> [a2] -> [a2]

a1 ~ [a2]
b1 ~ [a2]
b1 ~ [a2]

So

foldr (++) :: [a2] -> [[a2]] -> [a2] ~ [a] -> [[a]] -> [a]

Then I derive the type of map (:)

map :: (a1 -> b1) -> [a1] -> [b1]
(:) :: a2 -> [a2] -> [a2]

a1 ~ a2
b1 ~ [a2] -> [a2]

So

map (:) :: [a2] -> [[a2] -> [a2]] ~ [a] -> [[a] -> [a]]

Finally the type of (.) (foldr(++)) (map (:))

(.) :: (b1 -> c1) -> (a1 -> b1) -> a1 -> c1
map (:) :: [a2] -> [[a2] -> [a2]]
foldr (++) :: [a3] -> [[a3]] -> [a3]

b1 ~ [a2]
c1 ~ [[a2] -> [a2]]
a1 ~ [a3]
b1 ~ [[a3]] -> [a3]

So I get

(.) (foldr(++)) (map (:)) :: a1 -> c1 ~ [a3] ->  [[a2] -> [a2]]

But if I ask GHCi for :t (.) (foldr(++)) (map (:)) I get (.) (foldr(++)) (map (:)) :: [a] -> [[[a] -> [a]]] -> [[a] -> [a]]

Which is different from my result, any help to derive the same result?

Thanks,
Sebastián.


Solution

  • In the final step, you mix up the argument oder. You unify b1 -> c1 with the type of map (:), but you should unify it with the type of foldr (++).

    More generally, you might want to learn how to debug such complicated calculations yourself. It is good that you do a sanity check at the end (in this case, by checking the result with ghci). If the sanity check doesn't work, a good next step can be to say or write down what each step is and why you did it. In this case, you would say "and then I unify b1 -> c1 with the type of map (:) because ...", and there you would find the bug because there's no reason to do this.

    Maybe rubber duck debugging works for you, where you explain your code to a rubber duck and by explaining, you see what's wrong about the code. .