Using shapeless, one can use LabelledGeneric
to update case class fields like so:
case class Test(id: Option[Long], name: String)
val test = Test(None, "Name")
val gen = LabelledGeneric[Test]
scala> gen.from(gen.to(test) + ('id ->> Option(1L)))
res0: Test = Test(Some(1),Name)
I would like the Test
class (and others) to extend an abstract class Model
, that will implement a method withId
that would use a LabelledGeneric
similar to the above code to update the id
field, should it have one (which it should).
My attempt adds an implicit parameter of a LabelledGeneric[A]
to the constructor of Model
, which materializes just fine. I also need to somehow provide evidence to the record syntax that the LabelledGeneric#Repr
has the id
field to replace. Adding an implicit Updater
parameter to withId
satisfies the compiler, so that the code below will compile, but it is not usable.
import shapeless._, record._, ops.record._, labelled._, syntax.singleton._, tag._
abstract class Model[A](implicit gen: LabelledGeneric[A] { type Repr <: HList }) { this: A =>
def id: Option[Long]
val idWitness = Witness("id")
type F = FieldType[Symbol with Tagged[idWitness.T], Option[Long]]
def withId(id: Long)(implicit u: Updater.Aux[gen.Repr, F, gen.Repr]) =
gen.from(gen.to(this) + ('id ->> Option(id)))
}
case class Test(id: Option[Long], name: String) extends Model[Test]
When calling test.withId(1)
, the implicit Updater
fails to materialize. The macro reports that gen.Repr
isn't an HList
type, when it in fact is. It seems that this match is the one that fails, where u baseType HConsSym
returns <notype>
. Equivalent to:
scala> weakTypeOf[test.gen.Repr].baseType(weakTypeOf[::[_, _]].typeConstructor.typeSymbol)
res12: reflect.runtime.universe.Type = <notype>
This is using shapeless 2.3, though it fails for different reasons in 2.2 (seems as though Updater
had a large refactor).
Is it possible to accomplish this with shapeless, or am I way off target?