Say I have a class with multiple parameters:
case class Foo(a: String, b: String, c: String, d: String, ... )
Now say I want to parse an instance of Foo
from a Map[String, String]
:
// returns either the parsed Foo as Right, or error message as Left
// example: Map("a" -> "1", "b" -> "2" ... ) returns Foo("1", "2", ... )
def parse(fromMap: Map[String, String]): Either[String, Foo]
The names of the keys are irrelevant; this is not a reflection type of question.
My implementation looks something like this:
fromMap.get("a") match {
case None => Left("couldn't find a")
case Some(a) => fromMap.get("b") match {
...
case Some(last) => Right(Foo(a, ... , last))
}
}
But when there are many parameters, the indentation is just too extreme, and that makes it hard to understand the logic of the method (it may be a bit more complex than just a map lookup).
Can you think of a more readable code for such a use-case?
1 Answer 1
You could create your custom method for extracting Map
values as an Either
instance and use them in a for comprehension:
implicit final class MapOps[A, B](self: Map[A, B]) {
def getAsEither(key: A): Either[String, B] = {
self.get(key) match {
case Some(value) => Right(value)
case _ => Left(s"Key not found: $key")
}
}
}
case class Foo(a: String, b: String, c: String, d: String)
def parse(input: Map[String, String]): Either[String, Foo] = {
for {
a <- input.getAsEither("a").right
b <- input.getAsEither("b").right
c <- input.getAsEither("c").right
d <- input.getAsEither("d").right
} yield Foo(a, b, c, d)
}
This could be shortened a little (all those .right
invocations could be removed) but would require using right-biased implementations of the Either
(e.g. Scala'z \/
or Cats' Xor
)
-
I pretty much dislike
implicit
and I don't see the need for the implicitMap
here; passing it as an argument togetAsEither
still persists the readable indentation. Other than that, the for-comprehension is a neat solution, thanks!Eyal Roth– Eyal Roth2016年05月25日 09:37:39 +00:00Commented May 25, 2016 at 9:37