为什么方法调用在原则上可以是常量时需要消除歧义?

时间:2016-01-07 14:22:40

标签: ruby methods syntax constants

方法调用通常可以省略接收器和参数的括号:

def foo; "foo" end
foo # => "foo"

在上面的例子中,foo在方法调用和对潜在局部变量的引用之间是不明确的。如果没有后者,则将其解释为方法调用。

但是,当方法名称原则上可以是常量名称时(即,当它以大写字母开头,并且只包含字母时),它似乎需要消除歧义。

def Foo; "Foo" end
Foo # => NameError: uninitialized constant Foo
Foo() # => "Foo"
self.Foo # => "Foo"

为什么会这样?为什么即使没有具有相同名称的常量,也需要明确区分方法调用与对常量的引用?

3 个答案:

答案 0 :(得分:5)

在程序中任何给定点的范围内的局部变量集是词法定义的,因此可以静态确定,即使早在解析时间。因此,Ruby甚至在运行时之前就知道哪些局部变量在范围内,因此可以区分消息发送和局部变量解除引用。

首先通过词法查找常量,然后通过继承查找,即动态地查找常量。在运行时之前,不知道哪些常量在范围内。因此,为了消除歧义,Ruby总是假定它是一个常量,除非它显然不是,即它需要参数或有接收器或两者兼而有之。

答案 1 :(得分:2)

差异背后没有大的理由。我只是希望foo表现得像foo(),如果范围内没有局部变量foo。我认为它对创建DSL等很有用。但我没有理由让Foo表现得像Foo()。

答案 2 :(得分:0)

你问了一个很好的问题。正如您所指出的,ruby希望将其视为常量,因此可以进行常量查找。

然而,以下代码段显示了当前行为,然后通过修改const_missing,您似乎获得了所需的行为。说实话,我似乎无法打破任何事情。

我的结论是,这是有人已经建议的,只是一个设计决定,但它很奇怪,因为一般来说,红宝石有利于惯例与执法。

或者我错过了一些事情让事情变得混乱而且发生了错误的事情。



<script type="text/ruby">
def puts(s); Element['#output'].html = Element['#output'].html + s.to_s.gsub("\n", "<br/>").gsub(" ", "&nbsp;") + "<br/>"; end

class ImAClass
  def self.to_s
    "I am ImAClass Class"
  end
end

def ImAMethod
  "hello"
end

class DontKnowWhatIAm
  def self.to_s
    "a Class"
  end
end

def DontKnowWhatIAm
  "a method"
end

puts "ImAClass: #{ImAClass}" 

begin 
  puts "ImAMethod: #{ImAMethod}" 
rescue Exception => e
  puts "confusion! #{e.message}"
end

puts "ImAMethod(): #{ImAMethod()}"

puts "DontKnowWhatIAm: #{DontKnowWhatIAm}"

puts "DontKnowWhatIAm(): #{DontKnowWhatIAm()}"

class Module
  alias_method :old_const_missing, :const_missing
  def const_missing(c)
    if self.respond_to? c
      self.send c
    else
      old_const_missing(c)
    end
  end
end

class Foo
  def self.Bar
    "im at the bar"
  end
end

puts "now we can just say: Foo::Bar and it works! #{Foo::Bar}"
 
</script>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rawgit.com/reactive-ruby/inline-reactive-ruby/master/inline-reactive-ruby.js"></script>
<div id="output" style="font-family: courier"></div>
&#13;
&#13;
&#13;