Scala/Shapeless:更新命名字段以防 class 实例
Scala/Shapeless: Updating named field in case class instance
我正在尝试创建一个类型 class,它允许我在任何情况下 class 增加一个名为 "counter" 的 Int 字段,只要 class有这样的领域。
我曾尝试用 Shapeless 做到这一点,但我遇到了困难(在第一次尝试消化 "The Type Astronaut's Guide to Shapeless"、Shapeless 2.0.0 的 "Feature overview" 和 Stack Overflow 上的许多线程之后)。
我想要的是能够做类似
的事情
case class MyModel(name:String, counter:Int) {}
val instance = MyModel("Joe", 4)
val incremented = instance.increment()
assert(incremented == MyModel("Joe", 5))
它应该适用于任何情况 class 具有合适的计数器字段。
我认为这可以使用类型 class 和 Shapeless 的记录抽象(以及隐式转换以获取作为方法添加的增量功能)。简略的结构应该是这样的:
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 的记录有 better/more 文档吗?或者这是一个完全错误的方法?
您可以通过简单的类型类推导轻松完成:
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)))
}
你必须隐含地要求一个 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))
}
我正在尝试创建一个类型 class,它允许我在任何情况下 class 增加一个名为 "counter" 的 Int 字段,只要 class有这样的领域。
我曾尝试用 Shapeless 做到这一点,但我遇到了困难(在第一次尝试消化 "The Type Astronaut's Guide to Shapeless"、Shapeless 2.0.0 的 "Feature overview" 和 Stack Overflow 上的许多线程之后)。
我想要的是能够做类似
的事情case class MyModel(name:String, counter:Int) {}
val instance = MyModel("Joe", 4)
val incremented = instance.increment()
assert(incremented == MyModel("Joe", 5))
它应该适用于任何情况 class 具有合适的计数器字段。
我认为这可以使用类型 class 和 Shapeless 的记录抽象(以及隐式转换以获取作为方法添加的增量功能)。简略的结构应该是这样的:
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 的记录有 better/more 文档吗?或者这是一个完全错误的方法?
您可以通过简单的类型类推导轻松完成:
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)))
}
你必须隐含地要求一个 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))
}