1

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

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,那将是理想的,但这不是真正的要求。

重要的部分是让结果的类型与 params 的类型匹配:

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 分钟手动完成(或使用小脚本)。

4

1 回答 1

1

Have it work with HList input and output

You just need some minor adjustments to the definition of nextstep:

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

I made the exact type of the HList L a type argument, and I required the implicit needed by map (see map's definition).

Then you (or your user) can simply call

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

and they will get a String :: Integer :: HNil as return type. It works as expected for any HList of ColDef[...] (returning an HList of the result).

Have it work with a tuple as input (and output HList)

In order to have it return a tuple instead of an HList, you can define it this way:

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) will then return a (String, Integer), and nextstep will return the rightly typed tuple in the general case.

Tuple as input and output

The final step to accept a tuple of ColDef as input and return a tuple as output looks like:

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)
}

The logic here is very similar to the functions defined in shapeless.syntax.std.TupleOps: converting the tuple to an HList, processing the HList, convert the output to a tuple.

于 2014-07-19T19:23:21.953 回答