Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

seeing error "magnolia: could not find any direct subtypes of trait" when its not true! #177

Open
cartazio opened this issue Feb 18, 2021 · 9 comments

Comments

@cartazio
Copy link

The pattern seems to be when the base sealed trait has several parameters
eg
sealed trait Foo[A]

but the child class instantiates them

case class Bar(..) extends Foo[SomeConcreteType]

i'm hoping its other stuff that spuriously confusing zio-json, because overall i'm finding it to be a very humane tool! (though i did have to roll my own Map[k,v] instance to get some stuff to work )

@fsvehla
Copy link
Contributor

fsvehla commented Feb 18, 2021

@cartazio I’m also wondering what the issue with Map[K, V] was that you ran into

@joroKr21
Copy link

What was A that you tried to derive for?

@cartazio
Copy link
Author

a colleague and I will try to boil down a working reproducer that you can run yourself.

we're currently using the 0.1 release, if we can get a reproducer into your hands would that be helpful?

@agascon9
Copy link

agascon9 commented Feb 19, 2021

import zio.json._

object Example {

  trait Bar[T] {
    def get(a: T): T
  }

  case class BarInstance() extends Bar[Int] {
    def get(a: Int): Int = a
  }

  case class FooTwoInstance[A <: Bar[Int]](
    `type`: Int
  ) extends FooTwo[A]

  case class FooInstance (
    fooTwo: FooTwo[BarInstance]
  ) extends Foo[BarInstance]

  sealed trait FooTwo[A <: Bar[Int]] {
    val `type`: Int
  }

  sealed trait Foo[A <: Bar[Int]] {
    val fooTwo: FooTwo[A]
  }

  implicit def decoder[A <: Bar[Int]](): JsonDecoder[Foo[A]] =
    DeriveJsonDecoder.gen[Foo[A]]
  implicit def encoder[A <: Bar[Int]](): JsonEncoder[Foo[A]] =
    DeriveJsonEncoder.gen[Foo[A]]
}

produces the following error:

magnolia: could not find any direct subtypes of trait Foo
    DeriveJsonDecoder.gen[Foo[A]]

And if we modify the decoder and encoder to no longer use type parameters like so:

  implicit def decoder(): JsonDecoder[Foo[BarInstance]] =
    DeriveJsonDecoder.gen[Foo[BarInstance]]
  implicit def encoder(): JsonEncoder[Foo[BarInstance]] =
    DeriveJsonEncoder.gen[Foo[BarInstance]]

we get the following error:

magnolia: could not find JsonDecoder.Typeclass for type Example.FooTwo[Example.BarInstance]
    in parameter 'fooTwo' of product type Example.FooInstance
    in coproduct type Example.Foo[Example.BarInstance]
    DeriveJsonDecoder.gen[Foo[BarInstance]]

@cartazio
Copy link
Author

cartazio commented Feb 19, 2021

thanks @agascon9 (my lovely colleague).
this gives the essence of our problem (though it happens a few different ways).

and it actually nicely shows that the remaining challenges we have in our effort to migrate a code base to the joys of zio-json are one and the same (trying to fix our errors one way, pushes us to have errors another way ).

Any further ways we can help test/facilitate/validate a fix and subsequent release, we are at your service to enable and make happen. (the code base in question is large enough that moving to zio-json will transform the dev experience/build time for all our contributors, and help us generally do nice things)

@cartazio
Copy link
Author

I guess we could hand write those instances as our near term fix, but i'm a tad leary of that :)

@joroKr21
Copy link

Thanks for the minimisation, I assume this is rather a problem with magnolia itself.
I can't recall how well it supports GADTs.

@cartazio
Copy link
Author

yeah, once i realized it was a funny scala gadt i was worried about that. At the very least, better errormessages about how to resolve it would be great. I think our fix ultimately is to just do the equivalent of DeriveJsonEncoder.gen[Foo[BarInstance]] sortah

@plokhotnyuk
Copy link
Contributor

plokhotnyuk commented Feb 19, 2021

Currently jsoniter-scala-macros doesn't derive codecs for generic types, but it works fine for non-generic types:

import com.github.plokhotnyuk.jsoniter_scala.macros._
import com.github.plokhotnyuk.jsoniter_scala.core._

object Example {
  trait Bar[T] {
    def get(a: T): T
  }

  case class BarInstance() extends Bar[Int] {
    def get(a: Int): Int = a
  }

  case class FooTwoInstance[A <: Bar[Int]](`type`: Int) extends FooTwo[A]

  case class FooInstance(fooTwo: FooTwo[BarInstance]) extends Foo[BarInstance]

  sealed trait FooTwo[A <: Bar[Int]] {
    val `type`: Int
  }

  sealed trait Foo[A <: Bar[Int]] {
    val fooTwo: FooTwo[A]
  }

  implicit val codec: JsonValueCodec[Foo[BarInstance]] =
    JsonCodecMaker.make(CodecMakerConfig.withDiscriminatorFieldName(Some("hint")))

  def main(args: Array[String]): Unit = {
    val x: Foo[BarInstance] = FooInstance(FooTwoInstance(1))
    val json = writeToString(x)
    println(json)
    println(readFromString[Foo[BarInstance]](json))
  }
}

Bellow is an output, please check if it satisfies your expectations.

{"hint":"FooInstance","fooTwo":{"hint":"FooTwoInstance","type":1}}
FooInstance(FooTwoInstance(1))

Probably, primitive routines of jsoniter-scala-macros implementation can be easily ported to magnolia.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants