将无形的可扩展记录传递给函数(续) [英] Passing a Shapeless Extensible Record to a Function (continued)
问题描述
考虑这个问题:将无形状可扩展记录传递给函数,Travis 的回答表明,每个将可扩展记录作为参数的函数都必须有一个隐式选择器作为参数.我想知道如果我们有很多这种功能,是否可以分解这些声明.例如.:
Considering this question : Passing a Shapeless Extensible Record to a Function, Travis's answer shows that every function taking an extensible record as parameter must have an implicit selector as parameter. I wonder if one could factorize those declarations in case we have many functions of this kind. E.g. :
val w1 = Witness("foo1")
val w2 = Witness("foo2")
val w3 = Witness("foo3")
//Here some "magical" declarations avoiding to declara selectors in fun1, fun2, fun3 below
def fun1[L <: HList](xs: L) = ... //Access to foo1, foo2, foo3
def fun2[L <: HList](xs: L) = ... //Access to foo1, foo2, foo3
def fun3[L <: HList](xs: L) = ... //Access to foo1, foo2, foo3
谢谢
贝努瓦
12 月 10 日编辑
在尝试答案的代码时,有两个问题:
When trying the code of the answer, on comes with two problems :
- 关于与 foo1, foo2, foo3 关联的数据的真实类型一无所知:因此,像 fun1 这样的函数不能使用与这些类型关联的任何方法.例如,即使 foo3 是一个 Double,它也不能取其平方根.
如果我用 ("foo1"->> "hello") :: ("foo2" -> 1)::("foo3" ->> 1.2)::HNiL 调用 fun1,结果是(hello, 1, 1.2) 带类型 (selectors.s1.Out, selectors.s2.Out, selectors.s3.Out)如果我尝试将 1 添加到最后一个值 (1.2),Scala 会抱怨它无法添加 Int 和 selectors.s3.Out ;但是如果我写:
- Nothing is told about the real type of the data associated to foo1, foo2, foo3 : consequently, a function like fun1 can't use any method associated to these types. e.g., even if foo3 is a Double, it can't take its squareroot.
If I call fun1 with ("foo1"->> "hello") :: ("foo2" -> 1)::("foo3" ->> 1.2)::HNiL, the result is (hello, 1, 1.2) with type (selectors.s1.Out, selectors.s2.Out, selectors.s3.Out) If I try to add 1 to the last value (1.2), Scala complains that it can't add an Int and a selectors.s3.Out ;but if I write :
val x = fun1(("foo1"->> "hello") :: ("foo2" -> 1)::("foo3" ->> 1.2)::HNil)
我可以写:
x._3 == 1.2
scala 回答正确!
and scala answers True!
我尝试以这种方式修改代码,希望能够传播类型,但并没有解决问题.我什至不能用 (foo1->> "hello") :: (foo2 -> 1)::(foo3 ->> 1.2)::HNil 作为参数调用 fun1 :
I have tried to modify the code in this way, hopping that the types would be propagated, but it doesn't solve the problem. I can't even call fun1 with (foo1->> "hello") :: (foo2 -> 1)::(foo3 ->> 1.2)::HNil as parameter :
object foo1 extends FieldOf[String]
object foo2 extends FieldOf[Int]
object foo3 extends FieldOf[Double]
val w1 = Witness(foo1)
val w2 = Witness(foo2)
val w3 = Witness(foo3)
case class HasMyFields[L <: HList](implicit
s1: Selector[L, w1.T],
s2: Selector[L, w2.T],
s3: Selector[L, w3.T]
)
object HasMyFields {
implicit def make[L <: HList](implicit
s1: Selector[L, w1.T],
s2: Selector[L, w2.T],
s3: Selector[L, w3.T]
) = HasMyFields[L]
}
def fun1[L <: HList](xs: L)(implicit selectors: HasMyFields[L]) = {
import selectors._
(xs(foo1), xs(foo2), xs(foo3))
}
有进步的方法吗?
贝努瓦
推荐答案
您可以定义自己的类型类来收集记录具有您需要的字段的证据:
You can define your own type class to gather the evidence that the record has the fields you need:
import shapeless._, ops.record.Selector, record._, syntax.singleton._
val w1 = Witness("foo1")
val w2 = Witness("foo2")
val w3 = Witness("foo3")
case class HasMyFields[L <: HList](implicit
s1: Selector[L, w1.T, String],
s2: Selector[L, w2.T, Int],
s3: Selector[L, w3.T, Double]
)
object HasMyFields {
implicit def make[L <: HList](implicit
s1: Selector[L, w1.T, String],
s2: Selector[L, w2.T, Int],
s3: Selector[L, w3.T, Double]
) = HasMyFields[L]
}
然后,例如:
def fun1[L <: HList](xs: L)(implicit selectors: HasMyFields[L]) = {
import selectors._
(xs("foo1"), xs("foo2"), xs("foo3"))
}
它仍然有点冗长,特别是因为导入是必要的,但比要求所有选择器单独作为隐式参数要少得多.
It's still a little verbose, especially since the import is necessary, but much less so than requiring all of the selectors individually as implicit parameters.
这篇关于将无形的可扩展记录传递给函数(续)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!