(依赖?)打字容器
(Dependantly?) typing containers
给定一个类型 constructor/container 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 前缀)
F[L <: HList]
和 F[B]
变成 F[L :: B :: HNil]
(hlist append)
在 shapeless 中有什么好的方法可以做到这一点吗?或者有没有更简单的方法来解决这个特定问题 :) 问题是我有组合器产生 F[Int]
、F[HNil]
和 F[HNil]
。例如,组合这些不应产生 F[Int :: HNil :: HNil]
(但 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)
}
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"
}
有一种很好的方法可以使用一系列 classes 来做到这一点。如果你的 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) }
}
如果你想要真正的 return 类型,你可以在 Conv
伴随对象中定义一个 Aux
类型,然后使用 Prepend
类型 class一个 HList
以获得最终实现的结果类型。 (我实际上相当肯定你可以用 Applicative
甚至 Zip
类型 class 实现同样的事情。这将留给你去发现。)
给定一个类型 constructor/container 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 前缀)F[L <: HList]
和F[B]
变成F[L :: B :: HNil]
(hlist append)
在 shapeless 中有什么好的方法可以做到这一点吗?或者有没有更简单的方法来解决这个特定问题 :) 问题是我有组合器产生 F[Int]
、F[HNil]
和 F[HNil]
。例如,组合这些不应产生 F[Int :: HNil :: HNil]
(但 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)
}
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"
}
有一种很好的方法可以使用一系列 classes 来做到这一点。如果你的 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) }
}
如果你想要真正的 return 类型,你可以在 Conv
伴随对象中定义一个 Aux
类型,然后使用 Prepend
类型 class一个 HList
以获得最终实现的结果类型。 (我实际上相当肯定你可以用 Applicative
甚至 Zip
类型 class 实现同样的事情。这将留给你去发现。)