Why we need >: in defining case class

I came across this definition. I wonder why we need this EE :> E to have EE as superclass?. Why not just stick with E?

trait Either[+E, +A] {
def map[B](f: A => B): Either[E, B] =
this match {
case Right(a) => Right(f(a))
case Left(e) => Left(e)
}
def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] =
this match {
case Left(e) => Left(e)
case Right(a) => f(a)
}
def orElse[EE >: E, AA >: A](b: => Either[EE, AA]): Either[EE, AA] =
this match {
case Left(_) => b
case Right(a) => Right(a)
}
def map2[EE >: E, B, C](b: Either[EE, B])(f: (A, B) => C): Either[EE, C] =
for { a ← this; b1 ← b } yield f(a,b1)
}
case class Left[+E](value: E) extends Either[E, Nothing]
case class Right[+A](value: A) extends Either[Nothing, A]

Maybe this answer of mine on SO may help you understand what is variance, why we need the [EE >: E] to fix the error “covariant type found in invariant position”, as well as why this is useful.

If you still have doubt do not hesitate in reply with any questions you have.


BTW, it seems you are pretty new to the language and that you are trying to learn it by following the famous red book. While the book is great, IMHO, it is not really a good material for learning Scala as a language but for learning Functional Programming (and how to apply it in Scala).
And trying to learn the language plus FP at the same time may be somewhat hard; so I would recommend you to first focus on learning the language and then return to the red book.

If Either, Left, Right were invariant the following code would compile

trait Either[E, A] {
  def map[B](f: A => B): Either[E, B] =
    this match {
      case Right(a) => Right(f(a))
      case Left(e) => Left(e)
    }
  def flatMap[B](f: A => Either[E, B]): Either[E, B] =
    this match {
      case Left(e) => Left(e)
      case Right(a) => f(a)
    }
  def orElse(b: => Either[E, A]): Either[E, A] =
    this match {
      case Left(_) => b
      case Right(a) => Right(a)
    }
  def map2[B, C](b: Either[E, B])(f: (A, B) => C): Either[E, C] =
    for { a <- this; b1 <- b } yield f(a,b1)
}
case class Left[E, A](value: E) extends Either[E, A]
case class Right[E, A](value: A) extends Either[E, A]

If you now make Either, Left, Right covariant this code start not to compile with covariant type occurs in contravariant position. One fix is to introduce EE >: E, AA >: A where necessary. Another possible fix would be to define map, flatMap, orElse, map2 as extension methods like it’s done in Shapeless with HList methods to avoid covariance issues.

Do you know some books or material for learning scala that teach all this covariants and >: and other stuff? Do you think I can try Programming in Scala by Martin Odresky?

Sure, the official page has a good list of resources, you can check it here.

Now a more personal opinion would be:

  1. Do the two first courses of the Functional Programming in Scala Specialization (they are free).
    1.1. Check the tour of scala, look at the index and read the pages that you find interesting (maybe all of them :stuck_out_tongue: )
  2. Pick up any of the following books:
  • Programming in Scala. (not free, but probably the most complete one of the list)
  • Creative Scala. (free, practical)
  • [Hands-on Scala](https://www.handsonscal a.com/). (not free, practical)
  • Scala for the impatient. (not free, concise)
  1. Write some code!
  2. Read Scala with Cats.
  3. Continue with the red book.

I decided to buy programming in scala 4 edition for 29 usd. I like comprehensive books. Do you know book that is mainly about monads in scala or in general? I would love to read about monads.

There are a lot of monads here: https://github.com/hmemcpy/milewski-ctfp-pdf/releases/tag/v1.3.0

In Haskell there is also https://www.amazon.com/Book-Monads-practice-applied-problems-ebook/dp/B07JNZHYLT

An alternative approach: personally, I found that the way to learn Monads was the other way around. That is, after reading many tutorials and struggling to understand this subject for years, I:

  • Learned Scala
  • Learned the List, Option and Future types
  • Learned how to use those in nested for-comprehensions (which are, on the surface, similar to nested for-loops in other languages)
  • Learned how those for-comprehensions “desugar” (they are nothing but function calls into the data structures, and there’s a mechanical process for turning a for-comprehension into those function calls)
  • Learned what those function calls actually do
  • That’s basically it

There’s a lot of theory to Monads, and if you like that sort of thing, go for it. But in practice, Scala Monads are just types where there is a sensible definition for the map() and flatMap() methods. If you have those methods, it’s at least basically Monad-shaped.

(That is to say, in programming terms, Monads are effectively a design pattern, that happens to be so useful, so frequently, that Scala built first-class support for it into the language. The term comes from category theory, but you don’t have to know any category theory to understand them in practice.)

In principle, you also need some sort of pure() method that constructs a new Monad instance out of a value – that tends to just be the basic constructor in standard Scala collections. Libraries like Cats make this more explicit than the language itself does.

So when I teach Monads at work, the above is how I usually go about it – for many people, it’s easiest to learn the theory by seeing it in practice first. It’s a bit over-simplified, and glosses over some of the theoretical details (there are some additional laws that a proper Monad is supposed to follow, which leads to frequent arguments about whether Future is really a Monad), but that’s enough to be able to use Monads seriously and consistently in Scala.

1 Like