简体   繁体   English

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

[英]Map single type HList to HList of target types

I have trait-marker 我有特征标记

trait TypedTrait {
  type TYPE
}

and the realization 和实现

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

And I want to map HList of String into HList of TypedString in accordance with TypedString 's type parameters. 我要地图HListStringHListTypedString按照TypedString的类型参数。

The simplest way is to create convert method (as described in Shapeless map HList depending on target types ): 最简单的方法是创建convert方法(如Shapeless映射HList中所述, 具体取决于目标类型 ):

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

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

But I'd like to avoid redundant parameterization and use something like this: 但是我想避免多余的参数化,并使用类似这样的东西:

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

Complete code example for the first solution: 第一个解决方案的完整代码示例:

      import shapeless._

      trait TypedTrait {
        type TYPE
      }

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

      trait Convert[I <: HList, O <: HList] { def apply(i: I): O }

      object Convert extends LowPriorityConvertInstances {
        implicit val convertHNil: Convert[HNil, HNil] = new Convert[HNil, HNil] {
          def apply(i: HNil): HNil = i
        }

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

      sealed class LowPriorityConvertInstances {
        implicit def convertHCons[H, T <: HList, TO <: HList](implicit
                                                              c: Convert[T, TO]
                                                             ): Convert[H :: T, H :: TO] = new Convert[H :: T, H :: TO] {
          def apply(i: H :: T): H :: TO = i.head :: c(i.tail)
        }
      }


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

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

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

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

You can achive this by having three HList type arguments in Convert : 您可以通过在Convert使用三个HList类型参数来实现此HList

  • the type of the actual HList passed to convert (eg, String :: String :: HNil ) 传递给convert的实际HList的类型(例如String :: String :: HNil
  • the type parameter prescribed by the user (eg, Int :: Boolean :: HNil ) 用户指定的类型参数(例如, Int :: Boolean :: HNil
  • the output type – basically the prescribed HList wrapped in TypedString : eg, TypedString[Int] :: TypedString[Boolean] :: HNil . 输出类型–基本上是包装在TypedString的指定HList :例如, TypedString[Int] :: TypedString[Boolean] :: HNil

The output type can be completely calculated from the prescribed HList , so I'd use the Aux pattern commonly employed with shapeless code: 可以从指定的HList完全计算输出类型,因此我将使用shapeless代码常用的Aux模式

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)

Result: 结果:

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

I believe it may be possible to achieve this using some operations provided with shapeless ( shapeless.ops.hlist.Mapped , shapeless.ops.hlist.HKernel , or shapeless.ops.hlist.RightFolder look appropriate), but I don't know how to write a Poly function, that takes a type argument and a normal argument. 我认为可能可以使用shapeless提供的一些操作来实现此目的( shapeless.ops.hlist.Mappedshapeless.ops.hlist.HKernelshapeless.ops.hlist.RightFolder看起来合适),但我不知道如何编写一个Poly函数,该函数需要一个类型参数和一个普通参数。 Any tips would be welcome. 任何提示都将受到欢迎。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM