I'm having trouble deciphering the following function. Particularly, the flatMap line - where are the 'aa' and 'bb; variables coming from, and how is this executed? Maybe if someone could explain in words what is happening with this code it would be helpful. I'm just really struggling how to read this syntax.
def map2[EE >: E, B, C](b: Either[EE, B])(f: (A, B) => C): Either[EE, C] =
this flatMap(aa => b map (bb => f(aa, bb)))
Here is the flatMap function:
def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] = this match {
case Right(r) => f(r)
case Left(e) => Left(e)
}
and map:
def map[B](f: A => B): Either[E, B] = this match {
case Right(r) => Right(f(r))
case Left(e) => Left(e)
}
Looks like all these functions are defined for Either class. Either can be in two 'states' - Right - correct value and Left which interpreted ass error.
Lets go from the map definition. We have Either parametrized with type A. map accepts function converting type A to B. So map function checks if value is Right then it applies given function f to Right value, wraps it with Right and return. If error then just return error.
Right[String, Int](4).map(_*3) // gives Right(12)
Left[String, Int]("error").map(_*3) // gives Left("error")
flatMap is similar to map, but accepts function converting type A to Either of right type B, so unlike map we don't need to wrap right value
Right[String, Int](5).flatMap(item => Right(item*3)) //gives Right(15)
Left[String, Int]("error").flatMap(item => Right(item*3)) //gives Left("error")
As defined, map2 takes instance of Either b and function, converting pair of values of types A and B to some third value C, and return the result wrapped with Either. Let's take closer look at how it is implemented.
this.flatMap(aa => b map (bb => f(aa, bb)))
lets rewrite it with definition of flatMap:
this.match {
case Right(r) => b.map (bb => f(r, bb))
case Left(e) => Left(e)
}
And apply map:
this.match {
case Right(r) => {
b match {
case Right(br) => Right(f(r, br))
case Left(be) => Left(be)
}
}
case Left(e) => Left(e)
}
So there are three paths:
if right a and right b then apply function(a,b)
if right a and error b then error(b)
if error a then error a
Examples:
Right(7).map2(Right(4)){case (a,b) => a*b} // gives Right(28)
Right(7).map2(Left[String, Int]("failed")){case (a,b) => a*b} //gives Left("failed")
Left[String, Int]("error").map2(Left[String, Int]("failed")){case (a,b) => a*b} //gives Left("error")
Edit: Inner function explanation
You have to pass to a flatMap function with type:
A => Either[EE, C]
It means "take an argument of type A and convert it to result of type Either[EE,C]"
so you could define:
def map2[EE >: E, B, C](b: Either[EE, B])(f: (A, B) => C): Either[EE, C] = {
def func(aa:A):Either[EE, C] = {
b map (bb => f(aa, bb))
}
this flatMap(func)
}
Related
I am having problems understanding this code from the Book FP in Scala. Here is the code:
trait Monoid[A] {
def op(a1: A, a2: A): A
def zero: A
}
def endoMonoid[A]: Monoid[A => A] = new Monoid[A => A] {
def op(f: A => A, g: A => A) = f compose g
val zero = (a: A) => a
}
def foldMap[A, B](as: List[A], m: Monoid[B])(f: A => B): B =
as.foldLeft(m.zero)((b, a) => m.op(b, f(a)))
// The function type `(A, B) => B`, when curried, is `A => (B => B)`.
// And of course, `B => B` is a monoid for any `B` (via function composition).
def foldRight[A, B](as: List[A])(z: B)(f: (A, B) => B): B =
foldMap(as, endoMonoid[B])(f.curried)(z)
foldMap is expecting a function f: A => B.
In foldRight, when f is curried you have A => (B => B), so I suppose f.curried is working because it is the same as (A => B => B), so foldRight is passing in to foldMap what it expect (a function with type A => B), then, what happends next is that foldMap is called and its returning a function B => B, and that's when z comes into play in (f.curried)(z) you call the function B => B with the argument z to get the final B.
Am I right? it is a litle complicated to reason about this code for me.
NOTE: Here is a scalafiddle if you want to play with it.
Well, you seem to be mostly comprehensive to me. Nevertheless, I would clarify some points:
I'd rather say "so I suppose f.curried is working because A => (B => B) is the same as (A => B => B)" (it is ambiguous here and you're talking about f.curried result type basically, not with z)
I'd rather put a point instead of a comma here: "foldMap is expecting a function f: A => B . In foldRight, ... " and pretty much every where else. Shorter phrases, clearer explanation.
what could be an error, (and what is confusing to you?) is that (f.curried)(z) doesn't work on its own and is not called after foldMap(as, endoMonoid[B]). It's first foldMap(as, endoMonoid[B])(f.curried) which is called and then (z). The first returns B => B and called with the second returns B.
I have a trait that is extended by multiple subclasses
trait Sup
case class Sub[A, B](a: A, f: B => B)(implicit val ev: A =:= B) extends Sup
case class Sub2[A, B](a: A, f: B => Unit)(implicit val ev: A =:= B) extends Sup
And two functions:
def foo[A, B](a: A, f: B => B)(implicit ev: A =:= B) = f(a)
def bar[A, B](a: A, f: B => Unit)(implicit ev: A =:= B) = f(a)
Now I can do some form of dynamic dispatching and call foo if the object is a Sub and bar if the object is a Sub2.
def dispatch(obj: Sup) = {
obj match {
case Sub(a, f) => foo(a, f)
case Sub2(a, f) => bar(a, f) // type mismatch: found: Nothing => Unit. required: B => Unit
}
}
I've also tried to pass the evidence explicitly but it results in the same error:
case o # Sub2(a, f) => bar(a, f)(o.ev) // type mismatch
It is very weird that f: B => B works (I can call foo), but f: B => Unit doesn't work (I can't call bar).
Not an answer but something to think about:
case class Sub1[A, B](a: A, f: B => B)
case class Sub2[A, B](a: A, f: B => Unit)
def foo[A, B](a: A, f: B => B)(implicit ev: A =:= B) = f(a)
def bar[A, B](a: A, f: B => Unit)(implicit ev: A =:= B) = f(a)
def dispatch(obj: Any) = obj match {
case Sub1(a, f) => foo(a, f)
case Sub2(a, f) => bar(a, f) // type mismatch: found: Nothing => Unit. required: B => Unit
}
This code have the same problem as yours but Sub1 and Sub2 case classes don't even have implicit blocks.
implicit section in case class doesn't effect pattern resolution. This section is just syntax sugar for calling apply(a: A, f: B => B)(implicit val ev: A =:= B) method on Sub1/2's companion objects. Pattern matching use unapply method to match the pattern at runtime and this unapply don't even know about evidences.
But I'm still wondering why first case is compiled without having this evidence.
Edit: Adding helpful comment from #AlexeyRomanov
More type inference than type erasure. But yes, the compiler infers type Any for a and Any => Any for f and then produces and uses evidence that Any =:= Any. In the second case it infers Nothing => Unit for f, because B => Unit is contravariant in B, and fails to find Any =:= Nothing.
You can actually make it work using type variable patterns:
def dispatch(obj: Sup) = {
obj match {
case obj: Sub[a, b] => foo(obj.a, obj.f)(obj.ev)
case obj: Sub2[a, b] => bar(obj.a, obj.f)(obj.ev)
}
}
This part is an answer to the comments, because it doesn't really fit in there:
Btw, there is still one subtlety I do not get: why is B => Unit contravariant in B
what is compiler's logic for this Nothing => Unit inference staff
You need to start with function variance. X => Y is a subtype of X1 => Y1 if and only if X is a supertype of X1 and Y is a subtype of Y1. We say it's contravariant in X and covariant in Y.
So if you fix Y = Unit, what remains is just contravariant in X. Any => Unit is a subtype of String => Unit, which is a subtype of Nothing => Unit. In fact, Nothing => Unit is the most general of all B => Unit, and that's why it gets inferred in the Sub2 case.
and B => B not (since it infers Any => Any) ?
The situation with B => B is different: String => String is neither a subtype nor a supertype of Any => Any, or of Nothing => Nothing. That is, B => B is invariant. So there is no principled reason to infer any specific B, and in this case the compiler uses the upper bound for B (Any), and B => B becomes Any => Any.
How do I remove explicit casting asInstanceOf[XList[B]] in Cons(f(a), b).asInstanceOf[XList[B]] inside map function? Or perhaps redesign reduce and map functions altogether? Thanks
trait XList[+A]
case object Empty extends XList[Nothing]
case class Cons[A](x: A, xs: XList[A]) extends XList[A]
object XList {
def apply[A](as: A*):XList[A] = if (as.isEmpty) Empty else Cons(as.head, apply(as.tail: _*))
def empty[A]: XList[A] = Empty
}
def reduce[A, B](f: B => A => B)(b: B)(xs: XList[A]): B = xs match {
case Empty => b
case Cons(y, ys) => reduce(f)(f(b)(y))(ys)
}
def map[A, B](f: A => B)(xs: XList[A]): XList[B] = reduce((b: XList[B]) => (a: A) => Cons(f(a), b).asInstanceOf[XList[B]])(XList.empty[B])(xs)
You can merge two argument lists into one by replacing )( by ,:
def reduce[A, B](f: B => A => B, b: B)(xs: XList[A]): B = xs match {
case Empty => b
case Cons(y, ys) => reduce(f, f(b)(y))(ys)
}
def map[A, B](f: A => B)(xs: XList[A]): XList[B] =
reduce((b: XList[B]) => (a: A) => Cons(f(a), b), XList.empty[B])(xs)
This will force the type inference algorithm to consider both first arguments of reduce before making up its mind about what B is supposed to be.
You can either widen Cons to a XList[B] at the call site by providing the type parameters explicitly:
def map[A, B](f: A => B)(xs: XList[A]): XList[B] =
reduce[A, XList[B]]((b: XList[B]) => (a: A) => Cons(f(a), b))(XList.empty[B])(xs)
Or use type ascription:
def map[A, B](f: A => B)(xs: XList[A]): XList[B] =
reduce((b: XList[B]) => (a: A) => Cons(f(a), b): XList[B])(XList.empty[B])(xs)
As a side note, reduce is traditionally more strict at the method definition than what you've written. reduce usually looks like this:
def reduce[A](a0: A, a: A): A
Implicitly requiring a non empty collection to begin with. What you've implemented is similar in structure to a foldLeft, which has this structure (from Scalas collection library):
def foldLeft[B](z: B)(op: (B, A) => B): B
I have the following scala code (from the FP In Scala book):
import scala.{Option => _, Either => _, Left => _, Right => _, _} // hide std library `Option` and `Either`, since we are writing our own in this chapter
case class Left[+E](get: E) extends Either[E,Nothing]
case class Right[+A](get: A) extends Either[Nothing,A]
sealed trait Either[+E,+A] {
def map[B](f: A => B): Either[E, B] = this match {
case Right(r) => Right(f(r))
case Left(e) => Left(e)
}
def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] = this match {
case Right(r) => f(r)
case Left(e) => Left(e)
}
def map2[EE >: E, B, C](b: Either[EE, B])(f: (A, B) => C): Either[EE, C] = {
this flatMap(aa => b map (bb => f(aa, bb)))
}
}
I would like to know what is going on when I call map2 like this:
val b = fpinscala.errorhandling.Right(2)
val a = fpinscala.errorhandling.Right("right")
val f = (a: String, b:Int) => a + b
a.map2(b)(f)
How does scala know to use substitute this (i.e. a) into the aa in this line of code: this flatMap(aa => b map (bb => f(aa, bb))) in the map2 function?
#jcm, see if this makes any sense.
a.map2(b)(f)
We're calling the map2 method of the a object. Inside the method code a is now referenced as this.
this flatMap(aa => b map (bb => f(aa, bb)))
Now we're calling the flatMap method of the this object (which is still a).
What does flatMap take as an argument? It takes a function, a function that takes an argument (of some type that we'll refer to as "A") and returns an Either. So that means that everything between the parentheses is that function. That function has no name (i.e. no def statement) so it's often called an anonymous function.
That function takes an argument. This code aa => identifies the argument so that it can be referred to later in the function. "aa" is just an arbitrary name. We could have used "thisthat" or "xyz".
Where does the value that aa contains come from? It comes out of this. Consider the following code.
List(4,7,9).map(x => 42 - x)
In this case map invokes the anonymous function three times, once for every value from the List. x takes on a new value (first 4, then 7, then 9) every time the function is invoked.
If that all makes any sense then apply the same logic to b map (bb => .....) and you're almost home!
How does scala know to use substitute this (i.e. a) into the aa
aa is not replaced with this. aa is replaced with the string "right".
The replacement is done by flatMap here:
case Right(r) => f(r)
flatMap checks if this instance of Either is a Right or a Left. If it is a Right, then it feeds its content (the string "right") into the function f (aa => b map (bb => f(aa, bb))).
How does scala know to use substitute this (i.e. a) into the aa in
this line of code: this flatMap(aa => b map (bb => f(aa, bb))) in the
map2 function?
It is not. What is going on here is this:
A closure is being built, which takes an Either[EE,B], calls it aa and computes b map (bb => f(aa, bb). b and f are taken from the parent scope.
this.flatMap is being called, given that function as a parameter.
Read more on Scala closures :-)
While thinking about my previous question, I realized I ought to be able to write something like the following:
val empty: Try[B, forall types B] = Failure(new RuntimeException("empty"))
def firstSuccess[A, B](xs: Iterable[A], f: A => Try[B]): Try[B] = {
xs.foldLeft(empty)((e, a) => e.recoverWith { case _ => f(a) })
}
because a Failure is a valid Try[B] for any type B. Is there a way to achieve my "B, forall types B" in Scala?
You can use the Nothing type since everything in scala is Nothing:
val empty = Failure[Nothing](new RuntimeException("empty"))
def firstSuccess[A, B](xs: Iterable[A], f: A => Try[B]): Try[B] = {
xs.foldLeft[Try[B]](empty)((e, a) => e.recoverWith { case _ => f(a) })
}
You do have to sprinkle in a few types here and there though (added type parameter to foldLeft).