Passing a Shapeless Extensible Record to a Function (continued)
scala, shapeless
Solution
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]
}
And then, for example:
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.
Problem
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 ``` Thanks Benoit edit on Dec 10 When trying the code of the answer, on comes with two problems : - 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) ``` I can write : ``` x._3 == 1.2 ``` and scala answers True! 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)) } ``` Is there a way to progress? Benoit