How important is whitespace inside parens in Ruby? - ruby

I just discovered that whitespace inside parens can matter in Ruby in an unexpected way:
Here are 3 functions which look semantically identical to me:
def foo(x)
return {
:a => (x - 100),
}
end
def bar(x)
return {
:a => (x
- 100),
}
end
def zot(x)
return {
:a => (x -
100),
}
end
However, foo(10) and zot(10) return {:a=>-90} (as I expected) while bar(10) returns {:a=>-100} (to my dismay and disappointment).
What am I missing here?

It's an unusual case here but I believe what you're seeing is Ruby interpreting that as several consecutive statements and not a single statement. As in it sees that as:
x # Statement 1
-100 # Statement 2
Where the result of that block of code is -100.
In the case of zot you've expressed your intent to continue that line on the next by having a dangling - binary operator:
x - # Statement 1
100 # Statement 1 (cont)
It's worth noting that you can't do this when making method calls:
zot(x
-100 # Syntax error
)
As in that case the argument syntax rules are a lot more strict. Inside a free-form (...) structure you have considerably more latitude.

In Ruby, there are two possible expression separators: semicolon ; and newline.
So,
a
b
is the same as
a; b
Which means that
a
- b
is the same as
a; - b
which is the same as
a; b.-#()
Of course, you expected it to be equivalent to
a - b
which is the same as
a.-(b)
The point is: each of those two interpretations is equally valid. The language designer has to choose one of those two interpretations, and in this case, they chose the first one. Neither of the two is more "right" or more "correct".
If they had chosen the second interpretation, then you wouldn't have asked this question, but someone else would have asked the opposite one.

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.

Void value expression error in return statement [duplicate]

This is just fine:
def foo
a or b
end
This is also fine:
def foo
return a || b
end
This returns void value expression:
def foo
return a or b
end
Why? It doesn't even get executed; it fails the syntax check. What does void value expression mean?
return a or b is interpreted as (return a) or b, and so the value of return a is necessary to calculate the value of (return a) or b, but since return never leaves a value in place (because it escapes from that position), it is not designed to return a valid value in the original position. And hence the whole expression is left with (some_void_value) or b, and is stuck. That is what it means.
In "Does `return` have precedence to certain operators in Ruby?" which I asked, Stefan explained in a comment that the or and and are actually control flow operators and should not be used as boolean operators (|| and && respectively).
He also referenced "Using “and” and “or” in Ruby":
and and or originate (like so much of Ruby) in Perl. In Perl, they were largely used to modify control flow, similar to the if and unless statement modifiers. (...)
They provide the following examples:
and
foo = 42 && foo / 2
This will be equivalent to:
foo = (42 && foo) / 2 # => NoMethodError: undefined method `/' for nil:NilClass
The goal is to assign a number to foo and reassign it with half of its value. Thus the and operator is useful here due to its low precedence, it modifies/controls what would be the normal flow of the individual expressions:
foo = 42 and foo / 2 # => 21
It can also be used as a reverse if statement in a loop:
next if widget = widgets.pop
Which is equivalent to:
widget = widgets.pop and next
or
useful for chaining expressions together
If the first expression fails, execute the second one and so on:
foo = get_foo() or raise "Could not find foo!"
It can also be used as a:
reversed unless statement modifier:
raise "Not ready!" unless ready_to_rock?
Which is equivalent to:
ready_to_rock? or raise "Not ready!"
Therefore as sawa explained the a or b expression in:
return a or b
Has lower precedence than return a which, when executed, escapes the current context and does not provide any value (void value). This then triggers the error (repl.it execution):
(repl):1: void value expression
puts return a or b
^~
This answer was made possible due to Stefan comments.
Simply because or has lower precedence than || which means return a will be executed before or b, or b is therefore unreachable

Implicit Return in Ruby Returns Errors When Returning Two Values

Consider the following two code examples:
1
# Implicit Return
def yo
'swag', 'yolo'
end
whaat, ya = yo
puts ya
puts whaat
2
# Explicit Return
def yo
return 'swag', 'yolo'
end
whaat, ya = yo
puts ya
puts whaat
The first example returns this error: syntax error, unexpected ',', expecting keyword_end and the 2nd example works like a champ.
So, this got me curious as I know there a lot of shortcuts(idioms) in Ruby ( e.g. 2 + 2 is actually 2.+(2) ) and if something is not working right always be clearer in your syntax and take the idioms out.
Thus, my question is how does Ruby handle returns behind the scenes as to me this shouldn't throw an error?
Methods (and blocks, and class definitions, and module definitions) implicitly return the last expression evaluated. a, b is simply not a valid expression, it isn't even valid syntactically. return a, b is a valid expression: you are passing two arguments to the return keyword. Passing more than one argument, and leaving out the parenthesis is perfectly legal.
For a similar reason, foo(a, b) works, but foo (a, b) doesn't: the former is passing two arguments to foo, the latter is passing one argument (a, b) to foo, which is syntactically invalid.
I think it might be
def yo
['swag', 'yolo']
end
It should work but it doesn't answer to your question

