在隐式接收器上调用*方法

时间:2015-12-30 14:02:22

标签: ruby dsl

我想创建下一个DSL部分:

* 'Ruby'

下一个实验显示了可预测的结果:

def *(a)
  a
end

* 'Ruby'         #=> SyntaxError: (irb):4: syntax error, unexpected '\n', expecting :: or '[' or '.'
self.*(1)        #=> NoMethodError: private method `*' called for main:Object
self.send(:*, 1) #=> 'Ruby'

我认为Ruby语法分析器将这种表达式视为星号方法的splat运算符或语法糖。有人可以解释这个更详细的(如果可能的话)提供解决方案来解决我的问题吗?

2 个答案:

答案 0 :(得分:5)

那是不可能的。有些方法在没有显式接收器的情况下根本无法调用,因为否则它们会模糊不清。特别是,在你同时拥有一元前缀和二元中缀运算符的每种情况下,都不可能区分这两种情况:

+ a == a.+@()
- a == a.-@()
& a # interpreted as the unary prefix proc-block conversion operator
* a # interpreted as the unary prefix splat operator

或在运算符与其他语法冲突的情况下:

/ a # interpreted as the start of a Regexp literal

为了保持一致性,Ruby强制为所有二进制中缀运算符使用两个操作数,而不仅仅是语法不明确的操作符。

同样,如果没有显式接收器,则无法调用[],因为它会与数组的文字语法冲突。

另一个众所周知的没有显式接收器就无法调用的方法示例是setters:

self.foo = bar # setter
foo = bar      # local variable

对于private setter,private方法的规则实际上有一个例外,它允许使用显式接收器调用它们,只要该显式接收器是文字伪变量即可关键字self。但是,调用私有二进制运算符或[]之类的方法不存在此类异常。 (尽管如此,仍有提案。)

答案 1 :(得分:2)

JörgWMittag回答为什么你的第一个表达式* 'Ruby'(没有明确的接收者)会引发错误。

我将添加为什么你的第二个表达式self.*(1)(带有显式接收器)会引发错误。这是因为该方法是在main对象上定义的,默认情况下都是私有的。

这使得调用方法的唯一方法既没有显式接收器,也没有显式接收器,换句话说,你不能用普通方法调用的形式调用该方法。您只能以描述该方法的符号形式传递它,而让另一种方法(send)执行它。