为无形记录定义类型类

Jim*_*imN 9 scala record typeclass shapeless

我正在尝试学习Shapeless,我想定义一个monoid,它将无形记录的实例加在一起.请注意,我使用的是algebird monoids(不是scalaz),但我确信它们非常相似.以下是我希望能够做到的一个例子:

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
Run Code Online (Sandbox Code Playgroud)

我想出了如何为HList编写monoid实例,如下所示:

  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]
Run Code Online (Sandbox Code Playgroud)

这允许我写:

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

既然我可以对HList实例求和,那么缺少的部分似乎是定义了monoid实例,它们可以对表单的字段求和('name ->> 1),我的IDE告诉我它具有以下类型:Int with record.KeyTag[Symbol with tag.Tagged[Constant(name).type] { .. }, Int] { .. }.此时我被困住了,因为我不知道该怎么做.

Tra*_*own 11

你是非常接近的,你只需要添加FieldType[K, H]在每个电感一步,而不是H和使用field[K]输入你从一开始的值Monoid[H]适当:

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)
  }
Run Code Online (Sandbox Code Playgroud)

或者您可以使用Shapeless的TypeClass机器,它也为您提供案例类等的实例:

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._
Run Code Online (Sandbox Code Playgroud)

我在derivedRecordInstance这里提供了一个方法,使得这个工作在记录上,但我有点惊讶,这是必要的 - 你可能会在未来的Shapeless版本中免费获得记录实例.