Passing a Shapeless Extensible Record to a Function

前端 未结 2 932
南方客
南方客 2020-12-13 14:56

I am trying to learn Shapeless (using version 2.10.2). I have created a very simple extensible record:

val rec1 = (\"foo\" ->> 42) :: HNil

相关标签:
2条回答
  • 2020-12-13 15:37

    As of shapeless 2.1.0 there is a new syntax to express record types:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    import shapeless._
    import shapeless.record._
    import shapeless.syntax.singleton._
    
    def fun(x: Record.`"foo" -> Int`.T) = x("foo")
    
    // Exiting paste mode, now interpreting.
    
    import shapeless._
    import shapeless.record._
    import shapeless.syntax.singleton._
    fun: (x: shapeless.::[Int with shapeless.labelled.KeyTag[String("foo"),Int],shapeless.HNil])Int
    
    scala> fun( ("foo" ->> 42) :: HNil )
    res2: Int = 42
    
    scala> fun( ("foo" ->> 42) :: ("bar" ->> 43) :: HNil )
    <console>:30: error: type mismatch;
     found   : shapeless.::[Int with shapeless.labelled.KeyTag[String("foo"),Int],shapeless.::[Int with shapeless.labelled.KeyTag[String("bar"),Int],shapeless.HNil]]
     required: shapeless.::[Int with shapeless.labelled.KeyTag[String("foo"),Int],shapeless.HNil]
           fun( ("foo" ->> 42) :: ("bar" ->> 43) :: HNil )
    

    But the Selector is probably the best approach for OP's use-case.

    0 讨论(0)
  • 2020-12-13 15:43

    Here's something a little more general that I think might answer your question. Suppose we want to write a method that will work on any record with a "foo" key. We can use a combination of a witness and a selector:

    import shapeless._, record._, syntax.singleton._
    
    val w = Witness("foo")
    
    def fun[L <: HList](xs: L)(implicit sel: ops.record.Selector[L, w.T]) = xs("foo")
    

    And then:

    scala> fun(("foo" ->> 42) :: HNil)
    res0: Int = 42
    

    Or:

    scala> fun(("bar" ->> 'a) :: ("foo" ->> 42) :: HNil)
    res1: Int = 42
    

    If we really wanted to only allow records with no other fields, we could write the following:

    def fun(l: Int with KeyTag[w.T, Int] :: HNil) = l("foo")
    

    But that's somewhat at odds with the way records are generally used.

    We have to define the witness precisely because Scala 2.10 doesn't provide any way to refer to a singleton type directly—see for example my fork of Alois Cochard's Shona project for some discussion.

    I will add as a final disclaimer that I'm only just now getting familiar with Shapeless 2.0 myself, but I don't think even Miles is magical enough to get around this limitation.

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