As I understand it, Scala "for" syntax is extremely similar to Haskell's monadic "do" syntax. In Scala, "for" syntax is often used for Lists and Options. I'd like to use it with Eithers, but the necessary methods are not present in the default imports.
for {
foo <- Right(1)
bar <- Left("nope")
} yield (foo + bar)
// expected result: Left("nope")
// instead I get "error: value flatMap is not a member..."
Is this functionality available through some import?
There is a slight hitch:
for {
foo <- Right(1)
if foo > 3
} yield foo
// expected result: Left(???)
For a List, it would be List(). For Option, it would be None. Do the Scala standard libraries provide a solution to this? (Or perhaps scalaz?) How? Suppose I wanted to provide my own "monad instance" for Either, how could I do that?
It doesn't work in scala 2.11 and earlier because Either is not a monad. Though there's talk of right-biasing it, you can't use it in a for-comprehension: you have to get a LeftProject or RightProjection, like below:
for {
foo <- Right[String,Int](1).right
bar <- Left[String,Int]("nope").right
} yield (foo + bar)
That returns Left("nope"), by the way.
On Scalaz, you'd replace Either with Validation. Fun fact: Either's original author is Tony Morris, one of Scalaz authors. He wanted to make Either right-biased, but was convinced otherwise by a colleague.
Is this functionality available through some import?
Yes, but via a third party import: Scalaz provides a Monad instance for Either.
import scalaz._, Scalaz._
scala> for {
| foo <- 1.right[String]
| bar <- "nope".left[Int]
| } yield (foo.toString + bar)
res39: Either[String,java.lang.String] = Left(nope)
Now if-guard is not a monadic operation. Therefore if you attempt to use if-guard, it results in a compiler error as expected.
scala> for {
| foo <- 1.right[String]
| if foo > 3
| } yield foo
<console>:18: error: value withFilter is not a member of Either[String,Int]
foo <- 1.right[String]
^
The convenience methods used above - .right and .left - are also from Scalaz.
Edit:
I missed this question of yours.
Suppose I wanted to provide my own "monad instance" for Either, how could I do that?
Scala for comprehensions are simply translated to .map, .flatMap, .withFilter, and .filter .foreach calls on the objects involved. (You can find the the full translation scheme here.) So if some class does not have the required methods, you can add them to a class using implicit conversions.
A fresh REPL session below.
scala> implicit def eitherW[A, B](e: Either[A, B]) = new {
| def map[B1](f: B => B1) = e.right map f
| def flatMap[B1](f: B => Either[A, B1]) = e.right flatMap f
| }
eitherW: [A, B](e: Either[A,B])java.lang.Object{def map[B1](f: B => B1): Product
with Either[A,B1] with Serializable; def flatMap[B1](f: B => Either[A,B1]):
Either[A,B1]}
scala> for {
| foo <- Right(1): Either[String, Int]
| bar <- Left("nope") : Either[String, Int]
| } yield (foo.toString + bar)
res0: Either[String,java.lang.String] = Left(nope)
As of Scala 2.12, Either is now right biased
From the documentation:
As Either defines the methods map and flatMap, it can also be used in for comprehensions:
val right1: Right[Double, Int] = Right(1)
val right2 = Right(2)
val right3 = Right(3)
val left23: Left[Double, Int] = Left(23.0)
val left42 = Left(42.0)
for (
a <- right1;
b <- right2;
c <- right3
) yield a + b + c // Right(6)
for (
a <- right1;
b <- right2;
c <- left23
) yield a + b + c // Left(23.0)
for (
a <- right1;
b <- left23;
c <- right2
) yield a + b + c // Left(23.0)
// It is advisable to provide the type of the “missing” value (especially the right value for `Left`)
// as otherwise that type might be infered as `Nothing` without context:
for (
a <- left23;
b <- right1;
c <- left42 // type at this position: Either[Double, Nothing]
) yield a + b + c
// ^
// error: ambiguous reference to overloaded definition,
// both method + in class Int of type (x: Char)Int
// and method + in class Int of type (x: Byte)Int
// match argument types (Nothing)
Related
I am not very versed in Scala and sincerily I found the documentation hard to try to figure this out, but I was wondering if someone could explain to me why the difference in compilation of the following statements:
I can easily iterate over a set of strings and yield the elements.
scala> for(name <- Set("asd", "123")) yield name
val res2: scala.collection.immutable.Set[String] = Set(asd, 123)
But i can't do it inline if the Set is inside an Option
scala> for(names <- Some(Set("asd", "123")); name <- names) yield (name)
^
error: type mismatch;
found : scala.collection.immutable.Set[String]
required: Option[?]
It happens because of for-yield is just syntactic sugar for flatMap, map and withFilter functions. So, your code:
for(names <- Some(Set("asd", "123")); name <- names) yield (name)
actually is the same as:
Some(Set("asd", "123")).flatMap{ names: Set[String] =>
names.flatMap{ name: String =>
name // return type is String, but Set[(of some )B] is expected
}
}// return type is Set[String] but Option[(of some) C] is expected
look at the Option flatMap function:
#inline final def flatMap[B](f: A => Option[B]): Option[B]
but your f returns Set[String]
as a result, compiler tell you about type missmatch (Set[String] != Option[?]):
error: type mismatch;
found : scala.collection.immutable.Set[String]
required: Option[?]
you should remember about type of the first statement in for-yield construction. In your case it's names <- Some(Set("asd", "123")). It has type Option[Set[String]], so you should use only Option[T] in x <- yourNextStatement lines (x should has Option[T] type).
In conclusion:
Be careful with mixing different container types in for-yield constructions. If you have some problems, just try to unwrap your for-yield into combination of flatMap, map, withFilter functions.
If you want to mix containers in for-yeild, you should start another for-yield for each-container type. For example:
for {
names <- Some(Set("asd", "123"))
// will return Option[String]
reducedNames <- (for {
name <- names // here will be used Seq flatMap function, not Option
} yield (name + "some_suffix"))
.reduceLeftOption(_ + _) // here we will get Option[String] from Seq[String]
} yield reducedNames // will return Option[String]
What do you want to happen for None? Assuming "nothing", you could do
val optionalSet = Some(Set("asd", "123"))
for (names <- optionalSet.getOrElse(Set.empty); name <- names) yield name
I am trying to do several dependent Slick/DB calls and then display the resulting data within a twirl template.
def show(slug: String) = Action.async { implicit rs =>
for {
f <- fooDAO.findBySlug(slug) // f is of type Option[foo]
fid <- f.flatMap(a => a.id.map(b => b)) // fid is of type Long
b <- barDAO.findByFooId(fid) // b is of type Seq[bar]
} yield {
f.map {
case Some(f) => Ok(views.html.foobar(f, b))
case _ => NotFound
}
}
}
I first need to get the "ID" to then be able to query other relevant data. The compiler is now producing this error:
play.sbt.PlayExceptions$CompilationException: Compilation error[type mismatch;
found : scala.concurrent.Future[Option[play.api.mvc.Result]]
required: Option[?]]
Any help would be greatly appreciated.
There is a fundamental flaw in your code, in that you're mixing in the same comprehension an Option and a Seq
A for-comprehension is expected to work on the same "container" type, which will be the resulting representation of the yield
e.g. if you combine several Options you get an Option, if you combine Seqs you get a Seq.
In this case you can overcome the problem by converting the Option (foo) to a Seq (which will be empty if the foo is None and have 1 element if not).
The end result would be
val results: Seq[(Foo, Bar)] =
for {
f <- fooDAO.findBySlug(slug).toSeq // f is of type Seq[Foo]
b <- barDAO.findByFooId(f.id) // b is of type Seq[Bar]
} yield (f, b)
But I guess this is not what you need. I suppose you want to get all Bars associated with the retrieved Foo, if any, and present it with your template. If no Foo is present for the slug, you want a NotFound.
We can do it like this
def show(slug: String) = Action.async { implicit rs =>
val f = fooDAO.findBySlug(slug) // f is of type Option[Foo]
f.fold(
NotFound,
foo => Ok(views.html.foobar(foo, barDAO.findByFooId(foo.id))
)
}
You can make it more explicit by defining a supporting method
def show(slug: String) = Action.async { implicit rs =>
def barsOf(f: Foo): Seq[Bar] = barDAO.findByFooId(f.id)
val f = fooDAO.findBySlug(slug) // f is of type Option[Foo]
f.fold(
NotFound,
foo => Ok(views.html.foobar(foo, barsOf(foo))
)
}
It's a bit tricky understanding what you're trying to achieve here, but if the whole thing is predicated on the findbySlug returning a Future[Option[Foo]] and the eventual outcome being a NotFound if that Option is a None, then your yield should probably just be:
...
} yield {
f.fold(NotFound)(foo => Ok(views.html.foobar(foo, b)))
}
Option[T] is a fantastic type for data-retrieval and control-flow, but pattern-matching on it is almost never the right approach. The use of fold feels nicely succinct for the task.
Given this spinet of code in Scala:
val mapMerge : (Map[VertexId, Factor], Map[VertexId, Factor]) => Map[VertexId, Factor] = (d1, d2) => d1 ++ d2
That can be shortened to:
val mapMerge : (Map[VertexId, Factor], Map[VertexId, Factor]) => Map[VertexId, Factor] = _ ++ _
What actually the code does is renaming the operator ++ of Map[VertexId, Factor] and therefore: Is there a way to assign that operator to the variable? Like in this imaginary example:
val mapMerge : (Map[VertexId, Factor], Map[VertexId, Factor]) => Map[VertexId, Factor] = Map.++
And probably with type inference it would enough to write
val mapMerge = Map[VertexId,Factor].++
Thanks
Unfortunately, no, because the "operators" in Scala are instance methods — not functions from a typeclass, like in Haskell.
Whey you write _ ++ _, you are creating a new 2-argument function(lambda) with unnamed parameters. This is equivalent to (a, b) => a ++ b, which is in turn equivalent to (a, b) => a.++(b), but not to (a, b) => SomeClass.++(a, b).
You can emulate typeclasses by using implicit arguments (see "typeclasses in scala" presentation)
You can pass "operators" like functions — which are not really operators. And you can have operators which look the same. See this example:
object Main {
trait Concat[A] { def ++ (x: A, y: A): A }
implicit object IntConcat extends Concat[Int] {
override def ++ (x: Int, y: Int): Int = (x.toString + y.toString).toInt
}
implicit class ConcatOperators[A: Concat](x: A) {
def ++ (y: A) = implicitly[Concat[A]].++(x, y)
}
def main(args: Array[String]): Unit = {
val a = 1234
val b = 765
val c = a ++ b // Instance method from ConcatOperators — can be used with infix notation like other built-in "operators"
println(c)
val d = highOrderTest(a, b)(IntConcat.++) // 2-argument method from the typeclass instance
println(d)
// both calls to println print "1234765"
}
def highOrderTest[A](x: A, y: A)(fun: (A, A) => A) = fun(x, y)
}
Here we define Concat typeclass and create an implementation for Int and we use operator-like name for the method in typeclass.
Because you can implement a typeclass for any type, you can use such trick with any type — but that would require writing quite some supporting code, and sometimes it is not worth the result.
Using Scala 2.9.1, consider the following two instances of Either:
scala> val er: Either[String, Int] = Right(1)
er: Either[String,Int] = Right(1)
scala> val el: Either[String, Int] = Left("a")
el: Either[String,Int] = Left(a)
It's great that, by using the left and right projections, one can use for-comprehensions (via biased monad of the projected Either):
scala> for { r <- er.right } yield r * 2
res6: Product with Either[String,Int] with Serializable = Right(2)
scala> for { r <- el.right } yield r * 2
res7: Product with Either[String,Int] with Serializable = Left(a)
Can someone explain to me why the decision was made not to have the filter method return an Either? I would have expected the following to work:
scala> for { r <- er.right if r > 2 } yield r * 2 // r is NOT greater than 2!
res8: Product with Either[String,Int] with Serializable = Left(a)
Instead you get the following error:
:9: error: value * is not a member of Either[Nothing,Int]
for { r <- er.right if r > 2 } yield r * 2
It appears that the underlying call in Either.RightProjection#filter actually returns an Option:
scala> er.right.filter(_ > 2)
res9: Option[Either[Nothing,Int]] = None
This defeats the use of the if clause in the for-comprehension, at least the way I was trying to use it.
Does anyone have an explanation of why this design is the way it is?
It boils down to the fact that if you have a Right(b), but your filter predicate fails, you have no value to put in a Left.
You might imagine an implementation that works for your case of Either[String, Int], by failing with a default value of Left(""). The Scala standard library doesn't have the facility to to produce a value for you, because it doesn't include a concept such as a monoid which would determine the "empty" value for a type.
The Scalaz library does include a monoid typeclass, and version 7 also includes a right-biased disjunction type, \/[A, B] (isomorphic to Either[A, B]) which has a filter method iff the left type is a monoid:
scala> \/.right[String, Int](1).filter(_ > 2)
res1: scalaz.\/[String,Int] = -\/()
But you couldn't do this for the general case - if you had an Either[Nothing, Int], you could never produce a left value.
I am experimenting with scalaz. I tried writing code in applicative code. I wrote code like this:
val max: Option[Int] = (a |#| b) { math.max(_, _) }
I didn't like this code very much. I would like to code which is closer to Haskell style, something like this:
val max: Option[Int] = { math.max(_, _) } <$> a <*> b
Is this possible. And why scalaz didn't implement it this way?
Scala's type inference, is much more limited than in Haskell (identifier overloading, which comes with the JVM being one of the reasons). Inferences flows from left to right, and the type of the arguments of a function may be deduced from previous context (if at the place of the definition, a function with arg type A is expected), but not from how they are used in the definition. Scalaz syntax makes the arguments types available. Reversing it would most of the time force you to write the function arguments types, e.g.
{math.max(_: Int, _: Int) } <$> a <*> b
You can translate the Haskell version directly into Scala, if you're willing to be a little more verbose:
import scalaz._, Scalaz._
val a = Option(1)
val b = Option(2)
val f: Int => Int => Int = x => math.max(x, _)
val c = b <*> (a map f)
Or, as a one-liner:
val c = 2.some <*> 1.some.map(x => math.max(x, _: Int))
Or:
val c = 2.some <*> (1.some map (math.max _).curried)
The order is reversed because these are method calls instead of infix operators, but it's essentially the same thing as max <$> a <*> b: we map the function over the first item, and then apply the result to the second.