首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将单类型HList映射到目标类型的HList

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

Stack Overflow用户
提问于 2016-08-10 11:31:08
回答 1查看 113关注 0票数 0

我有特征标记

代码语言:javascript
复制
trait TypedTrait {
  type TYPE
}

以及实现

代码语言:javascript
复制
case class TypedString[U](value: String) extends TypedTrait {
  type TYPE = U
}

并根据HList的类型参数,将StringHList映射为TypedStringHList

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

代码语言:javascript
复制
    val list = "Hello" :: "world" :: HNil

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

但是,我想避免冗余的参数化,并使用这样的方法:

代码语言:javascript
复制
    val mapped: TypedString[Int] :: TypedString[Boolean] :: HNil =
               convert[Int :: Boolean :: HNil](list)

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

代码语言:javascript
复制
      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)
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-08-10 14:26:39

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

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

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

代码语言:javascript
复制
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)

结果:

代码语言:javascript
复制
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函数,它需要一个类型参数和一个普通参数。任何小费都欢迎。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38872242

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档