Reputation: 45
I've been looking at a lot of Scala monad transformer examples and haven't been able to figure out how to do what I think is probably something straightforward. I want to write a for
comprehension that looks up something in a database (MongoDB), which returns an Option
, then if that Option
is a Some
, looks at its contents and gets another Option
, and so on. At each step, if I get a None
, I want to abort the whole thing and produce an error message like "X not found"
. The for
comprehension should yield an Either
(or something similar), in which a Left
contains the error message and a Right
contains the successful result of the whole operation (perhaps just a string, or perhaps an object constructed using several of the values obtained along the way).
So far I've just been using the Option
monad by itself, as in this trivial example:
val docContentOpt = for {
doc <- mongoCollection.findOne(MongoDBObject("_id" -> id))
content <- doc.getAs[String]("content")
} yield content
However, I'm stuck trying to integrate something like Either
into this. What I'm looking for is a working code snippet, not just a suggestion to try \/
in Scalaz. I've tried to make sense of Scalaz, but it has very little documentation, and what little there is seems to be written for people who know all about lambda calculus, which I don't.
Upvotes: 2
Views: 488
Reputation: 37435
I'd "try" something like this:
def tryOption[T](option: Option[T], message:String ="" ):Try[T] = option match {
case Some(v) => Success(v)
case None => Failure(new Exception(message))
}
val docContentOpt = for {
doc <- tryOption(mongoCollection.findOne(MongoDBObject("_id" -> id)),s"$id not found")
content <- tryOption(doc.getAs[String]("content"), "content not found")
} yield content
Basically an Option to Try conversion that captures the error in an exception. Try
is an specialized right-biased Either that is monadic (in contrast to Either, which is not)
Upvotes: 2
Reputation: 139038
Try
may be what you're looking for, but it's also possible to do this using the "right projection" of the standard library's Either
:
val docContentOpt: Either[String, String] = for {
doc <- mongoCollection.findOne(MongoDBObject("_id" -> id)).toRight(
s"$id not found"
).right
content <- doc.getAs[String]("content").toRight("Can't get as content").right
} yield content
This may make more sense if your error type doesn't extend Throwable
, for example, or if you're stuck on 2.9.2 or earlier (or if you just prefer the generality of Either
, etc.).
(As a side note, it'd be nice if the standard library provided toSuccess
and toFailure
methods on Option
that would make converting Option
into Try
as convenient as converting Option
into Either
is here—maybe someday.)
(And as another side note, Scalaz doesn't actually buy you much here—it would allow you to write .toRightDisjunction("error")
instead of .toRight("error").right
, but that's about it. As Gabriel Claramunt points out in a comment, this isn't a case for monad transformers.)
Upvotes: 2