So let's take this apart a bit. First, the original function:
flip (\n s -> show n <> s) "Ten" 10
If we take the type of each part, what do we see?
> :t flip
forall a b c. (a -> b -> c) -> b -> a -> c
This is really just flip
of course, and it takes a two-parameter function and turns it into another one with the arguments flipped.
Next, and this is the interesting part, the (a -> b -> c)
of flip
, which becomes b -> a -> c
:
> :t (\n s -> show n <> s)
forall t4. Show t4 => t4 -> String -> String
Ok, so, how does it come up with t4 -> String -> String
? The only function in this one that references String
specifically is show
:
> :t show
forall a. Show a => a -> String
Also, this is where the Show
constraint on t4
comes from. What PureScript is telling us is that (\n s -> show n <> s)
is a function, it takes two arguments, and returns a String
.
It's calling our first argument t4
, which is a valid and unique type variable for this session. It also can't tell us anything about t4
except that since show
requires an instance of Show
, t4
must be an instance of Show
.
Now, calling show
on our Show => t4
will return a String
, which we expect show
to do:
> :t show
forall a. Show a => a -> String
(Yes, we saw this already.) So, inside our function (\n s -> show n <> s)
, the show n
term has type String
. This is because show
has been fully applied with t4
, which is an instance of Show
, and so the compiler can infer that show n
will have the type of show
, String
.
Now this is where it gets interesting. <>
in its most general form has a single type parameter:
> :t (<>)
forall a. Semigroup a => a -> a -> a
It's a function, and it takes two values of type a
, and returns a new value of type a
. Note how while the function has a type parameter, it only has one type parameter, so any particular (<>)
will not be polymorphic in its types.
Now, while our function is polymorphic in its first parameter, it also only has a single type variable. This is actually a bit of a red herring anyway, because let's see what flip
does:
> :t flip (\n s -> show n <> s)
forall t5. Show t5 => String -> t5 -> String
Woah. Our polymorphic parameter is still there, but now it's the second one. This is what flip
does. So what does this mean, really?
> flip (\n s -> show n <> s) "Ten" 10
In this case, "Ten"
is a String
, which our flip
ped function expects. 10
is, well, it's something:
> :t 10
Int
In this case it's an Int
. Is Int
an instance of Show
?
> show (10 :: Int)
"10"
Yep! So, the values "Ten"
and 10
satisfy the parameters of the flip
ped function, forall a. Show a => String -> a -> String
, where "Ten"
is String
and 10
, an Int
, is an instance of Show
.
Now, let's look at the failing case:
> flip (\n s -> show n <> s) 10 "Ten"
"Ten"
is a String
. Is String
an instance of show
?
> show "Ten"
"\"Ten\""
It is! Neat! So that's fine. Now, is 10
a String
? Well, no, sadly, it's not. So 10
can't be used as the first argument to the flip
ped function.