2017-06-21 12 views
4

どのクラスでも "counter"というIntフィールドを増やすことができるタイプクラスを作成しようとしています。フィールド。Scala/Shapeless:caseクラスインスタンスで名前付きフィールドを更新する

私は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)) 

ような何かをできるようにすることであり、それは、適切なカウンタフィールドにどのような場合でもクラスのために働く必要があります。

これは、型クラスと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です。これは、コンパイラがcounterというフィールドとTタイプのIntであることを保証していないためです。しかし、どうすればそれを伝えることができますか? Shapelessのレコードに関するより良い/より多くの文書がありますか?または、これは完全に間違った方法ですか?

答えて

7

あなただけ暗黙的にかなり素晴らしいです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)) 
} 
+0

を要求しなければなりません。 –

+0

私は不思議です、この証人は何ですか?証人。 –

+1

@CyrilleCorpetこれは、 '' counter'' Symbol'の一意のインスタンス型です。これは、無形の 'レコード'がキーのために使用するものです。 'Witness'は' 'Witness''や' '.T''のように、' Symbol'以外の型のインスタンスに対してユニークな型を作るのに使うことができます。 – Kolmar

0

あなたは簡単に、単純な型クラスの派生でそれを行うことができます。

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))) 
} 
関連する問題