Passing a shapeless extensible record to a function (never ending story?

后端 未结 2 499
旧时难觅i
旧时难觅i 2021-02-09 15:10

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

相关标签:
2条回答
  • 2021-02-09 15:20

    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.

    0 讨论(0)
  • 2021-02-09 15:39

    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?

    0 讨论(0)
提交回复
热议问题