2015-03-06 8 views
9

Ich versuche, Shapeless zu lernen, und ich möchte ein Monoid definieren, das Instanzen von formlosen Aufzeichnungen zusammenfügt. Beachten Sie, dass ich Algebird-Monoids (nicht Scalaz) verwende, aber ich bin mir sicher, dass sie sich sehr ähnlich sind. Hier ist ein Beispiel dafür, was Ich mag würde der Lage sein, zu tun:Definieren Sie eine Typklasse für formlose Datensätze

val result = Monoid.sum(
    ('a ->> 1) :: ('b ->> 1) :: HNil, 
    ('a ->> 4) :: ('b ->> 3) :: HNil, 
    ('a ->> 2) :: ('b ->> 6) :: HNil) 
// result should be: ('a ->> 7) :: ('b ->> 10) :: HNil 

ich herausgefunden, wie Monoid-Instanzen für hList zu schreiben, wie folgt:

implicit val HNilGroup: Group[HNil] = new ConstantGroup[HNil](HNil) 
    implicit val HNilMonoid: Monoid[HNil] = HNilGroup 
    class HListMonoid[H, T <: HList](implicit hmon: Monoid[H], tmon: Monoid[T]) extends Monoid[::[H, T]] { 
    def zero = hmon.zero :: tmon.zero 
    def plus(a: ::[H, T], b: ::[H, T]) = 
     hmon.plus(a.head, b.head) :: tmon.plus(a.tail, b.tail) 
    } 
    implicit def hListMonoid[H, T <: HList](implicit hmon: Monoid[H], tmon: Monoid[T]) = new HListMonoid[H, T] 

Dies erlaubt mir zu schreiben:

val result = Monoid.sum(
    1 :: 1 :: HNil, 
    4 :: 3 :: HNil, 
    2 :: 6 :: HNil) 
// result is 7 :: 10 :: HNil 

Nun, da ich hList Instanzen summieren kann, scheint das fehlende Stück Monoid Instanzen zu definieren, welche Felder der Form summieren können ('name ->> 1), die meine IDE sagt mir den folgenden Typ hat: Int with record.KeyTag[Symbol with tag.Tagged[Constant(name).type] { .. }, Int] { .. }. An dieser Stelle stecke ich fest, da ich einfach nicht weiß, wie ich das machen soll.

Antwort

11

Sie waren ganz in der Nähe Sie nur FieldType[K, H] statt H und field[K] bei jedem induktiven Schritt hinzufügen müssen verwenden, um die Werte, die Sie aus dem Monoid[H] angemessen erhalten zu geben:

import com.twitter.algebird._ 
import shapeless._, labelled._, record._, syntax.singleton._ 

implicit val hnilGroup: Group[HNil] = new ConstantGroup[HNil](HNil) 
implicit val hnilMonoid: Monoid[HNil] = hnilGroup 
implicit def hconsMonoid[K, H, T <: HList](implicit 
    hm: Monoid[H], 
    tm: Monoid[T] 
): Monoid[FieldType[K, H] :: T] = 
    Monoid.from(field[K](hm.zero) :: tm.zero) { 
    case (hx :: tx, hy :: ty) => field[K](hm.plus(hx, hy)) :: tm.plus(tx, ty) 
    } 

Oder Sie Shapeless der TypeClass Maschinen nutzen könnten , das gibt Ihnen auch Instanzen für Fallklassen, etc .:

import com.twitter.algebird._ 
import shapeless._, ops.hlist._, ops.record._, record._, syntax.singleton._ 

object MonoidHelper extends ProductTypeClassCompanion[Monoid] { 
    object typeClass extends ProductTypeClass[Monoid] { 
    def emptyProduct: Monoid[HNil] = Monoid.from[HNil](HNil)((_, _) => HNil) 
    def product[H, T <: HList](hm: Monoid[H], tm: Monoid[T]): Monoid[H :: T] = 
     Monoid.from(hm.zero :: tm.zero) { 
     case (hx :: tx, hy :: ty) => hm.plus(hx, hy) :: tm.plus(tx, ty) 
     } 

    def project[F, G](m: => Monoid[G], to: F => G, from: G => F): Monoid[F] = 
     Monoid.from(from(m.zero))((x, y) => from(m.plus(to(x), to(y)))) 
    } 

    implicit def deriveRecordInstance[ 
    R <: HList, 
    K <: HList, 
    H, 
    T <: HList 
    ](implicit 
    vs: Values.Aux[R, H :: T],   
    vm: Lazy[Monoid[H :: T]], 
    ks: Keys.Aux[R, K], 
    zk: ZipWithKeys.Aux[K, H :: T, R] 
): Monoid[R] = typeClass.project(vm.value, vs(_), zk(_: H :: T)) 
} 

import MonoidHelper._ 

ich ein derivedRecordInstance Verfahren hier zur Verfügung gestellt haben, die dies macht arbeite an Datensätzen, aber ich bin ein wenig überrascht, dass es notwendig ist - es ist möglich, dass du in einer zukünftigen Version von Shapeless Record-Instanzen kostenlos erhältst.

+0

Danke für die Hilfe, die erste Lösung hat den Trick für mich. Ich bin mir sicher, dass das zweite Beispiel auch funktioniert, aber ich muss es ein wenig mehr studieren, um es zu verstehen. Ich wollte eigentlich lernen, wie ich Shapeless 'Auto-Typklassen-Ableitung verwenden kann, aber im Moment ist dieser Code ziemlich undurchsichtig für mich. – JimN

Verwandte Themen