(依赖?)打字容器

时间:2016-01-06 20:03:41

标签: generic-programming shapeless hlist

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

  • 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追加)

有没有一种很好的方法可以在无形状中做到这一点?或者是否有一种更简单的方法来解决这个特定的问题:)问题是我有一个产生F[Int]F[HNil]F[HNil]的组合子。例如,组合这些不应该产生F[Int :: HNil :: HNil](但F[Int :: HNil])。为此目的,我已经制作了类型类:

 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)

}

2 个答案:

答案 0 :(得分: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

答案 1 :(得分: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类型,然后使用Prepend的{​​{1}}类型类来获得最终版本实现结果类型。 (我实际上相当肯定你可以使用HList甚至是Applicative类型来实现同样的目标。请将这一点留给你去发现。)