What's the precedence of method calls with and without parentheses?
Prelude
This aims to test all possible scenarios.
Note that when saying "operator X
has higher precedence than method invocation" what is meant is in arguments. Aka:
invocation foo X bar
as opposed to (call on object)
X invocation
As far as the second case is concerned, method calls always have higher precedence.
Short answer
It doesn't fit:
- It causes
SyntaxError
in some cases - It has higher precedence than
rescue
, but lower than assignment
Summary
not
can't be used after method invocation regardless of brackets- Using brackets (
()
) with method invocations sometimes causes aSyntaxError
. These cases are:and
,or
,if
,unless
,until
,while
andrescue
- In cases when brackets don't cause an error, they don't change the precedence in any way
- All operators, except for
and
,or
, postfixif
,unless
,until
,while
,rescue
have higher precedence than method invocation
Lets try it:
class Noone < BasicObject
undef_method :!
def initialize(order)
@order = order
end
def method_missing(name, *args)
@order << name
self
end
end
First unary:
# + and - will become binary
unary_operators = %i(! ~ not defined?)
puts 'No brackets'
unary_operators.each do |operator|
puts operator
order = []
foo = Noone.new order
bar = Noone.new order
begin
eval("foo.meta #{operator} bar")
rescue SyntaxError => e
puts e
end
p order
puts '-----------'
end
puts 'Brackets'
unary_operators.each do |operator|
puts operator
order = []
foo = Noone.new order
bar = Noone.new order
begin
eval("foo.meta(#{operator} bar)")
rescue SyntaxError => e
puts e
end
p order
puts '-----------'
end
Points taken:
not
after a method invocation is aSyntaxError
- all unary operators have higher precedence than method invocation regardless of brackets
Now binary:
binary_operators = %i(
**
* / %
+ -
<< >>
&
| ^
> >= < <=
<=> == === =~
.. ...
or and
)
puts 'No brackets'
binary_operators.each do |operator|
order = []
foo = Noone.new order
bar = Noone.new order
baz = Noone.new order
begin
eval("foo.meta bar #{operator} baz")
rescue SyntaxError => e
puts e
end
p order
end
puts 'Brackets'
binary_operators.each do |operator|
order = []
foo = Noone.new order
bar = Noone.new order
baz = Noone.new order
begin
eval("foo.meta( bar #{operator} baz)")
rescue SyntaxError => e
puts e
end
p order
end
Points taken:
- brackets around method invocation with
and
oror
is aSyntaxError
- we have to test
and
andor
further without brackets ..
and...
call<=>
. We have to test this further- we couldn't test a few other binary operators this way, namely
&&
,||
,==
,!=
, modifierrescue
,if
,unless
,until
,while
- other than the above mentioned, operators have higher precedence, regardless of brackets
def yes
puts 'yes'
true
end
def no
puts 'no'
false
end
def anything(arg)
puts 'Anything'
arg
end
anything yes and no
anything no or yes
anything yes && no
anything no || yes
anything(yes && no)
anything(no || yes)
anything yes == no
anything(yes == no)
anything yes != no
anything(yes != no)
Points taken:
and
andor
have lower precedence without brackets&&
,||
,==
and!=
have higher precedence regardless of brackets
def five(*args)
p args
5
end
five 2..7
five(2..7)
five 2...7
five(2...7)
Points taken:
..
and...
have higher precedence regardless of brackets
anything yes if no
anything(yes if no)
anything no unless yes
anything(no unless yes)
anything no until yes
anything(no until yes)
anything yes while no
anything(yes while no)
Points taken:
- brackets with
if
,unless
,until
,while
cause aSyntaxError
- all of the above have lower precedence than method invocation without brackets
def error
puts 'Error'
raise
end
anything error rescue yes
anything(error rescue yes)
Points taken:
- brackets around
rescue
cause aSyntaxError
rescue
has lower precedence if no brackets are present
Ternary:
anything yes ? no : 42
anything(yes ? no : 42)
Points taken:
- ternary has higher precedence regardless of brackets
Assignment (left for last as it changes yes
and no
):
anything yes = no
anything(no = five(42))
Points taken:
- Assignment has higher precedence than invocation
Note that +=
and the like are just shortcuts for +
and =
so they exhibit the same behaviour.
Precedence of operators in ruby
Because of Operator Precedence, you should either do:
if object && (object.is? ball)
or
if object && object.is?(ball)
The reason is "&&" and "||" have higher precedence than "and" and "or". Thus if you don't use parenthesis for the latter "object.is? ball", "object.is?" will be grabbed first by "&&". However, if you use "and" without parenthesis, it works fine, since it has lowest priority in this case:
if object and object.is? ball
Ruby operator precedence
If x
is integer, then x**2.to_s
is x.**(2.to_s)
. Because Fixnum#**
method exist. In your case **
is a method, not an operator.
In this case, no operator precedence and associativity comes into existence.
In the expression - x.**(2.to_s)
x
isFixnum
object.**
is a method called onx
.2.to_s
is simply an expression, which will be evaluated first, and passed as an argument to the method**
.
My answer is as per your example. But there are several edge cases in Ruby. Just to get the taste of those, give some time to read this thread why does a + f b
not parse?.
Are there Ruby precedence issues with using Proc.call vs. Proc.[]?
The #call
technique allows the operator precedence to potentially obscure intent:
p = Proc::new do |a1| Proc::new do |a2| "#{a1.inspect}:#{a2.inspect}" end end
p.call([1,2,3]).call [1]
=> => "[1, 2, 3]:[1]"
p.call [1,2,3][1]
=> #<Proc:0x7ffa08dc@(irb):1>
p.call([1,2,3])[1]
=> "[1, 2, 3]:1"
p[[1,2,3]][[1]]
=> "[1, 2, 3]:[1]"
The []
syntax makes the syntactic association of the arguments to the method more robust, but you'd achieve the same effect by putting parentheses around the arguments to Proc#call
.
Ruby precedence of methods in objects extended with multiple modules
method_added
is in Module
.
I actually meant included
, not extended
, but both are also in Module
.
The mechanism would revolve around doing something like this:
module Foo
def self.included(base)
base.extend(FooMethods)
end
module FooMethods
def bar
# Whatever
end
end
end
Inside Foo.included
you can determine, based on arbitrary criteria, whether or not the methods in question should be added to base
(the entity including the module).
In your case, you could check to see if a "higher priority" module was already included, or see if the module is the "higher priority" module. Based on that you'd decide whether or not to add the methods.
Related Topics
Heroku Not Sending Email With Gmail Smtp
String Interpolation in Ruby Doesn't Work
Best Practices to Handle Routes For Sti Subclasses in Rails
Iterate Through Every File in One Directory
Difference Between Print and Puts
How to Install Therubyracer Gem on 10.10 Yosemite
Rails Paperclip How to Delete Attachment
Haml: Append Class If Condition Is True in Haml
Extract a Substring from a String in Ruby Using a Regular Expression
How to Get Rid of Non-Ascii Characters in Ruby
Confusion With Atomic Grouping - How It Differs from the Grouping in Regular Expression of Ruby
Converting String from Snake_Case to Camelcase in Ruby
When to Use Each Method of Launching a Subprocess in Ruby
Nomethoderror When Trying to Invoke Helper Method from Rails Controller