使用无形的多态映射函数

时间:2014-05-24 01:14:02

标签: scala shapeless hlist

我正在努力以类型安全的方式开展这项工作:

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

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

我确实知道光滑和其他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,那将是理想的,但这不是真正的要求。

关键部分是使结果的类型与params的类型匹配:

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

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


让我补充一点澄清。我想为任意arity创建一个方法,并避免为每个参数计数创建一个函数。如果我对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

然后我不需要无形或HList,因为所有可能的元组都会被明确定义。

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

1 个答案:

答案 0 :(得分: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,将输出转换为元组。