1

假设我正在设计一种特定领域的语言。对于这个简化的示例,我们有变量、数字以及将事物相加的能力(变量或数字)

class Variable
  attr_reader :name

  def initialize(name)
    @name = name
  end
end

class Addition
  attr_reader :lhs, :rhs

  def initialize(lhs, rhs)
    @lhs = lhs
    @rhs = rhs
  end
end

现在,如果我想表示表达式x + 1,我写Addition.new(Variable.new('x'), 1)

我们可以通过提供一个+on 方法来使这更方便Variable

class Variable
  def +(other)
    Addition.new(self, other)
  end
end

然后我们就可以写了Variable.new('x') + 1

但是现在,假设我想要相反的结果:1 + x. 显然,我不想猴子补丁Integer#+,因为这会永久禁用普通的 Ruby 整数加法。我认为这将是一个很好的改进用例。

具体来说,我想定义一个方法expr,它接受一个块并在+重新定义的上下文中评估该块以构造我的 DSL 的实例。也就是说,我想要类似的东西

module Context
  refine Integer do
    def +(other)
      Addition.new(self, other)
    end
  end
end

def expr(&block)
  Context.module_eval(&block)
end

因此,理想情况下,expr { 1 + Variable.new('x') }将导致 DSL 表达式Addition.new(1, Variable.new('x'))

然而,Ruby 的细化似乎是非常善变的,并且module_eval' 进入具有活动细化的范围并不会像我希望的那样激活块内的细化。有没有办法使用module_eval,instance_eval等来激活特定 Ruby 块内的细化?

我意识到我可以将整数包装在一个IntegerExpr类中并提供+它。然而,这是 Ruby,元编程的极限是天空,所以我很好奇它是否可以用普通的 RubyInteger实例来完成。我想定义一个方法expr,这样,在

expr { 1 + Variable.new('x') }

+块内部是一个细化定义的,Integer#+即使该细化在 的调用站点上未激活expr。这可能吗?

4

0 回答 0