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:
- 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 )
- 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)
- Write some code!
- Read Scala with Cats.
- 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.
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