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

StackOverflowException for contra-variant type-class #464

Open
alexandru opened this issue Apr 10, 2023 · 1 comment
Open

StackOverflowException for contra-variant type-class #464

alexandru opened this issue Apr 10, 2023 · 1 comment

Comments

@alexandru
Copy link
Contributor

Hi all,

I have this typeclass that's contravariant in its type parameter (sorry for any compilation errors, I had to modify the code in-place):

trait LogShow[-A] {
  def logShow(a: A): String
}

object LogShow {
  type Typeclass[-T] = LogShow[T]

  def join[T](ctx: CaseClass[LogShow, T]): LogShow[T] =
    (value: T) => {
      ctx.parameters.foldLeft("") {
        case (str, p) =>
          val child = p.typeclass.logShow(p.dereference(value))
          str + "; " + child
      }
    }

  def split[T](ctx: SealedTrait[LogShow, T]): LogShow[T] =
    (value: T) => {
      ctx.split(value) { sub =>
        sub.typeclass.logShow(sub.cast(value))
      }
    }

  def derive[T]: LogShow[T] =
    macro Magnolia.gen[T]
}

Then I have the following test:

sealed trait Entity

object Entity {
  final case class User(
    name: String,
    age: Int,
    email: String,
  ) extends Entity

  final case class Robot(
    id: String,
    description: String
  ) extends Entity

  implicit lazy val logShow: LogShow[Entity] =
    LogShow.derive[Entity]
}

Invoking this logShow leads to a stack-overflow error.

It makes sense to me because the auto-derivation logic looks for instances of LogShow[Robot] or LogShow[User], which are hitting the same lazy val.

Are there some tricks I could use here to deal with variance like this?

@alexandru alexandru changed the title StackOverflowException on deriving instances for contra-variant type-class StackOverflowException for contra-variant type-class Apr 10, 2023
@adamw
Copy link
Member

adamw commented Apr 12, 2023

Hm good question ... not sure if that's possible. As I understand you'd like to invoke derivation for the leaves here. But how should magnolia know if in a specific case it should use the available implicit, or invoke its own derivation?

Probably not very useful, but I think explicitly specifying the implicits for the leaves might work:

implicit lazy val logShow: LogShow[Robot] = LogShow.derive[Robot]
implicit lazy val logShow: LogShow[User] = LogShow.derive[User]
implicit lazy val logShow: LogShow[Entity] = LogShow.derive[Entity]

though I haven't tested this

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

2 participants