将单一类型HList映射到目标类型的HList

时间:2016-08-10 11:31:09

标签: scala shapeless

我有特质标记

trait TypedTrait {
  type TYPE
}

和实现

case class TypedString[U](value: String) extends TypedTrait {
  type TYPE = U
}

我想根据HList的类型参数将String的{​​{1}}映射到HList的{​​{1}}。

最简单的方法是创建TypedString方法(如Shapeless map HList depending on target types中所述):

TypedString

但我想避免冗余参数化,并使用类似的东西:

convert

第一个解决方案的完整代码示例:

    val list = "Hello" :: "world" :: HNil

    val mapped: TypedString[Int] :: TypedString[Boolean] :: HNil =
           convert[TypedString[Int] :: TypedString[Boolean] :: HNil](list)

1 个答案:

答案 0 :(得分:1)

您可以通过HList中的三个Convert类型参数来实现此目的:

  • 传递给HList的实际convert的类型(例如String :: String :: HNil
  • 用户规定的类型参数(例如,Int :: Boolean :: HNil
  • 输出类型 - 基本上是HList中包含的规定TypedString:例如,TypedString[Int] :: TypedString[Boolean] :: HNil

输出类型可以从规定的HList完全计算,因此我使用shapeless代码中常用的Aux pattern

trait Convert[In <: HList, Prescribed <: HList] {
  type Out <: HList
  def apply(i: In): Out
}

object Convert {
  type Aux[I <: HList, P <: HList, O <: HList] = Convert[I, P] { type Out = O }

  // Adapt the implicits accordingly. 
  // The low priority one is left as an exercise to the reader.

  implicit val convertHNil: Convert.Aux[HNil, HNil, HNil] = 
    new Convert[HNil, HNil] {
      type Out = HNil
      def apply(i: HNil): HNil = i
    }

  implicit def convertHConsTS[TS, TI <: HList, TP <: HList, TO <: HList](implicit
    c: Convert.Aux[TI, TP, TO]
  ): Convert.Aux[String :: TI, TS :: TP, TypedString[TS] :: TO] =
    new Convert[String :: TI, TS :: TP] {
      type Out = TypedString[TS] :: TO
      def apply(i: String :: TI): TypedString[TS] :: TO = 
        TypedString[TS](i.head) :: c(i.tail)
    }
}  

class PartiallyAppliedConvert[P <: HList] {
  def apply[I <: HList](i: I)(implicit c: Convert[I, P]): c.Out = c(i)
}

def convert[O <: HList]: PartiallyAppliedConvert[O] =
  new PartiallyAppliedConvert[O]

val list = "Hello" :: "world" :: HNil

val mapped = convert[Int :: String :: HNil](list)

结果:

scala> mapped
res3: shapeless.::[com.Main.TypedString[Int],shapeless.::[com.Main.TypedString[String],shapeless.HNil]] = TypedString(Hello) :: TypedString(world) :: HNil

我相信可以使用shapeless提供的一些操作(shapeless.ops.hlist.Mappedshapeless.ops.hlist.HKernelshapeless.ops.hlist.RightFolder看起来合适)来实现这一目标,但我不会&#39 ; t知道如何编写一个Poly函数,它接受一个类型参数和一个普通参数。任何提示都会受到欢迎。