【问题标题】:Is there a way to create dot-free dsl in scala with two identifiers between variables?有没有办法在 scala 中使用变量之间的两个标识符创建无点 dsl?
【发布时间】:2013-12-21 22:52:20
【问题描述】:

有没有办法定义一个 dsl,允许以下形式?

variable identifier identifier variable

例如:

1 equals to 2

我知道如何创建一个更简单的表单:1 equals to (2),但我想避免使用括号。有办法吗?

【问题讨论】:

    标签: scala


    【解决方案1】:

    你可以问解析器:

    $ scala -Xprint:parser
    Welcome to Scala version 2.9.2 ... blah
    
    scala> variable1 identifier1 identifier2 variable2
    // lots of stuff and inside:
    val res0 = variable1.identifier1(identifier2).variable2
    // this is how the parser sees it.
    // if you can make that work (dynamic classes…?), you’re good to go.
    

    但是,有一个问题:这只有在variable2 是标识符时才有效(以便它可以用作方法名称)。与

    scala> 1 equals to 2
    

    解析器已经失败:

    <console>:1: error: ';' expected but integer literal found.
           1 equals to 2
                       ^
    

    括号确实是你唯一的方法(*):

    scala> 1 equals to (2)
    // ...
    val res1 = 1.equals(to(2))
    

    (*) 除非您将 2 与反引号一起使用作为标识符

    scala> 1 equals to `2`
    // ...
    val res2 = 1.equals(to).2
    

    ……不,也许不是。

    【讨论】:

    • 啊,解析器输出的好技巧。我相信这将再次被证明是方便的。
    【解决方案2】:

    据我所知,目前不可能按照您的意愿实施(我很高兴被证明是错误的)。当我创建用于注入的小型 DSL 时,我也遇到了这个问题。但是我已经意识到,即使两个标识符之间不能有 2 个变量,它们之间仍然可以有三个标识符。它看起来像这样:

    variable1 method constant method variable2
    

    等同于:

    variable1.method(constant).method(variable2)
    

    有了这个,我能够想出看起来像这样的非常好的 DSL:

    by default defaultDb and identified by 'remote
    'remote is by default defaultDb
    

    您可以在本规范中找到更多使用示例:

    https://github.com/OlegIlyenko/scaldi/blob/48f7e4186cf7eb441116087003d7f45f16e0ac6c/src/test/scala/scaldi/InjectableSpec.scala#L47

    它的实现可以在这里找到:

    https://github.com/OlegIlyenko/scaldi/blob/master/src/main/scala/scaldi/Injectable.scala

    我使用ByWordIdentifiedWord 类类型作为方法参数。例如:

    def is(by: ByWord) = and(by)
    

    这留下了可能性,图书馆的用户将扩展ByWorld 类,并且通常可以提供他们自己的ByWorld 实现。现在回想起来,我觉得这不是很好。更好的解决方案是为常量词创建单例对象,然后像这样使用它们的类型:

    object by
    def is(byWord: by.type) = and(by)
    

    这通常将参数限制为只有一个可能的 by 单词实例。

    【讨论】:

    • 是的,我知道三标识符解决方案:)
    【解决方案3】:

    Unary Operators

    如果最后一个是一元运算符,你可以有两个连续的标识符:

    a identifier1 - d
    

    被解析为

    a.identifier1(d.unary_-)
    

    请注意,一元函数只能是 ! ~ 和 -

    Dynamic methods

    如果您不介意将您的最后一个变量名视为字符串(例如,您可以稍后从地图或interpreter itself 中检索其值),您可以使用动态进行以下操作:

    import scala.Dynamic
    import scala.language.dynamics
    
    class I2
    class Res(i: I2) extends Dynamic {
      def selectDynamic(obj: String): Unit = {
          println(obj)
        }
    }
    class V1 { def i1(i2: I2): Res= new Res(i2) }
    val v1 = new V1
    val i2 = new I2
    
    v1 i1 i2 v2
    

    产生字符串“v2”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-12
      • 1970-01-01
      • 2020-08-04
      • 2015-08-02
      相关资源
      最近更新 更多