How do method-calls interact with operator precedence? - ruby

I am curious about how method calls relate to operator precedence. In irb, I see this:
var = puts(5)
5
=> nil
var
=> nil
This implies that the call to puts has higher precedence than the assignment operator, since nil (the return value of puts(5)) is assigned to var, rather than the method call itself. Because nil is assigned to var (as we can see on line 4), I would guess that puts(5) was called before the assignment operator.
In this Stackoverflow thread, everybody agrees that method-calls have lower precedence than every operator.
However this website lists the . as an operator for method-calls, and says that it is the highest-precedence operator.
If this second website is indeed accurate, I'm unsure about whether there is an implicit . operator when you call a method on main (and therefore about whether . being a high-precedence operator is sufficient to explain the irb session above).
In general, I'm curious about the order in which Ruby does things when it encounters a line of code, so if you know of any resources that explain that in an accessible way I would be interested in reading them.
EDIT: thanks for answers so far. Maybe I wasn't clear enough about my basic questions, which are theoretical not practical (so are arguably 'overthinking', depending on how much you like to think):
is . technically an operator, or technically not an operator?
is there a . somewhere behind the scenes every time you call a method?
are operators the basic way that Ruby decides in what order it will evaluate a line of code, or are there factors other than operators and their precedence/associativity/arity?
Thanks

You're overthinking this. Your expression is basically this: x = something. So, right-hand side must be evaluated first, then the assignment can be done.

