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