给定一个类型构造函数/容器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)
}
答案 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"
}
答案 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
类型来实现同样的目标。请将这一点留给你去发现。)