Question

In Scala, I am thinking of a simple monad Result that contains either a Good value, or alternatively an Error message. Here is my implementation.

I'd like to ask: Did I do something in an excessively complicated manner? Or mistakes even?

Could this be simplified (but maintaining readability, so no Perl golf)? For example, do I need to use the abstract class and the companion object, or could it be simpler to put everything in a normal class?

abstract class Result[+T] {
  def flatMap[U](f: T => Result[U]): Result[U] = this match {
    case Good(x) => f(x)
    case e: Error => e
  }
  def map[U](f: T => U): Result[U] = flatMap { (x: T) => Result(f(x)) }
}
case class Good[T](x: T) extends Result[T]
case class Error(e: String) extends Result[Nothing]
object Result { def apply[T](x: T): Result[T] = Good(x) }

Now if I, for example

val x = Good(5)
def f1(v: Int): Result[Int] = Good(v + 1)
def fE(v: Int): Result[Int] = Error("foo")

then I can chain in the usual manner:

x flatMap f1 flatMap f1    // => Good(7)
x flatMap fE flatMap f1    // => Error(foo)

And the for-comprehension:

for (
  a <- x;
  b <- f1(a);
  c <- f1(b)
) yield c    // => Good(7)

P.S: I am aware of the \/ monad in Scalaz, but this is for simple cases when installing and importing Scalaz feels a bit heavy.

Was it helpful?

Solution

Looks good to me. I would change the abstract class into a sealed trait. And I think you could leave off the return types for flatMap and map without losing any readability.

I like the companion object because it calls out your unit function for what it is.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top