Search code examples
scalascalazargonaut

DecodeJson not working after scalaz version update in application


I tried to upgrade the scalaz version to 7.2.18. In prior version following block of code was working quite well.

  implicit val decode: DecodeJson[Uuid] =
    DecodeJson( cursor =>
      cursor.as[String].flatMap( str =>
        DecodeResult(
            \/.fromTryCatchThrowable[Uuid,IllegalArgumentException](from(str))
              .leftMap(exc => (exc.getMessage, cursor.history))
        ) ) )

but I upgraded the version, the DecodeResult(...) block gave error:

Type Mismatch, 
    expected: Either((String, CursorHistory), NotInferredA)
    actual  : \/((String, CursorHistory),Uuid)

I would appreciate if anyone could let me know why that error occurred and the proper implementation for above block.


Solution

  • I suspect that you use Argonaut library for JSON and that your DecodeJson and DecodeResult come from there. It is hard to guess how exactly it worked before as you don't specify from what versions of those libraries you upgraded and what other dependencies you had (i.e. when code worked).

    Currently the issue comes from the fact that DecodeResult expects scala.util.Either i.e. Either from the standard Scala library while what you give is scalaz.\/ which is feature-rich equivalent of the Either from the Scalaz library. Also those types are isomorphic (of the same shape) and can be easily transformed to one another, as far as the compiler knows scala.util.Either and scalaz.\/ are two unrelated classes. Probably the easiest way to fix it is to use \/.toEither method to convert the value:

    implicit val decode: DecodeJson[Uuid] =
      DecodeJson(cursor =>
        cursor.as[String].flatMap(str =>
          DecodeResult(
            \/.fromTryCatchThrowable[Uuid, IllegalArgumentException](Uuid.from(str))
              .leftMap(exc => (exc.getMessage, cursor.history)).toEither
          )))
    

    Alternatively you may try to find what dependency earlier brought some automatic conversion from \/ to Either. Or you can write it yourself:

    object ScalaZEitherHelper {
      implicit def scalaZToStd[A, B](scalazValue: A \/ B): Either[A, B] = scalazValue.toEither
    }
    

    Then your original code compiles as far as you do import ScalaZEitherHelper._