【问题标题】:Polymorphic mapping function using shapeless使用 shapeless 的多态映射函数
【发布时间】:2014-07-21 00:50:20
【问题描述】:

我正在尝试以类型安全的方式进行这项工作:

val rows = db.select( ID_COLUMN, STR("name"), INT("count") ). from("tablename") ......
for ( (id, name, count) <- rows ) {
       //some code to use the individual values
}

到目前为止,除了无形之外,我还没有找到另一种使这种类型安全的方法。

我知道 slick 和其他 ORM,所以请不要把我送到那里。

HList 似乎是一种传入异构对象的方法,并返回具有特定类型的值列表。

我试图做这样的事情:

 trait ColDef [X] {
     def colName
     def valalue (m:Map[String, Object]):X
 }

 object XS extends ColDef[String]{
     def colName = "s"
     def value (m:Map[String, Object]) = m("s").asInstanceOf[String] 
 }

 object XI extends ColDef[Integer]{
     def colName = "i"
     def value (m:Map[String, Object]) = m("i").asInstanceOf[Integer] 
 }

 val xhl = XS::XI::HNil
 val data:Map[String,Object] = Map(("s" ->"asdf"), ("i" -> new Integer(5)))

 object p1 extends Poly1 {
    implicit def getValue[T, S <% ColDef[T]] = at[S] (coldef => coldef.value(data) )
 }

 val res = xhl map p1 
 val (s, i) = res.tupled //this works, giving s:String, and i:Integer

 //but following does not compile
 def nextstep(hl : HList, data:Map[String,Object]) = {
     hl map p1
 }

只是重申什么是必不可少的:

HList/Shapeless 可能是解决问题的候选对象,但不是本练习的目标。我的目标是让函数的返回类型与传入的变量类型和参数个数相对应。

如果我的小实用程序的用户不需要了解 HList,那将是理想的,但这不是真正的要求。

最重要的部分是让结果的类型与参数的类型相匹配:

val param1 = new Conf[String]
val param2 = new Conf[Integer]
 ... etc ....
val res = function(param1, param2, param3) 

更准确地说是上述参数的有效载荷类型,因此 res 的类型是 T(String, Integer, ....) 。


让我再补充一点。我想为任意数量创建一个方法,并避免为每个参数计数创建一个函数。如果我对 22 种方法没问题,它看起来像这样:

def f[A](a:ColDef[A]):(A)
def f[A,B](a:ColDef[A], b:ColDef[B]):(A,B)
def f[A,B,C](a:ColDef[A], b:ColDef[B],c:ColDef[C]):(A,B,C)
 ..... and so on

然后我不需要 shapeless 或 HList,因为所有可能的元组都将被显式定义。

实际上查看这 3 个签名 - 将它们设为 22 会花费一些时间,但会避免无形的依赖,它们的实现也将是单行的。也许我应该只花 30 分钟手动完成(或使用小脚本)。

【问题讨论】:

  • 不确定我是否完全理解这些问题,但您是否想要这样的东西:def function[A,B,C](p1: ColDef[A], p2: ColDef[B], p3: ColDef[C]) = p1::p2::p3::HNil
  • @VolkerStampa 不,我认为您的函数签名不是我想要的。如果可以接受固定的数量,那么我就不需要 HList,因为我可以定义返回适当大小的元组的每个函数。

标签: scala shapeless hlist


【解决方案1】:

让它与 HList 输入和输出一起工作

你只需要对nextstep的定义做一些小的调整:

def nextstep[L <: HList](hl: L, data: Map[String, Object])(implicit mapper: Mapper[p1.type, L]): mapper.Out = {
  hl map p1
}

我将 HList L 的确切类型设置为类型参数,并且我需要 map 所需的隐式(参见 map 的定义)。

然后您(或您的用户)可以简单地调用

nextstep(XS :: XI :: HNil, data)

他们会得到一个String :: Integer :: HNil 作为返回类型。它对ColDef[...] 的任何 HList 都按预期工作(返回结果的 HList)。

让它使用元组作为输入(和输出 HList)

为了让它返回一个元组而不是一个 HList,你可以这样定义它:

import shapeless.ops.hlist.{Tupler, Mapper}
def nextstep[L <: HList, OutL <: HList, Out](hl: L, data: Map[String, Object])(implicit mapper: Mapper.Aux[p1.type, L, OutL], tupler: Tupler.Aux[OutL, Out]): Out = {
  tupler(hl map p1)
}

nextstep(XS :: XI :: HNil, data) 将返回一个(String, Integer),而nextstep 将在一般情况下返回正确类型的元组。

元组作为输入和输出

接受ColDef 元组作为输入并返回元组作为输出的最后一步如下所示:

def nextstep[P, L <: HList, OutL <: HList, Out](c: P, data: Map[String, Object])(implicit gen: Generic.Aux[P, L], mapper: Mapper.Aux[p1.type, L, OutL], tupler: Tupler.Aux[OutL, Out]): Out = {
  tupler(gen.to(c) map p1)
}

这里的逻辑和shapeless.syntax.std.TupleOps中定义的函数非常相似:将元组转换为HList,处理HList,将输出转换为元组。

【讨论】:

    猜你喜欢
    • 2021-06-09
    • 1970-01-01
    • 1970-01-01
    • 2018-10-25
    • 2021-02-13
    • 2015-09-12
    • 2013-03-11
    • 1970-01-01
    相关资源
    最近更新 更多