【问题标题】:Map single type HList to HList of target types将单一类型 HList 映射到目标类型的 HList
【发布时间】:2016-08-10 11:31:08
【问题描述】:

我有特征标记

trait TypedTrait {
  type TYPE
}

以及实现

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

并且我想根据TypedString的类型参数将StringHList映射成TypedStringHList

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

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

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

但我想避免多余的参数化并使用这样的东西:

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

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

      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)

【问题讨论】:

    标签: scala shapeless


    【解决方案1】:

    您可以通过在Convert 中使用三个HList 类型参数来实现这一点:

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

    输出类型完全可以从规定的HList计算出来,所以我会使用Aux pattern常用的shapeless代码:

    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 看起来合适)可能可以实现这一点,但我不知道如何编写Poly函数,它接受一个类型参数和一个普通参数。欢迎任何提示。

    【讨论】:

      猜你喜欢
      • 2017-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-17
      • 1970-01-01
      • 2012-07-11
      • 2016-07-30
      • 2014-06-03
      相关资源
      最近更新 更多