I feel like I'm not really getting how function application works or maybe I'm fixating on something that I shouldn't be.
Let's try to determine the type signature of (fmap .)
fmap :: (x -> y) -> fx -> fy
(.) :: (b -> c) -> (a -> b) -> a -> c -- signatures taken from GHC
Applying fmap
to (.)
, we esentially wanna make (x -> y) -> fx -> fy
and (b -> c)
equal. When I first tried to do this I remember having a really hard time. You gotta match a set of 4 variables to a set of 2, so obviously you gotta do some grouping, but how? There's 3 possible solutions that I see, are all of them valid? Or is it only 1? I read that parentheses aren't really taken into consideration by the compiler, so we end up with x -> y -> fx -> fy
and b -> c
, but also functions associate to the right, so fmap
should actually look like x -> (y -> (fx -> fy))
, which lead me to
b = x
c = y -> fx -> fy
resulting in (a -> x) -> (a -> y -> fx -> fy)
as a signature for (fmap .)
.
The grouping I've seen this solved with though is (a -> b) -> (fa -> fb)
. So
b = x -> y
c = fx -> fy
leading to (a -> x -> y) -> a -> fx -> fy
, which is more or less the same as GHC's (a1 -> a2 -> b) -> a1 -> f a2 -> fb
if you rename some type variables.
Now to compare the 2 results, parentheses removed:
a -> x -> a -> y -> fx -> fy
a -> x -> y -> a -> fx -> fy
We can clearly see that the 3rd and 4th parameter types are switched, which means they're obviously not the same since flip
is a thing. Which means this substitution
b = x
c = y -> fx -> fy
is not valid. Or am I missing something?
I have a feeling that I'm trying to make the functions work based on the signatures when I should be doing it the other way around. :t fmap 'c'
fails with Couldn't match expected type 'a -> b' with actual type 'Char'
which means that you can't really partially apply the a -> b
part of fmap
. You can only partially apply fmap
itself and its first argument is indivisible I guess. I don't know if I'm getting the pattern behind it all though.
I should really try to write a typed lambda calculus.
There's 3 possible solutions that I see, are all of them valid?
No.
Or is it only 1?
Only one. (x -> y) -> fx -> fy
is equal to (x -> y) -> (fx -> fy)
and only to that since ->
is right associative. Putting parentheses in other points is wrong.
I read that parentheses aren't really taken into consideration by the compiler,
That's false. The compiler implicitly works as parentheses are always added in a right-associative way: the type a -> b -> c -> d
is handled as a -> (b -> (c -> d))
, only. By contrast, the type (a -> b) -> c -> d
is handled as (a -> b) -> (c -> d)
, only, and the type (a -> b -> c) -> d
is handled as (a -> (b -> c)) -> d
, only.
so we end up with
x -> y -> fx -> fy
andb -> c
,
No, that's wrong, you must keep the parentheses. We must satisfy the type equality
(x -> y) -> (f x -> f y)
~
b -> c
and the unique solution to this is
b ~ (x -> y)
c ~ (f x -> f y)
as you found out later.
To understand what's going on in unification, I think it's beneficial if you start by adding the implicit parentheses in a right-associative way. If you do that, you can forget about types like a -> b -> c
, and only care about the fundamental case (T -> U)
and its unification step:
from (T1 -> U1) ~ (T2 -> U2)
deduce T1 ~ T2 and U1 ~ U2
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.