Why is `return a or b` a void value expression error in Ruby?

This is just fine:
def foo
a or b
end
This is also fine:
def foo
return a || b
end
This returns void value expression:
def foo
return a or b
end
Why? It doesn't even get executed; it fails the syntax check. What does void value expression mean?
return a or b is interpreted as (return a) or b, and so the value of return a is necessary to calculate the value of (return a) or b, but since return never leaves a value in place (because it escapes from that position), it is not designed to return a valid value in the original position. And hence the whole expression is left with (some_void_value) or b, and is stuck. That is what it means.
In "Does `return` have precedence to certain operators in Ruby?" which I asked, Stefan explained in a comment that the or and and are actually control flow operators and should not be used as boolean operators (|| and && respectively).
He also referenced "Using “and” and “or” in Ruby":
and and or originate (like so much of Ruby) in Perl. In Perl, they were largely used to modify control flow, similar to the if and unless statement modifiers. (...)
They provide the following examples:
and
foo = 42 && foo / 2
This will be equivalent to:
foo = (42 && foo) / 2 # => NoMethodError: undefined method `/' for nil:NilClass
The goal is to assign a number to foo and reassign it with half of its value. Thus the and operator is useful here due to its low precedence, it modifies/controls what would be the normal flow of the individual expressions:
foo = 42 and foo / 2 # => 21
It can also be used as a reverse if statement in a loop:
next if widget = widgets.pop
Which is equivalent to:
widget = widgets.pop and next
or
useful for chaining expressions together
If the first expression fails, execute the second one and so on:
foo = get_foo() or raise "Could not find foo!"
It can also be used as a:
reversed unless statement modifier:
raise "Not ready!" unless ready_to_rock?
Which is equivalent to:
ready_to_rock? or raise "Not ready!"
Therefore as sawa explained the a or b expression in:
return a or b
Has lower precedence than return a which, when executed, escapes the current context and does not provide any value (void value). This then triggers the error (repl.it execution):
(repl):1: void value expression
puts return a or b
^~
This answer was made possible due to Stefan comments.
Simply because or has lower precedence than || which means return a will be executed before or b, or b is therefore unreachable

Why does a range with invalid arguments sometimes not cause an argument error?

The following code causes an argument error:
n = 15
(n % 4 == 0)..(n % 3 == 0)
# => bad value for range (ArgumentError)
which I think is because it evaluates to:
false..true
and different types of classes are used in range: TrueClass and FalseClass. However, the following code does not raise an error. Why is that? Does Enumerable#collect catch it?
(11..20).collect { |i| (i % 4 == 0)..(i % 3 == 0) ? i : nil }
# => no error
Added later: If fcn returns 15, then only first half of range is evaluated
def fcn(x)
puts x
15
end
if (fcn(1) % 4 == 0)..(fcn(2) % 3 == 0); end
# => 1
but if we change return value to 16 then input will be
# => 1
# => 2
It's strange, because in this case expression evaluates to
true..false
And such kind of range is invalid according to sawa's answer below.
Then in first case (with def's return value 15) we have only partial range with no ending part? It's so strange :)
In Ruby if start..finish is a flip-flop, a special syntax for writing fast and obscure scripts. It is usually used in loops:
while input = gets
puts "Processing #{input.inspect}" if input =~ /start/ .. input =~ /end/
end
When the first condition is true, the whole condition is considered true on every consecutive execution until the second condition evaluates to true. You can play with the above script to get the idea. Here is my input & output:
foo
start
Processing "start\n"
foo
Processing "foo\n"
bar
Processing "bar\n"
end
Processing "end\n"
foo
bar
start
Processing "start\n"
Note that if the condition is not started Ruby doesn't evaluate the finishing condition because this is useless to do so.
Although it does not make much sense to use this outside of loops, Ruby is not restricting that.
>> if nil..raise; :nothing_gonna_happen; end
=> nil
First of all, note the following valid and invalid literals:
true..true # => valid
false..false # => valid
true..false # => invalid
false..true # => invalid
So the question reduces to why the expressions that evaluate to false..true and true..false become valid when embedded in a loop condition. According to documentation, a range literal in loop condition does not actually create a range. It rather has a special meaning akin to sed and awk. That is, the truth of the start of a range initiates the loop and truth of the end of the range terminates it. Some examples found here.

Resources