【问题标题】:Is it possible to (re)bind the receiver inside a block of code?是否可以(重新)将接收器绑定到代码块中?
【发布时间】:2013-01-31 09:23:30
【问题描述】:

问题

这个问题的动机是试图为this question寻找解决方案。

假设您想使用以下语法构建层次结构:

root {
  subA {
    subB("b1.1")
    subB("b1.2")
  }
}

构造 DSL 应该是类型安全的,也就是说,不能将 subB 直接嵌套在 root 中,或者将 subA 嵌套在另一个 subA 中。因此,我的想法是有一个方法root,它返回一个定义方法subA的对象,而后者又返回一个定义subB的对象。

我现在想要的是传递给root的代码块,也就是

  subA {
    subB("b1.1")
    subB("b1.2")
  }

的执行使得subB 的调用绑定到root 创建的对象。基本上是这样的

root { r: Root =>
  r.subA { sa: SubA =>
    sa.subB("b1.1")
    sa.subB("b1.2")
  }
}

但不必明确接收者 rsa

问题:是否可以在 Scala 中的代码块中重新绑定接收器,尤其是隐式的 this-receiver - 可能使用宏?

其他方法

This article 描述了一种外观相似的 XML 树构造 DSL。它们的实现基于Dynamic 功能,生成的 DSL 语法如下所示:

xml html {
  xml head {
    xml title "Search Links"
  }
}

但是,这种方法需要显式接收器(此处为对象 xml),更严重的是,我认为它不是类型安全的,因为它会静态阻止您嵌套 html 节点在title 节点内。

【问题讨论】:

    标签: scala dynamic compile-time scala-macros


    【解决方案1】:

    我不确定这是否可用,但它看起来与您编写的代码非常相似。我添加了, 并将{} 更改为()

    trait RootElement
    trait SubAElement
    
    def root(f:(() => RootElement)*) = ???
    def subA(f:(() => SubAElement)*):() => RootElement = ???
    def subB(s:String):() => SubAElement = ???
    
    root(
      subA (
        subB("b1.1"),
        subB("b1.2")
      )
    )
    

    我不确定你想如何使用它,如果你提供的话,我可以尝试根据用例制作一个更具体的版本。


    编辑

    在 Scala 中是否可以在代码块中重新绑定接收器,尤其是隐式 this-receiver - 可能使用宏?

    不,据我所知,这不可能开箱即用。您可能可以使用宏来做到这一点,但我无法帮助您。我对宏的有限了解告诉我,这将是一次冒险。

    为了在没有宏的情况下实现相同的效果,我创建了一个小示例。我已经删除了懒惰以使其更简洁。

    class Root {
      def subA(subA: SubA) = {
        // do something with subA
      }
    }
    
    class SubA {
      def subB(s: String) = {
        // do something with subB
      }
    }
    
    case class RootElement(value: SubA)
    case class SubAElement(value: String)
    
    def root(rootElems: (RootElement)*): Root = {
      val r = new Root
    
      rootElems foreach { sub =>
        r.subA(sub.value)
      }
      r
    }
    
    def subA(subElems: SubAElement*): RootElement = {
      val sA = new SubA
      subElems foreach { sub =>
        sA.subB(sub.value)
      }
      RootElement(sA)
    }
    
    def subB(s: String): SubAElement = SubAElement(s)
    
    root(
      subA(
        subB("b1.1"),
        subB("b1.2")
      )
    )
    

    【讨论】:

    • 这或多或少也是我想出的,请参阅我对上面问题中提到的问题的回答。在语法方面它确实与我在这里描述的很接近,但在实现方面它显然是完全不同的。无论如何,这绝对是迄今为止最好的答案:-)
    • 您能否在答案中添加您想要的最终对象结构的示例?然后,我可以帮助您弄清楚如何从这个结构转到另一个结构。
    • 如前所述(并由我的回答 stackoverflow.com/a/14608594/491216 见证)我知道如何使用您的方法来实现该已回答问题的 OP 想要的。我在这里感兴趣的是是否可以在代码块内/(匿名)函数内更改调用的接收者。
    • 对不起,您的解决方案只是(或多或少)重复了我已经发布的作为对其他问题的答案的内容。如果您不这么认为,请指出您的解决方案与我的解决方案有何不同。
    • 我明白你的意思。您能否尝试更清楚地说明您想要实现的目标?我感觉“重新绑定接收器”不是您想要实现的解决方案。更新后的答案反映了您的问题,因为 subA 现在执行为 (r:Root).subA
    猜你喜欢
    • 2021-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-29
    • 1970-01-01
    • 1970-01-01
    • 2020-09-29
    • 2021-10-06
    相关资源
    最近更新 更多