argonaut-shapeless icon indicating copy to clipboard operation
argonaut-shapeless copied to clipboard

Example in readme failing

Open flyrev opened this issue 9 years ago • 4 comments

import argonaut.Shapeless._
import argonaut.{DecodeJson, DecodeResult, EncodeJson, _}

object ArgonautPlaying extends App {
   case class CC(i: Int, s: String)

   // encoding
   val encode = EncodeJson.of[CC]

   val json = encode(CC(2, "a"))
   json.nospaces == """{"i":2,"s":"a"}"""

   // decoding
   val decode = DecodeJson.of[CC]

   val result = decode.decodeJson(json)
   result == DecodeResult.ok(CC(2, "a"))
}
<dependencies>
    <dependency>
        <groupId>org.scala-lang</groupId>
        <artifactId>scala-library</artifactId>
        <version>2.11.8</version>
    </dependency>

    <dependency>
        <groupId>io.argonaut</groupId>
        <artifactId>argonaut_2.11</artifactId>
        <version>6.2-M1</version>
    </dependency>

    <dependency>
        <groupId>com.github.alexarchambault</groupId>
        <artifactId>argonaut-shapeless_6.1_2.11</artifactId>
        <version>1.1.0-RC1</version>
    </dependency>
</dependencies>

Errors:

Error:(15, 29) could not find implicit value for evidence parameter of type argonaut.DecodeJson[ArgonautPlaying.CC] val decode = DecodeJson.of[CC] ^ Error:(15, 29) not enough arguments for method of: (implicit evidence$1: argonaut.DecodeJson[ArgonautPlaying.CC])argonaut.DecodeJson[ArgonautPlaying.CC]. Unspecified value parameter evidence$1. val decode = DecodeJson.of[CC] ^

flyrev avatar Apr 21 '16 12:04 flyrev

argonaut-shapeless only works with argonaut 6.1 for now. I'm running into problems with the covariance of DecodeJson when trying to port it to argonaut 6.2 for now.

alexarchambault avatar Apr 21 '16 12:04 alexarchambault

I have observed same issue. In attempt to trace the problem I created a new project with just this library as a dependency.

My build.sbt:

name := "Argonaut Shapeless Test"

version := "0.1"

scalaVersion := "2.11.8"

libraryDependencies ++= Seq(
  "com.github.alexarchambault" %% "argonaut-shapeless_6.1" % "1.1.0-RC2"
)

The test class:

import argonaut._, Argonaut._, Shapeless._

object Test {
  sealed trait Base
  case class First(i: Int) extends Base
  case class Second(s: String) extends Base

  // encoding
  val encode = EncodeJson.of[Base]

  val json = encode(First(2))
  json.nospaces == """{"First":{"i":2}}"""

  // decoding
  val decode = DecodeJson.of[Base]

  val result = decode.decodeJson(json)
  result == DecodeResult.ok(First(2))
}

Errors on compiling:

[info] Set current project to Argonaut Shapeless Test (in build file:/private/tmp/test/)
[info] Compiling 1 Scala source to /private/tmp/test/target/scala-2.11/classes...
[error] /private/tmp/test/src/main/scala/Test.scala:9: could not find implicit value for evidence parameter of type argonaut.EncodeJson[Test.this.Base]
[error]   val encode = EncodeJson.of[Base]
[error]                             ^
[error] /private/tmp/test/src/main/scala/Test.scala:15: could not find implicit value for evidence parameter of type argonaut.DecodeJson[Test.this.Base]
[error]   val decode = DecodeJson.of[Base]
[error]                             ^
[error] two errors found
[error] (compile:compileIncremental) Compilation failed

However it seems odd that pasting the following into the console works:

import argonaut._, Argonaut._, Shapeless._

sealed trait Base
case class First(i: Int) extends Base
case class Second(s: String) extends Base

// encoding
val encode = EncodeJson.of[Base]
val json = encode(First(2))
json.nospaces == """{"First":{"i":2}}"""

// decoding
 val decode = DecodeJson.of[Base]

val result = decode.decodeJson(json)
result == DecodeResult.ok(First(2))

Changing where the model classes are declared to outside of the test class works:

import argonaut._, Argonaut._, Shapeless._

sealed trait Base
case class First(i: Int) extends Base
case class Second(s: String) extends Base

object Test {

  // encoding
  val encode = EncodeJson.of[Base]

  val json = encode(First(2))
  json.nospaces == """{"First":{"i":2}}"""

  // decoding
  val decode = DecodeJson.of[Base]

  val result = decode.decodeJson(json)
  result == DecodeResult.ok(First(2))
}

But this doesn't:

import argonaut._, Argonaut._, Shapeless._

object Model {
  sealed trait Base
  case class First(i: Int) extends Base
  case class Second(s: String) extends Base
}

object Test {
  // encoding
  val encode = EncodeJson.of[Base]

  val json = encode(First(2))
  json.nospaces == """{"First":{"i":2}}"""

  // decoding
  val decode = DecodeJson.of[Base]

  val result = decode.decodeJson(json)
  result == DecodeResult.ok(First(2))
}

This is all because of the way macros work and compile order (Shapless uses macros to fill in the implicits here). It works in the REPL because everything is ordered and compiled on the fly, however when using scalac things can get compiled in different orders, and break the macro's compile phase as the classes that it is looking for aren't there.

I would recommend that the best way around this is to have the model classes in a separate module to the JSON parsing, that way the model classes will always be built first and the code will compile.

Would it be worth an update to the readme to describe this behaviour (I'm happy to raise a PR)?

janstenpickle avatar Jun 06 '16 18:06 janstenpickle

@janstenpickle If you want to add a word about this issue (this is the classic SI-7046, aka SI-7755), go for it! Putting the models and the codec derivations in different projects is indeed the most reliable way of addressing that afaik.

alexarchambault avatar Jun 14 '16 21:06 alexarchambault

If you change the README, change the one under doc/, then run sbt tut. The latter will update the README at the root of the project. You can then commit both READMEs.

alexarchambault avatar Jun 14 '16 21:06 alexarchambault