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

Null typeclass for Param in automatic derivation #402

Open
wsargent opened this issue May 30, 2022 · 2 comments
Open

Null typeclass for Param in automatic derivation #402

wsargent opened this issue May 30, 2022 · 2 comments

Comments

@wsargent
Copy link

I have a failing spec in my project that is coming from a null typeclass, and I suspect this is a bug in Magnolia

[info] - should derive a case class *** FAILED ***
[info]   java.lang.NullPointerException: type class is null!
[info]   at java.base/java.util.Objects.requireNonNull(Objects.java:246)
[info]   at com.tersesystems.echopraxia.plusscala.api.Derivation.$anonfun$joinCaseClass$2(Derivation.scala:75)
[info]   at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:286)
[info]   at scala.collection.IndexedSeqOptimized.foreach(IndexedSeqOptimized.scala:36)
[info]   at scala.collection.IndexedSeqOptimized.foreach$(IndexedSeqOptimized.scala:33)
[info]   at scala.collection.mutable.WrappedArray.foreach(WrappedArray.scala:38)
[info]   at scala.collection.TraversableLike.map(TraversableLike.scala:286)
[info]   at scala.collection.TraversableLike.map$(TraversableLike.scala:279)
[info]   at scala.collection.AbstractTraversable.map(Traversable.scala:108)
[info]   at com.tersesystems.echopraxia.plusscala.api.Derivation.com$tersesystems$echopraxia$plusscala$api$Derivation$$$anonfun$joinCaseClass$1(Derivation.scala:72)

Here's the code for it:

sealed trait Derivation extends ValueTypeClasses {
  type Typeclass[T] = ToValue[T]
  type CaseClass[T]   = magnolia1.CaseClass[Typeclass, T]
  type SealedTrait[T] = magnolia1.SealedTrait[Typeclass, T]

  final def join[T](ctx: CaseClass[T]): Typeclass[T] = {
    if (ctx.isValueClass) {
      joinValueClass(ctx)
    } else if (ctx.isObject) {
      joinCaseObject(ctx)
    } else {
      joinCaseClass(ctx)
    }
  }

  // this is a regular case class
  protected def joinCaseClass[T](ctx: CaseClass[T]): Typeclass[T] = { obj =>
    val typeInfo = Field.keyValue("@type", ToValue(ctx.typeName.full))
    val fields: Seq[Field] = ctx.parameters.map { p =>
      val name: String = p.label
      val attribute = p.dereference(obj)
      val typeclassInstance = Objects.requireNonNull(p.typeclass, "type class is null!")
      val value: Value[_] = typeclassInstance.toValue(attribute)
      Field.keyValue(name, value)
    }
    ToObjectValue(typeInfo +: fields)
  }

  // this is a case object, we can't do anything with it.
  protected def joinCaseObject[T](ctx: CaseClass[T]): Typeclass[T] = {
    // ctx has no parameters, so we're better off just passing it straight through.
    value => Value.string(value.toString)
  }

  // this is a value class aka AnyVal, we should pass it through.
  protected def joinValueClass[T](ctx: CaseClass[T]): Typeclass[T] = {
    val param = ctx.parameters.head
    value => param.typeclass.toValue(param.dereference(value))
  }

  // this is a sealed trait
  def split[T](ctx: SealedTrait[T]): Typeclass[T] = (value: T) => {
    ctx.split(value) { sub =>
      sub.typeclass.toValue(sub.cast(value))
    }
  }
}

trait SemiAutoDerivation extends Derivation {
  final def gen[T]: Typeclass[T] = macro Magnolia.gen[T]
}

The PR at tersesystems/echopraxia-plusscala#1 (comment)

This is taken directly from https://github.com/softwaremill/magnolia/blob/scala2/examples/src/main/scala/magnolia1/examples/print.scala#L22 so I don't think this should be possible.

@wsargent wsargent changed the title Null typeclass for Param in semi-automatic derivation Null typeclass for Param in automatic derivation May 30, 2022
@wsargent
Copy link
Author

wsargent commented May 30, 2022

I've put together an isolated test case https://github.com/wsargent/magnolia-issue-402

@wsargent
Copy link
Author

wsargent commented May 30, 2022

It looks like a simple workaround is to define generated implicits using implicit lazy val fooToValue = gen[Foo] and that will resolve the NPE (presumably because by then the instant implicit is in scope)

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

1 participant