将无形的可扩展记录传递给函数(永无止境的故事?
Passing a shapeless extensible record to a function (never ending story?
我继续研究 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 那样声明尽可能多的 class,因为它们是可能的组合(如果我们有 n 个字段,就有 2**n 种组合!)?
如果没有额外的类型 class 使用新的 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)
新语法允许我们避免单独创建 witness 值,因此只需要您需要的 Selector
个实例非常容易。
从 Travis 的回答开始,我已经能够改进它:
在第一个文件中,我有:
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] )
有什么建议吗?
我继续研究 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 那样声明尽可能多的 class,因为它们是可能的组合(如果我们有 n 个字段,就有 2**n 种组合!)?
如果没有额外的类型 class 使用新的 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)
新语法允许我们避免单独创建 witness 值,因此只需要您需要的 Selector
个实例非常容易。
从 Travis 的回答开始,我已经能够改进它:
在第一个文件中,我有:
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] )
有什么建议吗?