Here is how to print AST
2.6.3 :008 > RubyVM::AbstractSyntaxTree.parse('x = puts(5)')
=> #<RubyVM::AbstractSyntaxTree::Node:SCOPE#1:0-1:11>
2.6.3 :009 > pp _
(SCOPE#1:0-1:11
tbl: [:x]
args: nil
body:
(LASGN#1:0-1:11 :x
(FCALL#1:4-1:11 :puts (ARRAY#1:9-1:10 (LIT#1:9-1:10 5) nil))))
=> #<RubyVM::AbstractSyntaxTree::Node:SCOPE#1:0-1:11>
I'm using ruby 2.6. This way is possible to solve any parsing doubt. For this case is little obvious as other answers said if you have x = expr, then expr need to be evaluated first since we're talking about a strict language, for lazy languages you will only need to evaluate expr when x is evaluated, but this is another topic

I'm guessing you come from a JavaScript or similar background. Where the following is possible:
function puts(...args) { args.forEach(arg => console.log(arg)); }
var x;
(x = puts)(5);
puts(x);
However in JavaScript calling puts without () will return the whole function. Which allows easy function assignment. However in Ruby calling puts without () will still call the method. Making parentheses optional. See the Calling Methods documentation.
In Ruby (x = puts)(5) would result in a syntax error. You can achieve the same by doing the following:
(x = method(:puts)).call(5)
# here parentheses are still required since
x = method(:puts).call(5)
# will still assign the result of the puts call to x
The first link you provided talking about operators having a higher precedence than method calls is talking about method arguments.
puts 5 + 5
# can be seen as
(puts 5) + 5
# or
puts (5 + 5)
In this case 10 is printed since the operators have higher precedence than the method call itself. This also works for the = operator, but when used as argument.
puts x = 5
Will print 5, return nil and have 5 assigned to x. When using x = puts 5, x can't be assigned without evaluating puts 5 so that is what happens first. Precedence only comes into play if the same code could be executed in multiple ways.
Calling methods with parentheses never yields the above issue.
puts(5 + 5)
# or
puts(5) + 5
Both speak for themself. Although the latter will raise a NoMethodError.

Related

Ruby Proc syntax usage

my_proc = proc{|x| "this is #{x}"}
given my_proc, what makes the following syntax work?
my_proc.call("x") # makes sense
my_proc.("x") # not really sure but ok
my_proc["x"] # uhhh....
my_proc === "x" # what the deuce?!
About ===:
http://ruby-doc.org/core-2.2.0/Proc.html#method-i-3D-3D-3D
proc === obj → result_of_proc
Invokes the block with obj as the proc's parameter like #call. It is
to allow a proc object to be a target of when clause in a case
statement.
That means you can use it in case statements, like this:
odd = proc { |x| x % 2 != 0 }
even = proc { |x| x % 2 == 0 }
case 1
when odd then 'odd'
when even then 'even'
end
# => "odd"
Ruby often has several syntaxes for the same method, to best fit the develloper needs.
my_proc === "x" : http://ruby-doc.org/core-2.2.0/Proc.html#method-i-3D-3D-3D
-> this one is said to be useful in case statements (#Marek_Lipka explained it further in his answer)
my_proc["x"] : http://ruby-doc.org/core-2.2.0/Proc.html#method-i-5B-5D -> This one is said to be "syntax sugar", hiding the method's name for a more compact syntax.
Since you are specifically asking about the syntax, this has nothing to do with Procs. Ruby doesn't allow objects to change the syntax of the language, therefore it doesn't matter what kind of objects we are talking about.
my_proc.call("x")
This is just standard message sending syntax. It sends the message call with the argument "x" to the object returned by evaluating the expression my_proc.
You are asking "what makes this syntax work". Well, this is just how message sending is specified in the Ruby Language Specification.
my_proc.("x")
This is syntactic sugar for my_proc.call("x"), i.e. exactly what we had above: sending the message call with argument "x" to the result of evaluating my_proc.
If you want to make this work for your objects, you need to respond to call.
This syntax was added in Ruby 1.9 to make calling a "function-like object" look more like sending a message, with the only difference being the additional period character. Note that Ruby is not the only language using this syntax, elixir uses it as well.
my_proc["x"]
This is syntactic sugar for my_proc.[]("x"), i.e. sending the message [] with argument "x" to the result of evaluating my_proc.
If you want to make this work for your objects, you need to respond to [].
Proc#[] was added as an alias_method of Proc#call, so that calling a "function-like object" looks more like sending a message, with the only difference being the shape of the brackets. With the addition of the .() syntax sugar in Ruby 1.9, I generally prefer that one.
my_proc === "x"
This is syntactic sugar for my_proc.===("x"), i.e. sending the message === with argument "x" to the result of evaluating my_proc.
If you want to make this work for your objects, you need to respond to ===.
This was added so that Procs could be used as conditions in case expressions and in Enumerable#grep, both of which use === to determine whether or not an object could be subsumed unter a category.

Why does irb echo the right hand side of an assignment instead of the return value in the case of a setter method?

It seems surprising that in all other instances irb will echo the return value of a method. Why does assignment via a setter behave differently?
I'm using Ruby 2.2.2.
irb(main):001:0> def x=(value); puts "puts_from_x"; "returned_string_from_x"; end
=> nil
irb(main):002:0> self.x = 3
puts_from_x
=> 3
update
It has dawned on me that it echoes the rhs because that's the actual return value. Why is this?
Following the #Matz reply in this thread :
Setters always return the value they were originally assigned
It's a design choice. We defined the value of the assignment as the
value of the right hand expression, not the return value from the
assigning method.
Haven't found a concrete answer why, but this page as a discussion about it.
Specifically:
If you define an « assignment-like » method (with an equal sign at the end), Ruby will execute the method when you call it, but will always return the supplied parameter and never the result of the method.
Think about something like this, that many people would be used to seeing from other languages like C:
foo.x = foo.y = 3
You'd assume foo.x and foo.y were both set to 3, and because of this Ruby "feature", you'd be right.
edit: Arup's post has a good link to a why...

What is the purpose of |element| in Ruby array operations syntax?

[1,2,3,4,5,6,7].delete_if{|i| i < 4 }
For example, in the above, why do you need to put |i| before i < 4?
I'm new to Ruby programming and the purpose of this element escapes me.
This is very basic Ruby syntax for a block. A block can sometimes take parameters which are given between the bars |. In your case:
[1,2,3,4,5,6,7].delete_if { |i| i < 4 }
The delete_if method for the type Array accepts a block as a parameter. When the bock is given, the block accepts the array element as a parameter. So it iterates i over each value within the array in this case. More specifically, an element will be deleted from the array if that element is < 4. The result will be:
[4,5,6,7]
You'll often see documentation for methods for Ruby types which say, for example:
delete_if { |item| block } -> Array
Which means that the method accepts a block with a parameter, the block being some code that uses the parameter, and the output being another array. The method's description explains more detail in the documentation (e.g., Ruby Array).
I recommend reading some Ruby getting started information online or a good introductory book which will explain this in more detail.
You have to put i there for the same reason you would put i in the first line here:
def plus_one(i)
return i + 1
end
You have to name your method argument, which you later use as a local variable in the method.
Ruby blocks are similar to methods, they can also receive arguments, and syntax for declaring them is slightly different: enclosing them in | |.
I've redone my answer, even though the OP's question has already been answered, because I thought of a new way to explain this that may help future SO users with the same question.
From high school algebra, you should remember functions like this: f(x) = x + 1.
Imagine putting curly braces around the x + 1: f(x) = { x + 1 }
Then move the (x) to inside the curly braces: f = {(x) x + 1 }
And then get rid of the name f: {(x) x + 1 }. This makes it an "anonymous function," i.e. a "lambda."
Here's the problem: The braces could contain arbitrary statements, which may themselves use parentheses: (x + 1) * 4. So how would Ruby know that the (x) is supposed to be an argument to the function, and not an expression to execute? Some other syntax had to be used. Hence the vertical bars: |x|. (At least I assume that was the thought process).
So {|i| i > 4 } is just like f(i) = i > 4, except that it has no name and is not defined in advance, so the parameter has to be defined "inside" the function itself, rather than being outside attached to the name.
Array#delete_if expects such a function (called a "block" when it's used like this) and knows what to do with it. It passes each member of the array [1,2,3,4,5,6,7] into the block as the argument i to see whether i > 4 is true for that member. It's equivalent to doing something like this:
def greater_than_four(x)
x > 4
end
arr = [1,2,3,4,5,6,7]
arr.each do |el|
arr.delete(el) if greater_than_four(el)
end
You could avoid defining the greater_than_four method in advance by defining a lambda on the fly like this:
arr = [1,2,3,4,5,6,7]
arr.each do |el|
arr.delete(el) if lambda{|i| i > 4}.call(el)
end
But since Array#delete_if already expects a block, and already knows to call it on each element, you can save yourself a whole lot of code:
[1,2,3,4,5,6,7].delete_if{|i| i < 4 }
The parameter which you are passing to the delete_if method is a block and the thing inside the parameter you pass to the block.
Think of the block as a method of sorts. The delete_if method iterates over the block and passes the current item as the parameter i to the block. If the condition evaluates to true then that element gets deleted.

Ruby method chaining with blocks - confusingly inconsistent syntax error

So, thinking I'm all clever, I add a method like this to Object:
class Object
def apply_if(cond)
if cond
yield self
else
return self
end
end
end
This (I thought) allows my to conditionally add bits to a method chain, which simplifies my ActiveRecord query manipulation quite a bit. But it gives a syntax error, which I can reduce down to the following code:
data = [1,2,3,4,5]
results = data.
apply_if(true and false) do |q|
q + [0,0]
end
Likewise this errors:
results = data.apply_if(true and false){|q| q + [0,0]}
But this works:
results = data.apply_if ((true and false)) {|q| q + [0,0]}
As does:
results = data.apply_if (true && false) {|q| q + [0,0]}
I see that the differences there are all to do with operator precendence, but how can the precedence of an operator inside a pair of parentheses matter?
Why is there a syntax error here at all? I don't see any likely syntactic ambiguities, and this method is identical in shape to the Array#reduce method.
I've tried a number of combinations here - explicit block parameters with calls, various types of explicit precedence inside the method definition. Using a lambda instead of a block worked fine, but is obviously too clunky to use for my purposes.
This has nothing to do with any of your code — the and operator just isn't allowed in an argument list. As for why, you'd have to ask the Core team, but the most obvious possibility IMO is that its raison d'etre (having extremely low precedence) doesn't work in an argument list.

ruby, define []= operator, why can't control return value?

Trying to do something weird that might turn into something more useful, I tried to define my own []= operator on a custom class, which you can do, and have it return something different than the value argument, which apparently you can't do. []= operator's return value is always value; even when you override this operator, you don't get to control the return value.
class Weird
def []=(key, value)
puts "#{key}:#{value}"
return 42
end
end
x = Weird.new
x[:a] = "a"
output "a:a"
return value => "a" # why not 42?
Does anyone have an explanation for this? Any way around it?
ruby MRI 1.8.7. Is this the same in all rubys; Is it part of the language?
Note that this behavior also applies to all assignment expressions (i.e. also attribute assignment methods: def a=(value); 42; end).
My guess is that it is designed this way to make it easy to accurately understand assignment expressions used as parts of other expressions.
For example, it is reasonable to expect x = y.a = z[4] = 2 to:
call z.[]=(4,2), then
call y.a=(2), then
assign 2 to the local variable x, then finally
yield the value 2 to any “surrounding” (or lower precedence) expression.
This follows the principle of least surprise; it would be rather surprising if, instead, it ended up being equivalent to x = y.a=(z.[]=(4,2)) (with the final value being influenced by both method calls).
While not exactly authoritative, here is what Programming Ruby has to say:
Programming Ruby (1.8), in the Expressions section:
An assignment statement sets the variable or attribute on its left side (the lvalue) to refer to the value on the right (the rvalue). It then returns that value as the result of the assignment expression.
Programming Ruby 1.9 (3rd ed) in section 22.6 Expressions, Conditionals, and Loops:
(right after describing []= method calls)
The value of an assignment expression is its rvalue. This is true even if the assignment is to an attribute method that returns something different.
It’s an assignment statement, and those always evaluate to the assigned value. Making this different would be weird.
I suppose you could use x.[]= :a, "a" to capture the return value.

Resources