Precedence of and/or versus method arguments in ruby - ruby

Here are two tests:
if [1,2,3,4].include? 2 && nil.nil?
puts :hello
end
#=>
and
if [1,2,3,4].include?(2) && nil.nil?
puts :hello
end
#=> hello
The above tells me that && has higher precedence than method arguments so it logically ands 2 && nil.nil? which is true and passes that as an argument to include?.
However, there is this test:
if [1,2,3,4].include? 2 and nil.nil?
puts :hello
end
#=> hello
So this is telling me that method arguments and 'and' have the same precedence (or method args are higher than 'and') since it passed 2 to include? before it processed 'and'.
Note: I understand that && and and have different precedence. The question is not regarding this but regarding and or or vs the arguments to a ruby method.
I can't find documentation that affirms this. For instances, this doesn't mention method arguments at all: http://phrogz.net/programmingruby/language.html#table_18.4 or http://romhack.wikia.com/wiki/Ruby_operators.
Could anyone explain this behavior? Namely in that how does ruby know to pass values as arguments to a method vs. process operators?

As you said && and and have different precedence, however the explanation for the following example:
if [1,2,3,4].include? 2 and nil.nil?
puts :hello
end
#=> hello
is the binding strenght of the and as you can read here:
Difference between "and" and && in Ruby?
This basically explains that 2 and nil.nil? will be evaluated as nil, however it will return 2 as can be seen in this example:
foo = :foo
bar = nil
a = foo and bar
# => nil
a
# => :foo
a = foo && bar
# => nil
a
# => nil

I've never seen any documentation about method argument precedence, but one rule of thumb I use when seeing method arguments is to mentally strip the whitespace wherever possible in the arguments and still have the same expression. This normally gives me the precedence:
[1,2,3,4].include? 2&&nil.nil? is the same expression, but you cannot strip the whitespace in
[1,2,3,4].include? 2 and nil.nil? and therefore, the precedence is left to right ... I.e. Method argument is 2.
Anyway, the better question is why on earth would you write statements like this?
Omitting method parenthesis is only useful for code readability. However, your statements are hardly readable and makes one pause over the code and think about it more than he should. If I was to review code like this, I would definitely fail the code review due to poor readability.
In fact, many style guides explicitly state that most methods with arguments should be parenthesized (is this even a word ;). For example:
Ruby style guide

Related

Does Ruby safe navigation operator evaluate its parameters when its receiver is nil?

