1

给定一个类型构造函数/容器F[_],我想创建一个组合器,它可以通过以下规则集将多态类型和 hlist 类型组合到一个新容器中:

  • F[HNil]F[HNil]进入一个F[HNil]
  • F[A]F[HNil]进入一个F[A :: HNil]
  • F[HNil]F[A]进入一个F[A :: HNil]
  • F[A]F[B]进入一个F[A :: B :: HNil]
  • F[L <: HList]F[R <: HList]进入F[L :: R](hlist concat)
  • F[A]F[R <: HList]进入F[A :: R](hlist prepend)
  • F[L <: HList]F[B]进入F[L :: B :: HNil](hlist append)

有没有一种很好的方法可以在无形中做到这一点?或者有没有更简单的方法来解决这个特殊问题:) 问题是我有组合器,它们产生一个F[Int],F[HNil]F[HNil]. 例如,将这些结合起来不应产生F[Int :: HNil :: HNil](but F[Int :: HNil])。为此,我现在制作了 typeclass:

 trait CodecAppender[F, A, B] {
  type Out
  final type CodecOut = Codec[F, Out]
  def apply(a: Codec[F, A], b: Codec[F, B]): CodecOut
}

object CodecAppender {

  type Aux[F, A, B, C] = CodecAppender[F, A, B] { type Out = C }

  private def make[F, A, B, C](decompose: C => (A, B))(implicit S: Monoid[F], D: DecoderAppender.Aux[F, A, B, C]): Aux[F, A, B, C] = new CodecAppender[F, A, B] {
    type Out = C

    def apply(a: Codec[F, A], b: Codec[F, B]): CodecOut = new Codec[F, C] {
      def encode(in: C) = {
        val (head, tail) = decompose(in)
        a.encode(head) |+| b.encode(tail)
      }

      val decoder = D.append(a.decoder, b.decoder)
    }
  }

  implicit def HNilAndHNil[F, O](implicit M: Monoid[F],
                                 D: DecoderAppender.Aux[F, HNil, HNil, HNil]) =
    make[F, HNil, HNil, HNil](r => HNil -> HNil)

  //other cases omitted, but there are 6 more (which I described above)

}
4

2 回答 2

1

Scodec 正在做类似的事情:

  def prepend[A, L <: HList](a: Codec[A], l: Codec[L]): Codec[A :: L] = new Codec[A :: L] {
    override def sizeBound = a.sizeBound + l.sizeBound
    override def encode(xs: A :: L) = Codec.encodeBoth(a, l)(xs.head, xs.tail)
    override def decode(buffer: BitVector) = Codec.decodeBothCombine(a, l)(buffer) { _ :: _ }
    override def toString = s"$a :: $l"
  }

https://github.com/scodec/scodec/blob/ac71016dcea61cc6aaabe4f4dff4ab5bf13ac239/shared/src/main/scala/scodec/codecs/HListCodec.scala#L19-L25

于 2016-01-07T10:04:15.640 回答
1

使用一系列类型类有一个很好的方法来做到这一点。如果你F[_]有一个实例,你可以做一些事情Monad

trait Conv[A]{
  type Out <: HList
  def apply(a: A): Out
}

object Conv extends LowPrior{
  implicit def hlist[L <: HList] = new Conv[L]{
    type Out = L
    def apply(hl: L) = hl
  }
}

trait LowPrior{
  implicit def other[A] = new Conv[A]{
    type Out = H :: HNil
    def apply(a: A) = a :: HNil
  }
}

def combineF[F[_], A, B](fa: F[A], fb: F[B])(implicit ca: Conv[A], cb: Conv[B], m: Monad[F]) = m.flatMap(fa){ a: A =>
  m.map(fb){ b: B => ca(a) ::: c(b) }
}

如果你想要真正的返回类型,你可以Aux在伴生对象中定义一个类型Conv,然后使用Prependan 的类型类HList来获得最终实现的结果类型。(我实际上相当肯定你可以用一个Applicative甚至一个Zip类型类来实现同样的事情。这将留给你去发现。)

于 2016-01-08T01:44:20.147 回答