Passing a Shapeless Extensible Record to a Function (continued)

前端 未结 2 1169
梦谈多话
梦谈多话 2020-12-09 12:51

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 hav

相关标签:
2条回答
  • 2020-12-09 13:26

    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.

    0 讨论(0)
  • 2020-12-09 13:29

    The out type of a given field can be specified using:

    Selector[L, w1.T] { type Out = String }
    

    Also, we can slightly simplify the syntax using a type constructor:

    import shapeless._, ops.record.Selector, record._, syntax.singleton._
    
    val w1 = Witness("foo1")
    val w2 = Witness("foo2")
    val w3 = Witness("foo3")
    
    type HasFoo1[L <: HList] = Selector[L, w1.T] { type Out = String }
    type HasFoo2[L <: HList] = Selector[L, w2.T]
    type HasFoo3[L <: HList] = Selector[L, w3.T]
    
    @implicitNotFound("${L} should have foo1, foo2 and foo3")
    case class HasMyFields[L <: HList](implicit s1: HasFoo1[L], s2: HasFoo2[L], s3: HasFoo3[L])
    
    object HasMyFields {
      implicit def make[L <: HList : HasFoo1 : HasFoo2 : HasFoo3] = HasMyFields[L]
    }
    
    
    def fun1[L <: HList : HasMyFields](xs: L) = {
      val selectors = implicitly[HasMyFields[L]]
      import selectors._
      (xs("foo1").length, xs("foo2"), xs("foo3"))
    }
    
    fun1(("foo1"->> "hello") :: ("foo2" ->> 1)::("foo3" ->> 1.2)::HNil)
    
    // Does not compile: the value in foo1 is not a String
    fun1(("foo1"->> 2)       :: ("foo2" ->> 1)::("foo3" ->> 1.2)::HNil)
    
    0 讨论(0)
提交回复
热议问题