【问题标题】:Passing a shapeless extensible record to a function (never ending story?将无形的可扩展记录传递给函数(永无止境的故事?
【发布时间】:2016-02-01 14:37:17
【问题描述】:

我继续研究Passing a Shapeless Extensible Record to a Function (continued) 中的可扩展记录:提供的解决方案适用于所有接受参数的函数,该参数至少包括 foo1、foo2 和 foo3;那就是可以写:

fun1(("foo1" ->> "hello") :: ("foo2" ->> 1) :: ("foo3" ->> 1.2) :: HNil)
fun1(("foo1" ->> "hello") :: ("foo2" ->> 1) :: ("foo3" ->> 1.2) :: ("foo4" ->> true) :: HNil)

等等

如果我们可以添加第二个函数 fun2:

def fun2[L <: HList : HasMyFields](xs: L) = {
  val selectors = implicitly[HasMyFields[L]]
  import selectors._
  xs("foo1").length + xs("foo2") + xs("foo3")
}

到目前为止,一切都很好。

现在,假设我们有一组字段 foo1, foo2,... 和一组函数 fun1, fun2,它们将由 {foo1, foo2,...} 的任何子集组成的记录作为参数。例如,fun1 可以将包含 foo1 和 foo3 但不一定包含 foo2 的记录作为参数,fun2 会期望至少包含 foo4 的记录等等。

有没有办法避免尽可能多地声明 HasMyFields 之类的类,因为它们是可能的组合(如果我们有 n 个字段,则有 2**n 个组合!)?

【问题讨论】:

    标签: scala shapeless


    【解决方案1】:

    如果没有使用新式 Witness 语法的额外类型类,这会容易得多:

    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))
    

    如果我们有这个记录:

    val rec = ("foo1" ->> "hello") :: ("foo2" ->> 1) :: ("foo3" ->> 1.2) :: HNil
    

    我们得到这个:

    scala> fun1(rec)
    res0: (String, Double) = (hello,1.0)
    

    新语法允许我们避免单独创建见证值,因此很容易只需要您需要的 Selector 实例。

    【讨论】:

    • 你绝对没有价值!非常感谢!
    【解决方案2】:

    从特拉维斯的回答开始,我已经能够改进它:

    在第一个文件中,我有:

    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]
    }
    

    然后,在其他地方:

    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));
    }
    

    第一个文件可以被认为是一个模式,我在其中声明了我可能在我的应用程序中使用的字段并且我只是将其导入。

    太棒了!

    6 月 30 日编辑: 我想知道我们是否可以做得更好,也许用一个宏: 能不能写这样的:

     def fun1[L <:HList] WithSelectors(MyFields)=...
    

    WithSelectors 宏会生成:

     (implicit foo1: wfoo1[L], foo2: wfoo2[L] )
    

    有什么建议吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-17
      • 1970-01-01
      • 2017-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-25
      • 1970-01-01
      相关资源
      最近更新 更多