I know we can treat
F[A => B]
asF[C]
So C
is A => B
. Let's call that fact 1.
Let's rewrite bind
with replacing A with C and B with D, so that we don't get confused by colliding type parameter variables:
def bind[C, D](fc: F[C])(f: C => F[D]): F[D]
So the f
argument in the second parameter list of bind
has to be of type C => F[D]
, which can be written (A => B) => F[D]
(using fact 1). Note that it's sneaky how in bind(f)(f => ...)
, the second f
is just a lambda parameter (which happens to be a function), while the first f
is not a function. It could have been written bind(f)(fun => map(fa)(fun))
.
how is
f => map(fa)(f)
equivalent toA => F[B]
??
Well, it is not... f => map(fa)(f)
has to be typed as (A => B) => F[D]
. So
f
is of typeA => B
fa
is of typeF[A]
, that is thefa
in the first parameter list ofap
- not bind- Looking at the definition of map,
map(fa)(f)
will be of typeF[B]
Which means that
(A => B) => F[D]
f => map(fa)(f)
(A => B) => F[B]
// D is really B
So bind(f)(f => map(fa)(f))
is of type F[B]
which is what is required for ap
...
May be this makes it clearer, conceptually, this is what is going on:
def ap[A, B](m_elem: => F[A])(m_fun: => F[A => B]): F[B] =
for {
fun <- m_fun
elem <- m_elem
} yield {
fun(elem)
}
//To hammer it home, same as: m_fun.flatMap(fun => m_elem.map(elem => fun(elem)))