Question:
Does Ruby safe navigation operator (&.) evaluate its parameters when its receiver is nil?
For example:
logger&.log("Something important happened...")
Is the "Something important happened..." string evaluated here?
Could you provide an authoritative source, which proves or denies this fact?
Or suggest a way how to check it?
Thanks in advance.
Why I am looking for an answer to this question?
I have the code like the following throughout my codebase:
logger.log("Something important happened. (#{Time.current})") if verbose
My main goal is to remove the repetition of the if verbose check whenever I call the log method since it is easy to forget about it and you will be not notified at all about the misusage.
Inspired by the Tell, don't ask principle,
I have moved if verbose check inside log method implementation.
class Logger
# ...
def log(message)
return unless verbose
# ...
end
end
def logger
#logger ||= Logger.new
end
logger.log("Something important happened. (#{Time.current})")
This approach simplified my code since I have solved my main problem - I don't need to remember to place if verbose whenever I call the log method,
but I have received another issue.
"Something important..." string is always evaluated, no matter whether verbose is true or false.
Therefore, I have completely changed the solution:
logger returns nil when verbose is false.
Ruby safe navigation operator should be used in front of log calls.
def logger
#logger ||= Logger.new if verbose
end
logger&.log("Something important happened. (#{Time.current})")
As a result, I have replaced the initial problem of remembering if verbose checks to remembering of &. calls.
But, anyway, I consider this as an improvement, since forgetting to utilize the safe navigation operator raises the NoMethodError, in other words, notifies about the log method misusage.
So now, in order to be sure that the 'safe navigation operator approach' is actually a 'better' option for my problem,
I need to know exactly whether the safe navigation operator in Ruby evaluates its parameters when its receiver is nil.
To quote from the syntax documentation for the safe navigation operator:
&., called “safe navigation operator”, allows to skip method call when receiver is nil. It returns nil and doesn't evaluate method's arguments if the call is skipped.
As such, the arguments of your log method are not evaluated if the logger is nil when you call it as
logger&.log("something happened at #{Time.now}")
With that being said, note that the Ruby core logger offers a different solution to your exact issue, namely to avoid having to evaluate potentially expensive arguments if the log level is to high.
The Ruby core logger implements its add method something like this (simplified):
class Logger
attr_accessor :level
def initialize(level)
#level = level.to_i
end
def add(severity, message = nil)
return unless severity >= level
message ||= yield
log_device.write(message)
end
def info(message = nil, &block)
add(1, message, &block)
end
end
You can then use this as
logger = Logger.new(1)
logger.info { "something happened at #{Time.now}" }
Here, the block is only evaluated if the log level is high enough that the message is actually used.
Expression Parsed But Not Executed
The argument to logger&.log isn't evaluated when logger.is_a?(NilClass) == true. Every Ruby expression that's evaluated should have an impact, so consider:
test = 1
nil&.log(test+=1); test
#=> 1
If the argument were evaluated by the interpreter, test would equal two. So, while the parser certainly parses the expression in your argument, it doesn't execute the inner expression.
You can verify what the parser sees with Ripper#sexp:
require 'ripper'
test = 1
pp Ripper.sexp "nil&.log(test+=1)"; test
[:program,
[[:method_add_arg,
[:call,
[:var_ref, [:#kw, "nil", [1, 0]]],
[:#op, "&.", [1, 3]],
[:#ident, "log", [1, 5]]],
[:arg_paren,
[:args_add_block,
[[:opassign,
[:var_field, [:#ident, "test", [1, 9]]],
[:#op, "+=", [1, 13]],
[:#int, "1", [1, 15]]]],
false]]]]]
#=> 1
This clearly shows that the parser sees the incremented assignment in the symbolic expression tree. However, the assignment is never actually executed.
It does not evaluate them:
require 'pry'
logger = nil
logger&.log(binding.pry)
This returns:
nil
If it evaluated it then it would trigger the binding like this example does:
a = []
a&.push(binding.pry)
If you don't have pry but do have a modern version of Ruby you can substitute binding.irb for binding.pry.
Whether or not this is a "better" solution is something you should benchmark to be sure.
You can read more about the safe navigation operator at How is the Ruby safe navigation (&.) implemented?
No, and it's very easy to test:
$ irb
> def test
> puts 'triggered!'
> end
=> :test
> def nothing
> end
=> :nothing
> nothing&.whatever(test)
=> nil
> nothing&.whatever("string_#{test}")
=> nil
Conceptually you might think of safe navigation operator as this:
x&.test(param) # is "conceptually" equal to
if x.respond_to?(:test)
x.test(param)
end
# or, as pointed in the comment:
unless x.nil?
x.test(param)
end
And now it's pretty clear why it's not evaluated when it's not called.

Why `TrueClass === true` evaluates to `true` but `true === TrueClass` evaluates to `false`? [duplicate]

In Ruby, what is the difference between == and ===? The RDoc says
Case Equality—For class Object,
effectively the same as calling #==,
but typically overridden by
descendents to provide meaningful
semantics in case statements.
Is #== the same as ==? And could you provide an example of when/how this is used in case statements?
The two really have nothing to do with each other. In particular, #== is the equality operator and #=== has absolutely nothing to with equality. Personally, I find it rather unfortunate that #=== looks so similar to #==, uses the equals sign and is often called the case equality operator, triple equals operator or threequals operator when it really has nothing to do with equality.
I call #=== the case subsumption operator (it's the best I could come up with, I'm open to suggestions, especially from native English speakers).
The best way to describe a === b is "if I have a drawer labeled a, does it make sense to put b in it?"
So, for example, Module#=== tests whether b.is_a?(a). If you have Integer === 2, does it make sense to put 2 in a box labeled Integer? Yes, it does. What about Integer === 'hello'? Obviously not.
Another example is Regexp#===. It tests for a match. Does it make sense to put 'hello' in a box labeled /el+/? Yes, it does.
For collections such as ranges, Range#=== is defined as a membership test: it makes sense to put an element in a box labeled with a collection if that element is in the collection.
So, that's what #=== does: it tests whether the argument can be subsumed under the receiver.
What does that have to with case expressions? Simple:
case foo
when bar
baz
end
is the same as
if bar === foo
baz
end
Yes, by #== the docs mean "the instance method == of the current object".
=== is used in case statements as such:
case obj
when x
foo
when y
bar
end
Is the same as
if x === obj
foo
elsif y === obj
bar
end
Some classes that define their own === are Range (to act like include?), Class (to act like obj.is_a?(klass)) and Regexp (to act like =~ except returning a boolean). Some classes that don't define their own === are the numeric classes and String.
So
case x
when 0
puts "Lots"
when Numeric
puts(100.0 / x)
when /^\d+$/
puts(100.0 / x.to_f)
default
raise ArgumentError, "x is not a number or numeric string"
end
is the same as
if 0 == x
puts "Lots"
elsif x.is_a? Numeric
puts(100.0 / x)
elsif x =~ /^\d+$/
puts(100.0 / x.to_f)
else
raise ArgumentError, "x is not a number or numeric string"
end
Fun fact, === is also used to match exceptions in rescue
Here is an example
class Example
def self.===(exception)
puts "Triple equals has been called."
true
end
end
raise rescue Example
# => prints "Triple equals has been called."
# => no exception raised
This is used to match system errors.
SystemCallError.=== has been defined to return true when the two have the same errno. With this system call errors with the same error number, such as Errno::EAGAIN and Errno::EWOULDBLOCK, can both be rescued by listing just one of them.

What test Ruby perform on the `when` clause statement before calling `.===` operator?

Ruby uses === operator on the case/when type execution style.Now It also known that Ruby depending on the type
of the thing present in the when clause, calls the respective .=== method.
Say when statement contains the class names, then the rule is - it will use Module#===, which will return true if the right side is an instance of,
or subclass of, the left side. One example with this context is:
Here instance of test occurs
obj = 'hello'
#=> "hello"
case obj
when String
print 'It is a string'
when Fixnum
print 'It is a number'
else
print 'It is not a string'
end
#It is a string
#=> nil
Here subclass of test occurs
num = 10
#=> 10
case num
when Numeric
puts "Right class"
else
puts "Wrong class"
end
#Right class
#=> nil
Now when contains String literals then String#=== is called, which in turn checks if left and right handside
literal are same(same chracter in same sequence) or not.
a = "abc"
#=> "abc"
case a
when "def" then p "Hi"
when "abc" then p "found"
else "not found"
end
#"found"
#=> "found"
The all logic is too cool. Now my query is with case/when structure -
How does ruby know if when holding class, or String literals or
anything valid at runtime?
or
What test does it perform before calling the respective .===
operator on the thing when holding currently.
EDIT
Before understanding the Case/when working principal,let me clear the below which when does while it gets its turn.
String.===("abc") #=> true
Because "abc" is an instance of String class. - Am I right?
Now I tried the below just to check who is whose super class.
10.class #=> Fixnum
Fixnum.superclass #=> Integer
Integer.superclass #=> Numeric
Numeric.superclass #=> Object
Humm. That means the below returns true as Fixnum is also the indirect subclass of Numeric. - Am I right?
Numeric.===(10) #=> true
But why then the below outputs contradictory to the above?
Numeric.===(Fixnum) #=> false
Trying to be more specific to my query as below :
When we are calling Numeric.===(10) and String.===("abc") . I think we are sending not "abc" and 10 rather "abc".class and 10.class.
10.===(10) #=> true
Numeric.===(10) #=> true
Now look at the above. Both return true. Does they output true on the same logic? I think NO. 10.===(10) is just like
10 ==(10) comparison. But Numeric.===(10) outputs true as class of 10 is the subclass of Numeric.
"abc".===("abc") #=> true
String.===("abc") #=> true
Now look at the above. Both return true. Does they output true on the same logic? I think NO. "abc".===("abc") is just like simple string literal comparison "abc" ==("abc") comparison. But String.===("abc") outputs true as "abc" which is an instance of String.
Now my question is how ruby detects lefthand side operands types and apply the proper rule of comparisons ?
I might be 100% wrong, In that case please correct me.
I'll try to explain what #Lee Jarvis also explaines.
class Someclass
end
s = Someclass.new
p s.methods.sort
#[:!, :!=, :!~, :<=>, :==, :===, :=~, :__id__, :__send__, :class, :clone,(...)
Look at the 5th method. My Someclass instance has a ===method out of nowhere.
Actually, it has 56 methods and I did not define one of them. They are inherited from Object; every class in Ruby inherits from Object. For class Object (and my Someclass), #=== is effectively the same as calling #==, but (as the docs say) #=== is typically overwritten by descendants to provide meaningful semantics in case statements.
So ruby does nothing smart, it just sends the object in question a === message (or calls the ===method if you prefer that).
Now my question is how ruby detects lefthand side operands types and apply the proper rule of comparisons ?
It doesn't. It simply calls the === method. That's it. That's just how object-orientation works, and it is nothing specific to Ruby, every OO language works the same way: you call a method on an object, and the object decides how to react. (Or, in a class-based language like Ruby, PHP, Java, C#, C++, Python etc. the class of the object decides.)
Different objects may react in different ways. Classes check whether the argument is an instance of themselves, Regexps check whether the argument is matched by them, Ranges check whether the argument is covered by them.
It's just basic method dispatch.

Use of the bang?

Some people say that the bang ! indicates the method "may" modify the object.
In this example:
foo = "A STRING"
foo.downcase
puts foo
# => A STRING
foo.downcase!
puts foo
# => a string
the bang symbol appears to be causing a side effect. Is obj.method! equivalent to obj = obj.method? If not, why? Is there some method for which these two expressions are not equivalent?
The bang ! in a method name means "danger" in general.
It is a method naming convention, not an operator, not a rule.
It typically means warning, or mutation, or irreversable, or raises an exception, etc.
We often use "!" as a naming convention to distiguish two similar methods:
The normal method name returns a result.
The "!" method name does something dangerous.
Made up examples of "!" meaning mutation:
obj.change #=> return a new object that has the change.
obj.change! #=> do the change by mutating the object.
Made up examples of "!" meaning "raises an error":
obj.save #=> if the object can't save, then return false.
obj.save! #=> if the object can't save, then raise an exception.
Made up examples of "!" to mean "DANGER" or "cannot be undone":
obj.exit #=> exit normally, running all exit handlers.
obj.exit! #=> exit immediately, skipping all exit handlers.
All of these are just naming conventions, where the developer has chosen to provide two similarly-named methods.

Ruby !sub returning strange null

I don't understand the result of the first line. It's supposed to return a file name without extension if the file has one. Can somebody explain to me why it is like that and also tell me what would be a more proper here?
irb(main):003:0> 'fafeafeafewafeawfeaw'.sub!(/\.[^\.]*$/, '')
=> nil
irb(main):004:0> '.fafeafeafewafeawfeaw'.sub!(/\.[^\.]*$/, '')
=> ""
irb(main):005:0> 'fafeafeafewafea.wfeaw'.sub!(/\.[^\.]*$/, '')
=> "fafeafeafewafea"
It is documented that the sub! (like many of the ! string operations) return nil if no change was made.
From the docs
Performs the substitutions of String#sub in place, returning str, or nil if no substitutions were performed.
Instead use the regular sub. In your case the extra bang (!) is unnecessary.
'fafeafeafewafeawfeaw'.sub(/\.[^\.]*$/, '')
Bang Methods
The difference between sub and sub! is subtle. But in ruby in general, the non bang (!) version of a method is safer. Since by convention the bang means the method has more side affects.
In the case of string functions (and many array/enumerable functions) the bang means the method operates on the contents of the caller, instead of making (and returning) a copy.
s = 'fafafa'
puts s #=> 'fafafa'
puts s.sub(/fa/, 'fo') #=> 'fofofo'
puts s #=> 'fafafa'
puts s.sub!(/fa/, 'fo') #=> 'fofofo'
puts s #=> 'fofofo'

Resources