Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
716 views
in Technique[技术] by (71.8m points)

scala - Passing a Shapeless Extensible Record to a Function (continued)

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 :

  1. 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.
  2. 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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...