Scala's Either not Having `flatMap` & Meaning of Either.left/.right - scala

Looking at Haskell's Either Monad, there's a >>= function.
Prelude Map> let add100 = \x -> Right (x+100 :: Int)
Prelude Map> x
Right 5
Prelude Map> x >>= add100
Right 105
Prelude Map> let y = Left "..." :: Either String Int
Prelude Map> y >>= add100
Left "..."
However, why doesn't Scala's Either[A,B] have a flatMap, i.e. equivalent to >>= function?
scala> e
res5: Either[String,Int] = Right(1)
scala> e.
asInstanceOf fold isInstanceOf isLeft isRight
joinLeft joinRight left right swap
toString
Also, what's the meaning of left and right?
scala> e.left
res6: scala.util.Either.LeftProjection[String,Int] = LeftProjection(Right(1))
scala> e.right
res7: scala.util.Either.RightProjection[String,Int] = RightProjection(Right(1))

Either can't have a flatMap, because Either is not a monad. Haskell makes it into a monad by favoring one of the two types over the other, but there really is no justification to do so. Either means "this can be one of two types". Period. It does not give any preferential treatment to one of the two types.
If you use Either for error reporting, then it is true that you want it to be biased to one side, but that is only one usecase of many, and hardcoding a single special usecase into a general interface smells of bad design. And for that usecase, you might just as well use Try, which is basically a biased Either.
left and right return a projection of the Either, which is biased to one side.
However, note that the design and usability of Either has been debated very often, and it is indeed not exactly a shining beacon of good API design. Which is one of the reasons why Try and ScalaZ \/ exist.

You can either use fold which requires two functions, one for left and one for right, or use the right projection view:
val e: Either[String, Int] = Right(5)
def add100(i: Int): Either[String, Int] = Right(i + 100)
e.fold(identity, add100) // Right(105)
e.right.flatMap(add100) // Right(105)
So the projection views allow you to see an Either instance as something that will be mapped through the left or right type. This is explained in the Either scala-doc help file.
If you are familiar with Haskell, the ScalaZ library might be for you, it has its own 'either' abstraction called \/ (as described here).

Scala's Either type is unbiased. With types like Try, Future, etc, map and flatMap operate on the successful state, but with Either is this is not the case (though Right is often favored for success). If Either has no true successful state, which value should flatMap operate on? Left or Right? That's left up to you with projections.
left and right are the projections of Either, which are essentially biased versions of Either.
val test: Either[String, Int] = Right(1)
test.right.map(_ + 1) // Right-biased, will map the value to Right(2)
test.left.map(_ + 1) // Left-biased, will not operate on the value since it's `Right`, therefore returning Right(1) again.

Scala’s Either class was redesigned in Scala 2.12. Prior to 2.12, Either was not biased, and didn’t implement map and flatMap methods. As the image from the book shows, Either is redesigned in 2.12 to include those methods, so it can now be used in Scala for-expressions as shown.
https://alvinalexander.com/photos/scala-either-left-right-2.12-biased-map-flatmap/
In Scala 2.12 you can you e.g. this:
for {
right1 <- either1
right2 <- either2
} yield (righ1 + right2)

Related

Why do `Left` and `Right` have two type parameters?

