Reputation: 41919
Looking at the type of ($)
and flip
:
ghci> :t ($)
($) :: (a -> b) -> a -> b
ghci> :t flip
flip :: (a -> b -> c) -> b -> a -> c
Can you please explain to me how flip ($)
has such a signature?
ghci> :t flip ($)
flip ($) :: b -> (b -> c) -> c
Upvotes: 3
Views: 190
Reputation: 74234
Pretty simple really:
($) :: (a -> b) -> a -> b
|______| | |
| | |
flip :: (a -> b -> c) -> b -> a -> c
Hence we are essentially unifying (a -> b -> c)
with (a -> b) -> a -> b
. For the sake of clarity let's rename (a -> b -> c)
to (r -> s -> t)
:
($) :: (a -> b) -> a -> b
|______| | |
| | |
flip :: (r -> s -> t) -> s -> r -> t
Hence:
r
unifies with (a -> b)
.s
unifies with a
.t
unifies with b
.Therefore:
flip ($) :: s -> r -> t
:: a -> (a -> b) -> b
This is equivalent to:
flip ($) :: b -> (b -> c) -> c
That's all that there is to it.
Edit:
flip
function has one argument, (a -> b -> c)
, and one return value b -> a -> c
.flip ($)
, the ($)
function becomes the first argument of the flip
function.($)
is unified with the type signature of the argument of flip
.(a -> b -> c)
and (a -> b) -> a -> b
.(a -> b -> c)
is first renamed to (r -> s -> t)
and then r
is substituted for (a -> b)
, s
is substituted for a
and t
is substituted for b
.For example, we could write a Prolog program to unify terms:
% fun(R, fun(S, T)) is equivalent to (r -> s -> t).
% fun(fun(A, B), fun(A, B)) is equivalent to (a -> b) -> a -> b.
?- fun(R, fun(S, T)) = fun(fun(A, B), fun(A, B)).
R = fun(A, B),
S = A,
T = B.
A unification algorithm can be found here: http://www.learnprolognow.org/lpnpage.php?pagetype=html&pageid=lpn-htmlse5
To summarize the unification algorithm, when unifying term1
and term2
:
term1
and term2
are constants then they unify if and only if they are the the same constant. For example Int
only unifies with the constant Int
. It doesn't unify with the constant Char
.term1
is a variable and term2
is a non-variable then term1
is instantiated to term2
(i.e. term1 := term2
). Example, a
and Int
unify with a := Int
.term2
is a variable and term1
is a non-variable then term2
is instantiated to term1
(i.e. term2 := term1
). Example, Int
and b
unify with b := Int
.term1
and term2
are both variables then they are both instantiated to one another and they are said to share values (i.e. term1 := term2
and term2 := term1
). For example, a
and b
, when unified, become the same variable.term1
and term2
are complex terms (e.g. term1
is Either a Int
and term2
is Either Char b
), then they unify if and only if:
Maybe Int
and Maybe Char
have the same type constructor. However, Maybe Int
and [Int]
do not.Either a Int
and Either Char b
, the arguments unify with a := Char
and b := Int
.Either a a
with Either Char Int
, we first have a := Char
and then a := Int
. This is incompatible. Hence the two terms do not unify.Upvotes: 12
Reputation: 4364
Unflipped $
gets a value of type a
on its right side and applies it to the function (a -> b)
from its left side: func $ value
is the same as func value
.
Flipped $
takes a value of type b
on its left side and applies the function (b->c)
from its right side to it: value `flip ($)` func
is the same as func value
.
Upvotes: 2
Reputation: 30237
Line up corresponding components of the types:
($) :: (a -> b) -> a -> b
flip :: (a -> b -> c) -> b -> a -> c
Important rule: in any type signature, you can replace every occurrence of a type variable with another that doesn't appear anywhere in the same signature, and get a type that is completely equivalent to the original. (Types don't care about the names of the type variables, only which variables are equal and which are different within the same signature.) So we can rename type variables slightly in flip
(a := x
, b := y
, c := z
) and it's the same type:
($) :: (a -> b) -> a -> b
flip :: (x -> y -> z) -> y -> x -> z
Slightly different rule: in any type signature, we can replace all instances of a type variable with any type and get a specialized version of that type. Let's specialize flip
to the type that makes it compatible with ($)
. We do that by replacing x := (a -> b)
, y := a
and z := b
:
($) :: (a -> b) -> a -> b
flip :: ((a -> b) -> a -> b) -> a -> (a -> b) -> b
Now that the type of the first argument of this specialized version of flip
matches the type of ($)
, we can figure out the type of flip ($)
:
flip ($) :: a -> (a -> b) -> b
Now we replace b := c
, and after that, a := b
:
flip ($) :: b -> (b -> c) -> c
Upvotes: 4