Reputation: 121
I'm studying Scalaz 7, the type class system is so abstract, and one thing I can not understand is that why Bind.ap
is implemented by bind
in such a way.
trait Apply[F[_]] extends Functor[F] { self =>
def ap[A,B](fa: => F[A])(f: => F[A => B]): F[B]
....
}
trait Bind[F[_]] extends Apply[F] { self =>
/** Equivalent to `join(map(fa)(f))`. */
def bind[A, B](fa: F[A])(f: A => F[B]): F[B]
override def ap[A, B](fa: => F[A])(f: => F[A => B]): F[B] = bind(f)(f => map(fa)(f))
....
}
I know we can treat F[A => B]
as F[C]
, so first argument of bind
make sense, but the second argument requires a A => F[B]
, how is f => map(fa)(f)
equivalent to A => F[B]
??
Upvotes: 0
Views: 110
Reputation: 41656
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 type A => B
fa
is of type F[A]
, that is the fa
in the first parameter list of ap
- not bindmap(fa)(f)
will be of type F[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)))
Upvotes: 3
Reputation: 21567
As you can see from the bind
method signature, it's just a pretentious Haskell's way of naming flatMap
function. So Bind
trait provides an essential flatMap
for a Monad
.
Maybe it would be simpler to understand if we take List[Int => String]
instead of F[A => B]
, so what bind
is doing it takes each function from the list, let's say we have the following list: List((x: Int) => (x + 1).toString)
as f
argument and List(1,2)
as fa
argument to ap
method, and apply each function from f
argument (List
of Int => String
functions) to each value of fa
argument (List
of Int
).
So the answer on how is f => map(fa)(f) equivalent to A => F[B], A
in this code is a Int => String
function from f
List
and when you map some value from fa
List
you get F[B]
, which would be of type String
Upvotes: 1