Scala / Lifeless: updating the field name in an instance of the case class

I am trying to create a type class that will allow me to increment an Int field called "counter" for any case class if that class has such a field.

I tried to do this with Shapeless, but I hit the walls (first I tried to digest the Lifeless Astronaut Guide), Shapeless 2.0.0 Feature Overview, and numerous threads in the stack overflow).

I want to be able to do something like

case class MyModel(name:String, counter:Int) {}

val instance = MyModel("Joe", 4)
val incremented = instance.increment()
assert(incremented == MyModel("Joe", 5))

And it should work for any case class with a suitable counter field.

I thought this was possible using a type class and Shapeless notation abstraction (and implicit conversion to get extra functionality added as a method). The bare bones will be something like this:

trait Incrementer[T] {
  def inc(t:T): T
}

object Incrementer {
  import shapeless._ ; import syntax.singleton._ ; import record._

  implicit def getIncrementer[T](implicit generator: LabelledGeneric[T]): Incrementer[T] = new Incrementer[T] {
    def inc(t:T) = {
      val repr = generator.to(t)
      generator.from(repr.replace('counter, repr.get('counter) + 1))
    }
  }     
}

. value replace is not a member of generator.Repr. , , , T counter Int. ? / Shapeless? ?

+4
2

Modifier

import shapeless._
import ops.record._
implicit class Incrementer[T, L <: HList](t: T)(
  implicit gen: LabelledGeneric.Aux[T, L],
  modifier: Modifier.Aux[L, Witness.`'counter`.T, Int, Int, L]
) {
  def increment(): T = gen.from(modifier(gen.to(t), _ + 1))
}
+7

typeclass:

trait Incrementer[T] {
  def inc(s: Symbol)(t: T): T
}

object Incrementer {
  def apply[T](implicit T: Incrementer[T]): Incrementer[T] = T
  implicit def head[Key <: Symbol, Head, Tail <: HList](implicit Key: Witness.Aux[Key], Head: Numeric[Head]) = new Incrementer[FieldType[Key, Head] :: Tail] {
    import Head._
    override def inc(s: Symbol)(t: FieldType[Key, Head] :: Tail): (FieldType[Key, Head] :: Tail) =
      if (s == Key.value) (t.head + fromInt(1)).asInstanceOf[FieldType[Key, Head]] :: t.tail
      else t
  }

  implicit def notHead[H, Tail <: HList](implicit Tail: Incrementer[Tail]) = new Incrementer[H :: Tail] {
    override def inc(s: Symbol)(t: H :: Tail): H :: Tail = t.head :: Tail.inc(s)(t.tail)
  }

  implicit def gen[T, Repr](implicit gen: LabelledGeneric.Aux[T, Repr], Repr: Incrementer[Repr]) = new Incrementer[T] {
    override def inc(s: Symbol)(t: T): T = gen.from(Repr.inc(s)(gen.to(t)))
  }
}

case class Count(counter: Int)
case class CountAndMore(more: String, counter: Int)
case class FakeCount(counter: Long)
object Test extends App {

  println(Incrementer[Count].inc('counter)(Count(0)))
  println(Incrementer[CountAndMore].inc('counter)(CountAndMore("", 0)))
  println(Incrementer[FakeCount].inc('counter)(FakeCount(0)))
}
0

Source: https://habr.com/ru/post/1679812/


All Articles