2015-03-06 9 views
9

Próbuję nauczyć się Shapeless i chciałbym zdefiniować monoid, który dodaje instancje bezkształtnych rekordów. Zauważ, że używam mono-algeberów (nie scalaz), ale jestem pewien, że są całkiem podobne. Oto przykład tego, co chciałbym być w stanie to zrobić:Definiowanie Typeclass for Shapeless Records

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 

zorientowali się, jak napisać monoid instancje dla HList, co następuje:

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] 

To pozwala mi pisać:

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

teraz mogę podsumować HList instancji, brakujący kawałek wydaje się definiowanie instancji monoid który może liczyć pól formularza ('name ->> 1), co mówi mi mój IDE posiada następujące rodzaje: Int with record.KeyTag[Symbol with tag.Tagged[Constant(name).type] { .. }, Int] { .. }. W tym momencie utknąłem, ponieważ po prostu nie wiem, jak to zrobić.

Odpowiedz

11

Byliście bardzo blisko, wystarczy dodać FieldType[K, H] na każdym kroku indukcyjnego zamiast H i używać field[K] wpisywać wartości otrzymasz od Monoid[H] odpowiednio miejsca:

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) 
    } 

Albo można użyć TypeClass maszyny bezkształtne za , co daje ci również instancje dla klas wypadku itp .:

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._ 

I udostępniliśmy derivedRecordInstance metody tutaj sprawia, że ​​ten pracuję nad rekordami, ale jestem trochę zaskoczony, że jest to konieczne - możliwe, że dostaniesz rekordowe instancje za darmo w przyszłej wersji programu Shapeless.

+0

Dzięki za pomoc, pierwsze rozwiązanie mi pomogło. Jestem pewien, że drugi przykład też działa, ale muszę go trochę przestudiować, żeby to zrozumieć. Chciałam spróbować nauczyć się używać metody "bezkształtnej" wyprowadzki z automatycznej typografii, ale na razie ten kod jest dla mnie dość nieprzejrzysty. – JimN