Question

I have two functions that are similar. One returns a String when successful and the second returns an Option[String] when successful. How can write this in a more elegant way, perhaps calling the first function in the second function? Thanks!

  private def readString(j: JsValue, key: String): Validation[String, String] = {
    j \ key match {
      case j: JsString =>
        Success(j.value)
      case j: JsUndefined =>
        Failure(s"Missing field $key")
      case j: JsValue =>
        Failure(s"Field $key value is not a string")
    }
  }

  private def readStringOpt(j: JsValue, key: String): Validation[String, Option[String]] = {
    (j \ key).as[JsValue] match {
      case j: JsString =>
        j.value.some.success
      case j: JsUndefined =>
        None.success
      case x =>
        Failure(s"Field $key value is not a string")
    }
  }
Was it helpful?

Solution

First, the .as[JsValue] in your second method isn't really necessary—all it does is look up the Reads instance for JsValue, which just passes through its argument and never fails.

If you want to go the route you suggest above (defining the Opt-less version in terms of the other), Scalaz provides some slightly more concise syntax:

def readString(j: JsValue, key: String): Validation[String, String] =
  readStringOpt(j, key).flatMap(_.toSuccess(s"Missing field $key"))

This will give you a deprecation warning in recent versions of Scalaz, however, since Validation does not have a monad instance and its flatMap is kind of a lie. This means you have two options (apart from ignoring the deprecation warning): you can switch to \/, which is monadic, or you can use a fold instead:

def readString(j: JsValue, key: String): Validation[String, String] =
  readStringOpt(j, key).fold(_.failure, _.toSuccess(s"Missing field $key"))

Which is a little more verbose, but it puts you on the right side of the validation-is-not-a-monad gods.

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