I continue to investigate extensible records as in Passing a Shapeless Extensible Record to a Function (continued): the provided solution works with functions that all takes a p
This is a lot easier without an additional type class with the new-ish Witness
syntax:
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))
And then if we have this record:
val rec = ("foo1" ->> "hello") :: ("foo2" ->> 1) :: ("foo3" ->> 1.2) :: HNil
We get this:
scala> fun1(rec)
res0: (String, Double) = (hello,1.0)
The new syntax allows us to avoid creating witness values separately, so it's pretty easy to just require the Selector
instances you need.
Starting with the answer of Travis, I've been able to improve it :
In a first file, I have :
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]
}
and then, elsewhere :
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));
}
The first file can be considered like a schema where I declare the fields that I potentially use in my application and I've just to import it.
That's cool!
Edited on June 30 : I wonder if we could do better, maybe with a macro : Would it be possible to write something like :
def fun1[L <:HList] WithSelectors(MyFields)=...
The macro WithSelectors would generate :
(implicit foo1: wfoo1[L], foo2: wfoo2[L] )
Any advice?