Ruby method chaining with blocks - confusingly inconsistent syntax error - ruby

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.

Related

Operator Precedence and Order of Evaluation in Ruby: yield vs << (shovel operator)

I'm reading this book Well-Grounded Rubyist and its Control-Flow Techniques chapter on p.184 has a simple example of implementing map with an iterator:
class Array
def my_map
c=0
acc = []
until c == size
acc << yield self[c]
c += 1 end
acc
end
end
I have the following error when run this code ruby MY_FILE.rb:
MY_FILE.rb:6: syntax error, unexpected `self', expecting `end'
acc << yield self[c]
Solution to this problem is to put brackets around yield self[c], so this whole line would look like this:
acc << (yield self[c])
Then, the routine works with this one-line change. In my understanding the problem is either with operator precedence or with order of evaluation. I surfed the web for quite a while but could not pinpoint why exactly the code from the book does not work.
Why the book example does not work? What is operator precedence or/and order of evaluation in acc << yield self[c]?
Usually, method parameters come in parentheseis:
x=foo(y)
a=bar(baz(z))
b=7+baz(w)
In certain situations (i.e. if precedences does not bind otherwise), you can leave out the braces. Therefore,
x = foo y
works, but
a = bar baz z
is interpreted as
a = (bar(baz))(z)
and
b = 7 + baz w
as
b = (7+baz) w
This is risky, if the interpretation of the expression results in something meaningful. In this case, you would not even get an error message, but the program would simply behave in a different way than you expected.
In general, it is a good idea to always use parenthesis when invoking methods.

How do method-calls interact with operator precedence?

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.

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.

Last expression evaluated in Ruby

I have this class definition:
class Test
attr_accessor :state
def multiple_state=(times)
#state *= times
end
end
obj = Test.new
obj.state = 2
puts #{obj.multiple_state=4}
I thought the output is 8, coz that is the value of the last
expression evaluated in multiple_state. (?)
But the output is 4.
Is my understanding of last expression evaluated wrong?
Thanks.
Ruby's syntactic sugar for setter methods always returns the right side of the assignment, even if you do something else in your method. The Well-Grounded Rubyist puts it better than I could:
Setter methods don’t return what you might think. When you use
the syntactic sugar that lets you make calls to = methods that look like assignments,
Ruby takes the assignment semantics seriously. Assignments (like x = 1)
evaluate to whatever’s on their right-hand side. Methods usually return the
value of the last expression evaluated during execution. But = method calls
behave like assignments: the value of the expression ticket.price = 63.00 is
63.00, even if the ticket= method returns the string "Ha ha!". The idea is to
keep the semantics consistent. Under the hood, it’s a method call; but it looks
like an assignment and behaves like an assignment with respect to its value as
an expression.
The Well-Grounded Rubyist - Chapter 3.3.3

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