I understand it would be difficult to change now without breaking existing code, but I'm wondering why it was done that way in the first place.
Why not just:
sealed trait Either[+A, +B]
case class Left[A](x: A) extends Either[A, Nothing]
case class Right[B](x: B) extends Either[Nothing, B]
Is there some drawback here that I'm failing to see...?
Not sure how relevant this answer really is to Scala, but it certainly is in Haskell which is evidently where Scala's Either was borrowed from and so that's probably the best historical reason for why Scala did it this way.
Either is the canonical coproduct, i.e. for any types A and B you have
The type EitherA,B ≈ A ⊕ B
Two coprojections LeftA,B : A -> A⊕B and RightA,B : B -> A⊕B
such that for any type Y and any functions fA : A -> Y and fB : B -> Y, there exists exactly one function f : A⊕B -> Y with the property that fA = f ∘ LeftA,B and fB = f ∘ RightA,B.
To formulate this mathematically, it is quite helpful to have the information which particular Left you're working with explicit, because else the domains of the morphisms would be all unclear. In Scala this may be unnecessary because of implicit covariant conversion, but not in maths and not in Haskell.
In Haskell it isn't really an issue at all, because type inference will automatically do what's needed:
GHCi, version 8.6.5: http://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /tmp/haskell-stack-ghci/2a3bbd58/ghci-script
Prelude> let right2 = Right 2
Prelude> let left42 = Left 42.0
Prelude> (+) <$> right2 <*> left42
Left 42.0
Unlike, apparently, in Scala, Haskell just leaves the unspecified second argument of left42 as a type variable (unless the monomorphism restriction is enabled), so you can later use it in any context requiring some Either Double R for any type R. Of course it's possible to make that explicit too
right2 :: Either a Int
right2 = Right 2
left42 :: Either Double a
left42 = Left 42
main :: IO ()
main = print $ (+) <$> right2 <*> left42
which surely is possible in Scala just as well.
There's no meaningful drawback that I've found to your scheme. For the last eight years or so I've used my own variant of Either which is exactly as you describe under another name (Ok[+Y, +N] with Yes[+Y] and No[+N] as the alternatives). (Historical note: I started when Either was not right-biased, and wanted something that was; but then I kept using my version because it was more convenient to have only half the types.)
The only case I've ever found where it matters is when you pattern match out one branch and no longer have access to the type information of the other branch.
def foo[A, B: Typeclass](e: Either[A, B]) =
implicitly[Typeclass[B]].whatever()
// This works
myEither match {
case l: Left[L, R] => foo(l)
case r: Right[L, R] => foo(r)
}
def bar[N, Y: Typeclass](o: Ok[N, Y]) =
implicitly[Typeclass[Y]].whatever()
// This doesn't work
myOk match {
case y: Yes[Y] => bar(y) // This is fine
case n: No[N] => bar(n) // Y == Nothing!
}
However, I never do this. I could just use o to get the right type. So it doesn't matter! Everything else is easier (like pattern matching and changing one case and not the other...you don't need case Left(l) => Left(l) which rebuilds the Left for no reason except to switch the type of the uninhabited branch).
There are other cases (e.g. setting types in advance) that seem like they should be important, but in practice are almost impossible to make matter (e.g. because covariance will find the common supertype anyway, so what you set doesn't constrain anything).
So I think the decision was made before there was enough experience with the two ways to do it, and the wrong choice was made. (It's not a very wrong choice; Either is still decent.)

flatMap function in scala

In the below two scenarios, I have flatMap function invoked on a list. In both the cases, the map portion of the flatMap function returns an array which has a iterator. In the first case, the code errors out where in the second case, it produces the expected result.
Scenario-1
val x = List("abc","cde")
x flatMap ( e => e.toArray)
<console>:13: error: polymorphic expression cannot be instantiated to expected type;
found : [B >: Char]Array[B]
required: scala.collection.GenTraversableOnce[?]
x flatMap ( e => e.toArray)
Scenario-2
val x = List("abc,def")
x flatMap ( e => e.split(",") )
res1: List[String] = List(abc, def) //Result
Can you please help why in the first case, it is not behaving as expected ?
I think the difference is that in scenario 1 you actually have a Array[B] where B is some yet-to-be-decided supertype of Char. Normally the compiler would look for an implicit conversion to GenTraversableOnce but because B is not yet known you run into a type inference issue / limitation.
You can help type inference by filling in B.
List("abc", "cde").flatMap(_.toArray[Char])
Or even better, you don't need flatMap in this case. Just call flatten.
List("abc", "cde").flatten
It is worth keeping in mind that Array is not a proper Scala collection so compiler has to work harder to first convert it to something that fits with the rest of Scala collections
implicitly[Array[Char] <:< GenTraversableOnce[Char]] // error (Scala 2.12)
implicitly[Array[Char] <:< IterableOnce[Char]] // error (Scala 2.13)
Hence because flatMap takes a function
String => GenTraversableOnce[Char]
but we are passing in
String => Array[Char]
the compiler first has to find an appropriate implicit conversion of Array[Char] to GenTraversableOnce[Char]. Namely this should be wrapCharArray
scala.collection.immutable.List.apply[String]("abc", "cde").flatMap[Char](((e: String) => scala.Predef.wrapCharArray(scala.Predef.augmentString(e).toArray[Char]((ClassTag.Char: scala.reflect.ClassTag[Char])))))
or shorter
List("abc", "cde").flatMap(e => wrapCharArray(augmentString(e).toArray))
however due to type inference reasons explained by Jasper-M, the compiler is unable to select wrapCharArray conversion. As Daniel puts it
Array tries to be a Java Array and a Scala Collection at the same
time. It mostly succeeds, but fail at both for some corner cases.
Perhaps this is one of those corner cases. For these reasons it is best to avoid Array unless performance or compatibility reasons dictate it. Nevertheless another approach that seems to work in Scala 2.13 is to use to(Collection) method
List("abc","cde").flatMap(_.to(Array))
scala> val x = List("abc","cde")
x: List[String] = List(abc, cde)
scala> x.flatMap[Char](_.toArray)
res0: List[Char] = List(a, b, c, c, d, e)
As the error mentions, your type is wrong.
In the first case, if you appy map not flatmap, you obtain List[Array[Char]]. If you apply flatten to that, you get a List[Char]
In the second case, if you appy map not flatmap, you obtain List[Array[String]]. If you apply flatten to that, you get a List[String]
I suppose you need to transform String to Char in your Array, in order to make it work.
I use Scala 2.13 and still have the same error.

Best Practices for nested Scala Option fields?

I've got a number of nested objects, all wrapped in a Scala Option type. Elsewhere in my project I'm having to call an attribute that is embedded some 5 levels deep (some of which are Lists), each time making a call to .get. This way I end up with something that looks like the following:
objectA.get.attrB.get.attrC.get(0).attrD.get
Other than the series of .get calls (which I'm not sure is ideal), I'm not implementing much error handling this way and should any of the attributes be empty then the whole thing falls apart. Given the nested calls, if I were to limit this to a one-liner as above, I would also only be able to use .getOrElse once at the end.
Are there any suggested ways of working with Option types in Scala?
In this case, the most readable solution out of the box (that is, without writing helper methods) would argueably be to chain calls to Option.flatMap:
objectA flatMap (_.attrB) flatMap (_.attrC.headOption) flatMap (_.attrD)
By using flatMap, if any of the option in the chain is None, you'll end up with None at the end (no exception unlike with get which will blow when called on None).
By example:
case class C(attrD: Option[String])
case class B(attrC: List[C])
case class A(attrB: Option[B])
val objectA = Some(A(Some(B(List(C(Some("foo")), C(Some("bar")))))))
// returns Some(foo)
objectA flatMap (_.attrB) flatMap (_.attrC.headOption) flatMap (_.attrD)
val objectA2 = Some(A(Some(B(List()))))
// returns None
objectA2 flatMap (_.attrB) flatMap (_.attrC.headOption) flatMap (_.attrD)
You could also use for comprehensions instead of flatMap (for comprehension being desugared to chains of flatMap/map), but in this case it will actually be less readable (the opposite is usually true) because on each step you'll have to introduce a binding and then reference that on the next step:
for ( a <- objectA; b <- a.attrB; c <- b.attrC.headOption; d <- c.attrD ) yield d
Another solution, if you're willing to use ScalaZ, is to use >>= in place of flatMap, which makes it somewhat shorter:
val objectA = Option(A(Some(B(List(C(Some("foo")), C(Some("bar")))))))
// returns Some(foo)
objectA >>= (_.attrB) >>= (_.attrC.headOption) >>= (_.attrD)
This is really exactly the same as using flatMap, only shorter.
I believe you wanted this,
val deepNestedVal = objectA.get.attrB.get.attrC.get.attrD.get
I think the more preferred way would be to use for-comprehension,
val deepNestedVal = for {
val1 <- objectA
val2 <- val1.attrB
val3 <- val2.attrC
val4 <- val3.attrD
} yield val4
Other way is to use flatMap as shown in the answer by #Régis Jean-Gilles

What does the ++: operator do to a list?

Alright, Scala has me feeling pretty dense. I'm finding the docs pretty impenetrable -- and worse, you can't Google the term "Scala ++:" because Google drops the operator terms!
I was reading some code and saw this line:
Seq(file) ++: children.flatMap(walkTree(_))
But couldn't figure it out. The docs for Seq show three things:
++
++:
++:
Where the latter two are over loaded to do.. something. The actual explanation in the doc says that they do the same thing as ++. Namely, add one list to another.
So, what exactly is the difference between the operators..?
++ and ++: return different results when the operands are different types of collection. ++ returns the same collection type as the left side, and ++: returns the same collection type as the right side:
scala> List(5) ++ Vector(5)
res2: List[Int] = List(5, 5)
scala> List(5) ++: Vector(5)
res3: scala.collection.immutable.Vector[Int] = Vector(5, 5)
There are two overloaded versions of ++: solely for implementation reasons. ++: needs to be able to take any TraversableOnce, but an overloaded version is provided for Traversable (a subtype of TraversableOnce) for efficiency.
Just to make sure:
A colon (:) in the end of a method name makes the call upside-down.
Let's make two methods and see what's gonna happen:
object Test {
def ~(i: Int) = null
def ~:(i: Int) = null //putting ":" in the tail!
this ~ 1 //compiled
1 ~: this //compiled
this.~(1) //compiled
this.~:(1) //compiled.. lol
this ~: 1 //error
1 ~ this //error
}
So, in seq1 ++: seq2, ++: is actually the seq2's method.
edited: As #okiharaherbst mentions, this is called as right associativity.
Scala function naming will look cryptic unless you learn a few simple rules and their precedence.
In this case, a colon means that the function has right associativity as opposed to the more usual left associativity that you see in imperative languages.
So ++: as in List(10) ++: Vector(10) is not an operator on the list but a function called on the vector even if it appears on its left hand-side, i.e., it is the same as Vector(10).++:(List(10)) and returns a vector.
++ as in List(10) ++ Vector(10) is now function called on the list (left associativity), i.e., it is the same as List(10).++(Vector(10)) and returns a list.
what exactly is the difference between the operators..?
The kind of list a Seq.++ operates on.
def ++[B](that: GenTraversableOnce[B]): Seq[B]
def ++:[B >: A, That](that: Traversable[B])(implicit bf: CanBuildFrom[Seq[A], B, That]): That
def ++:[B](that: TraversableOnce[B]): Seq[B]
As commented in "What is the basic collection type in Scala?"
Traversable extends TraversableOnce (which unites Iterator and Traversable), and
TraversableOnce extends GenTraversableOnce (which units the sequential collections and the parallel.)

Scala Vector fold syntax (/: and :\ and /:\)

Can someone provide some examples for how
/: :\ and /:\
Actually get used? I assume they're shortcuts to the reduce / fold methods, but there's no examples on how they actually get used in the Scala docs, and they're impossible to google / search for on StackOverflow.
I personally prefer the /: and :\ forms of foldLeft and foldRight. Two reasons:
It has a more natural feel because you can see that you are pushing a value into the left/right of a collection and applying a function. That is
(1 /: ints) { _ + _ }
ints.foldLeft(1) { _ + _ }
Are both equivalent, but I tend to think the former emphasises my intuition as to what is happening. If you want to know how this is happening (i.e. the method appears to be called on the value 1, not the collection), it's because methods ending in a colon are right-associative. This can be seen in ::, +: etc etc elsewhere in the standard library.
The ordering of the Function2 parameters is the same order as the folded element and that which is folded into:
(b /: as) { (bb, a) => f(bb, a) }
// ^ ^ ^ ^
// ^ ^ ^ ^
// B A B A
Better in every way than:
as.foldLeft(b) { (bb, a) => f(bb, a) }
Although I admit that this was a far more important difference in the era before decent IDE support: nowadays IDEA can tell me what function is expected with a simple CTRL-P
I hope it should also be obvious how :\ works with foldRight - it's basically exactly the same, except that the value appears to be being pushed in from the right hand side. I must say, I tend to steer well clear of foldRight in scala because of how it is implemented (i.e. wrongly).
/: is a synonym for foldLeft and :\ for foldRight.
But remember that : makes /: apply to the object to the right of it.
Assuming you know that (_ * _) is an anonymous function that's equivalent to (a, b) => a * b, and the signature of foldLeft and foldRight are
def foldLeft [B] (z: B)(f: (B, A) ⇒ B): B
def foldRight [B] (z: B)(f: (A, B) ⇒ B): B
i.e. they're curried functions taking a start value and a function combining the start value / accumulator with an item from the list, some examples are:
List(1,2,3).foldLeft(1)(_*_)
which is the same as
(1 /: List(1,2,3))(_*_)
And
List(1,2,3).foldRight(1)(_*_)
in infix notation is
(List(1,2,3) foldRight 1)(_*_)
which is the same as
(List(1,2,3) :\ 1)(_*_)
Add your own collections and functions and enjoy!
The thing to remember with the short (/: and :\) notations is that, because you're using the infix notations you need to put parentheses around the first part in order for it to pick up the second argument list properly. Also, remember that the functions for foldLeft and foldRight are the opposite way round, but it makes sense if you're visualising the fold in your head.
Rex Kerr has written nice answer about folds here. Near the end you can see an example of shortcut syntax of foldLeft and foldRight.