将无形的可扩展记录传递给函数(永无止境的故事?

时间:2015-06-07 12:39:54

标签: scala shapeless

我继续研究Passing a Shapeless Extensible Record to a Function (continued)中的可扩展记录:提供的解决方案适用于所有采用至少包含foo1,foo2和foo3的参数的函数;这是可以写的:

fun1(("foo1" ->> "hello") :: ("foo2" ->> 1) :: ("foo3" ->> 1.2) :: HNil)
fun1(("foo1" ->> "hello") :: ("foo2" ->> 1) :: ("foo3" ->> 1.2) :: ("foo4" ->> true) :: HNil)

等等

如果我们可以添加第二个函数fun2:

def fun2[L <: HList : HasMyFields](xs: L) = {
  val selectors = implicitly[HasMyFields[L]]
  import selectors._
  xs("foo1").length + xs("foo2") + xs("foo3")
}

到目前为止,非常好。

现在,让我们假设我们有一组字段foo1,foo2,...和一组函数fun1,fun2,它们将{foo1,foo2,...的任何子集组成的记录作为参数。 ..}。例如,fun1可以将包含foo1和foo3的记录作为参数,但不一定是foo2,fun2会期望记录至少包含foo4,依此类推。

有没有办法避免声明像HasMyFields这样多的类,因为它们是可能的组合(如果我们有n个字段,则有2 **组合!)

2 个答案:

答案 0 :(得分:6)

如果没有使用new-ish Witness语法的其他类型类,这会容易得多:

import shapeless._, ops.record.Selector, record._, syntax.singleton._

// Uses "foo1" and "foo2" fields; note that we get the appropriate static types.
def fun1[L <: HList](l: L)(implicit
   foo1: Selector.Aux[L, Witness.`"foo1"`.T, String],
   foo2: Selector.Aux[L, Witness.`"foo2"`.T, Int]
): (String, Double) = (foo1(l), foo2(l))

然后如果我们有这个记录:

val rec = ("foo1" ->> "hello") :: ("foo2" ->> 1) :: ("foo3" ->> 1.2) :: HNil

我们得到了这个:

scala> fun1(rec)
res0: (String, Double) = (hello,1.0)

新语法允许我们避免单独创建见证值,因此只需要您需要的Selector个实例就很容易。

答案 1 :(得分:2)

从特拉维斯的答案开始,我已经能够改进它:

在第一个档案中,我有:

package p2;
import shapeless._, ops.record.Selector, record._, syntax.singleton._

object MyFields {

  type wfoo1[L<: HList]=Selector.Aux[L,Witness.`"foo1"`.T, String]

  type wfoo2[L<: HList]=Selector.Aux[L,Witness.`"foo2"`.T, Int]
}

然后,在其他地方:

package p1;
import shapeless._, ops.record.Selector, record._, syntax.singleton._
import p2.MyFields._
object testshapeless extends App {

  def fun1[L <: HList](l: L)(implicit
   foo1: wfoo1[L],
   foo2: wfoo2[L]
  ): (String, Double) = (foo1(l), foo2(l))

  val rec = ("foo1" ->> "hello") :: ("foo2" ->> 1) :: ("foo3" ->> 1.2) :: HNil

  println(fun1(rec));
}

第一个文件可以被视为一个模式,我在其中声明了我可能在我的应用程序中使用的字段,而我只是要导入它。

太酷了!

6月30日编辑: 我想知道我们是否可以做得更好,也许用宏: 是否有可能写出如下内容:

 def fun1[L <:HList] WithSelectors(MyFields)=...

宏WithSelectors将生成:

 (implicit foo1: wfoo1[L], foo2: wfoo2[L] )

有什么建议吗?