Question

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] ??

Was it helpful?

Solution

I know we can treat F[A => B] as F[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 to A => 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 bind
  • Looking at the definition of map, map(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)))

OTHER TIPS

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top