Content-Length: 298705 | pFad | https://github.com/circe/circe/issues/1980

BD Automatic derivation for recursive types is broken in Scala 3 · Issue #1980 · circe/circe · GitHub
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

Automatic derivation for recursive types is broken in Scala 3 #1980

Open
luksow opened this issue Jun 28, 2022 · 8 comments
Open

Automatic derivation for recursive types is broken in Scala 3 #1980

luksow opened this issue Jun 28, 2022 · 8 comments

Comments

@luksow
Copy link

luksow commented Jun 28, 2022

Hi,

#!/usr/bin/env scala-cli
using scala "3.1.2"

using lib "io.circe:circe-core_3:0.14.1"
using lib "io.circe:circe-parser_3:0.14.1"
using lib "io.circe:circe-generic_3:0.14.1"

import io.circe._
import io.circe.generic.auto._

case class R1(int: Int, r: Seq[R1])
case class R2(int: Int, r: Option[R2])

summon[Encoder[R1]]
summon[Encoder[R2]]

Both summons fail but with slightly different messages:

[error] ./circe.sc:14:20: cannot reduce summonFrom with
[error]  patterns :  case given encodeA @ _:io.circe.Encoder[Seq[circe.R1]]
[error]              case given evidence$1 @ _:deriving.Mirror.Of[Seq[circe.R1]]
[error] summon[Encoder[R1]]
[error]

and

[error] ./circe.sc:15:20: method derived is declared as `inline`, but was not inlined
[error] 
[error] Try increasing `-Xmax-inlines` above 32
[error] summon[Encoder[R2]]
[error]                    ^

Probably related to scala/scala3#8183

@armanbilge
Copy link
Contributor

Thanks for the report. Note that the following works.

//> using scala "3.1.3"

//> using lib "io.circe::circe-core::0.14.2"
//> using lib "io.circe::circe-parser::0.14.2"

import io.circe._

case class R1(int: Int, r: Seq[R1]) derives Encoder.AsObject
case class R2(int: Int, r: Option[R2]) derives Encoder.AsObject

@main def main =
  summon[Encoder[R1]]
  summon[Encoder[R2]]

@luksow
Copy link
Author

luksow commented Jun 28, 2022

@armanbilge Yes, thanks for mentoning this workaround and sorry I forgot to 😃

I'm also looking for a different workaround as I'm not a big fan of mixing my precious data classes with 3rd party libraries - for the sake of domain purity.

@armanbilge
Copy link
Contributor

What is the "3rd party library" in this situation?

@armanbilge
Copy link
Contributor

Is this what you want?

//> using scala "3.1.3"

//> using lib "io.circe::circe-core::0.14.2"
//> using lib "io.circe::circe-parser::0.14.2"

import io.circe._

case class R1(int: Int, r: Seq[R1])
case class R2(int: Int, r: Option[R2])

given Encoder.AsObject[R1] = Encoder.AsObject.derived
given Encoder.AsObject[R2] = Encoder.AsObject.derived

@main def main =
  summon[Encoder[R1]]
  summon[Encoder[R2]]

@luksow
Copy link
Author

luksow commented Jun 28, 2022

Oh yes, this looks much better, thanks!!!

3rd party library in that case is circe. if I have a module that consists of only data classes, I prefer to have no dependencies and keep serialization/deserialization where it belongs (ex. router).

@djx314
Copy link
Contributor

djx314 commented Jul 8, 2022

And it seems that when a lib can use derives AObject, it can also use

given Encoder.AsObject[R1] = AObject.derived

It is not the privilege of circe.

@luksow
Copy link
Author

luksow commented Dec 7, 2022

New findings, it's probably some "by-name" calls missing, because this doesn't work:

using scala "3.2.1"

import scala.deriving.*
import scala.compiletime.{erasedValue, summonInline}

trait Show[A]:
  def show(a: A): String

given Show[Int] with {
  def show(a: Int): String = a.toString
}

inline def summonAll[T <: Tuple]: List[Show[_]] =
  inline erasedValue[T] match
    case _: EmptyTuple => Nil
    case _: (t *: ts) => summonInline[Show[t]] :: summonAll[ts]

inline given Option[A](using inline s: Show[A]): Show[Option[A]] = new Show[Option[A]] {
  override def show(a: Option[A]): String = a.map(s.show).getOrElse("")
}



inline given[A] (using m: Mirror.Of[A]): Show[A] = new Show[A] {
  override def show(a: A): String = {
    val elemInstances = summonAll[m.MirroredElemTypes]
    elemInstances.mkString(", ")
  }
}

case class Rec(int: Int, rs: Option[Rec])

object Main extends App {
  summon[Show[Rec]]
}

but this is ok:

using scala "3.2.1"

import scala.deriving.*
import scala.compiletime.{erasedValue, summonInline}

trait Show[A]:
  def show(a: A): String

given Show[Int] with {
  def show(a: Int): String = a.toString
}

inline def summonAll[T <: Tuple]: List[Show[_]] =
  inline erasedValue[T] match
    case _: EmptyTuple => Nil
    case _: (t *: ts) => summonInline[Show[t]] :: summonAll[ts]

inline given Option[A](using inline s: => Show[A]): Show[Option[A]] = new Show[Option[A]] {
  override def show(a: Option[A]): String = a.map(s.show).getOrElse("")
}



inline given[A] (using m: Mirror.Of[A]): Show[A] = new Show[A] {
  override def show(a: A): String = {
    val elemInstances = summonAll[m.MirroredElemTypes]
    elemInstances.mkString(", ")
  }
}

case class Rec(int: Int, rs: Option[Rec])

object Main extends App {
  summon[Show[Rec]]
}

@luksow
Copy link
Author

luksow commented Aug 16, 2024

@hamnis I believe this is still an issue with 0.14.9 and Scala 3.4.2:

//> using dep io.circe::circe-core:0.14.9
//> using dep io.circe::circe-generic:0.14.9
//> using dep io.circe::circe-parser:0.14.9

import io.circe._
import io.circe.generic.auto._

case class R1(int: Int, r: Seq[R1])
case class R2(int: Int, r: Option[R2])

summon[Encoder[R1]]
summon[Encoder[R2]]

output:

[error] ./Main.sc:11:20
[error] cannot reduce summonFrom with
[error]  patterns :  case given encodeA @ _:io.circe.Encoder[Seq[Main$_.this.R1]]
[error]              case given m @ _:scala.deriving.Mirror.Of[Seq[Main$_.this.R1]]
[error] summon[Encoder[R1]]
[error]                    ^
[error] ./Main.sc:12:20
[error] Failed to find an instance of Encoder[scala.Option[Main$_.this.R2]]
[error] summon[Encoder[R2]]
[error]   

@hamnis hamnis reopened this Aug 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: https://github.com/circe/circe/issues/1980

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy