Using || in arguments in Ruby? - ruby

I just saw this code:
method_name(ARGV.shift || "somefile.txt")
which basically should first evalute ARGV[0] and if it doesn't exist then read from "somefile.txt".
My question was, can the && operator be also used here, and in what situations?
Also, does this concept of "passing this or this argument" to a method in Ruby have a name?

The short answer, logical operators are going to return one of its operands based on if it's truthy or falsey. In practice, nearly everything in Ruby will evaluate to true except false or nil.
Examples:
expr1 || expr2 will return (expr1 if not falsey) or (expr2)
In other words, the first truthy value or the final operand,
expr1 && expr2 will return (expr1 if falsey) or (expr2).
In other words, the first falsey value or the final operand.
As for an actual use case, a similar example would be:
Using the && operator to check for a flag in ARGV then passing the file name.
method_name(ARGV.include?('-w') && "write_file.txt")
It should be noted that this is probably not a widely accepted practice. (see comments)
However, preferring a user supplied value over a default value, by using ||, in this manner would be.

If && is used, then the argument would be nil when there is no ARGV[0] and "somefile.txt" when there is ARGV[0]. Note that elements of ARGV, if any, would be strings, so there is no possibility of ARGV[0] being nil or false when there is an element passed.
Generally, || and && are called "(short circuit) disjunction" and "(short circuit) conjunction", respectively.
A typical use case of || is to provide a default value:
foo = potentially_falesy_value || default
A typical use case of && is to provide a value that depends on the truthness of another value:
foo = hash[:bar] && hash[:bar][:baz]

|| is using for providing default values. || returns first "true" value. "True" value -- value that is interpreted as true boolean value in ruby. So first "true" value in the chain will be as the result of the expression. && returns first "false" value. Complete analogy. But it does not have such graceful application.

Apart from the obvious Boolean operator functionality, && can be used like you could in some languages such as JavaScript:
a = cond1 && cond2 && value # a is now value if cond1 and cond2,
# else nil or false (depends on cond1 and cond2)
It's not very readable (IMHO) when assigning non-Boolean variables, but it works.

first evalute ARGV[0] and if it doesn't exist then read from "somefile.txt". My ques
You are correct. The ARGV.shift || "somefile.txt" expression will evaluate to ARGV.shift if it returns some non-falsy value, and "somefile.txt" otherwise. Some other examples:
puts nil || "foo" # => "foo"
puts "foo" || "bar" # => "foo"
puts "foo" || nil # => "foo"
puts "foo" || raise # => "foo"
# and doesn't blow up, because the 'raise'
# is never evaluated
can the && operator be also used here
Sure, but is arguably of less practical value. It might be clearer to use an if in that case:
puts foo && bar
# is the same as:
if foo
puts bar
end
# or:
puts bar if foo
does this concept of "passing this or this argument to a method in Ruby" has a name?
I'm not sure if it has any 'official' name, but I commonly see this pattern being called 'default value' or 'fallback value'.

Related

Why does an empty String returns false when comparing to boolean in ruby?

It might be a dumb question, but can someone please explain this:
if ""
true
else
false
end
=> true (OK)
!!("")
=> true (OK)
"" === true
=> false (OK)
"" == true
=> false (But why?)
Have a nice day!
The basic concept of this question is a deep misunderstanding of Ruby operators. Here's the short of it - there's no such thing as operators in Ruby! All this !, =, == and === that you throw around - these are not operators.
So what's going on?
Ruby is an object oriented language (a real one, not like that fake Java and JavaScript things) and all these fancy character sequences you think of as operators are actually method calls on the object in their left:
== method usually checks for equality - is the object on the right equal in content.
=== method is not used for "strict equality" or "identity" like it is often in other languages - some Ruby objects that implement it use it for "membership" tests, like in ranges ((1..3) === 2 ==> true) or regular expressions (/el/ === "hello" ==> true you can think of regular expressions as the group of all strings that would match), others implement it as equals.
So - how is the if working? Well, if and other "forced boolean contexts" check for "falsehood". In Ruby we recognize two false values - false and nil, everything else will run the truth branch of an if. This is actually the method ! that you also used in your second example. This method is implemented in BasicObject to return true for all objects, except if the object's type is the type of false - FalseClass or the type for nil - NilClass, in which case it returns true.
So your examples actually mean:
Check for the truthiness of "". Because that value's type is neither FalseClass or NilClass, it is always true.
For the value "", call the method ! - which will return false because the object is neither a FalseClass or a NilClass - then call ! on the result, which will return true because false is a FalseClass instance.
For the value "" call the method === with the argument true, but since it is an alias for == - call that instead (see 4).
For the value "" call the method == with the argument true. String's implementation of == will never return true for an argument that isn't a String type.
The only falsy values in Ruby are false and nil. Everything else is "truthy," but not necessarily equal to true. This includes empty objects like a String, Hash, or Array.
Pragmatically, it might help to think of == as "comparably equivalent to" rather than "equals." For example:
1 == 1.0 #=> true
This is true even though one is an Integer and one is a Float because they are comparably equivalent in value, even if they aren't the same object or of the same type.
In the same way, "" is truthy because it is not comparably equivalent to false or nil. However, it's also not the same object type as true, nor comparably equivalent to true. An empty String is simply "not falsy," which makes it truthy but not actually true.
Remember, only false and nil are falsy. Everything else, and I mean everything, is truthy even if it isn't strictly speaking true.
In Ruby, the only "falsey types" are FalseClass and NilClass, which have the instances false and nil respectively. All other values are considered "truthy". This is possibly different to what you'd expect coming from other C-like values, in which we do things like this pretty freely:
int x = get_value();
if (x) { /* implied x != 0 }
So, if you had something like this in Ruby:
puts 0 if 0 # => "0"
puts 1 if "" # => "1"
puts 2 if [] # => "2"
puts 3 if false # => nil
puts 4 if true # => "4"
puts 5 if nil # => "5"
So, if "" acts truthy, why isn't it equal to true? If that were how we defined ==, then this would also need to resolve to true then, since both values are truthy:
"1" == "2"
The difference here is that == is asking if two things are the same, which "" and true are most certainly not. Further, Ruby does not automatically convert types for you (like other languages like JavaScript do), so "" does not automatically get converted to a boolean during its comparison.

Precedence of to-block unary `&`

Consider the following Ruby code:
[1,3].any? &:even? || true
# => false
[1,3].any? &nil || :even?
# => false
[1,3].any? &nil || :odd?
# => true
So it seems that Boolean-or || has higher precedence than to-proc unary &. I didn't expect this. Is that right, and is it documented anywhere?
This is what the (wrongly-maligned) and and or keywords are for. You're supposed to write that as
[1,3].any? &:even? or true
As for why this happens--I can't find documentation for this--but I think it actually has more to do with optional parentheses and the restrictions of unary &.
Unary & is special. "Normal" operators like ~ are essentially syntactic sugar over method calls; you can put them wherever you want. But & is only allowed in method arguments, and even then only at the end.
foo x, &bar
# NameError, determined at runtime because it has to see if any of these names are defined
foo &bar, x
# SyntaxError! Didn't even make it past the parser
y = bar
# NameError
y = &bar
# SyntaxError!
And when you leave parentheses out from a method call, it slurps up pretty much everything, stopping only at super-low-precedence stuff like if/unless/and/or.
foo bar baz if true
# same as
foo(bar(baz)) if true
So your example is equivalent to
[1,3].any?(&:even? || true)
Now if & were somehow high-precendence this is either a totally normal value to be evaluated at runtime true or it's a highly-restricted special syntactic construct &:even?. It's not great to discover syntax errors at runtime, so I guess the devs chose to solve it the easy way: make & super low precedence. That way the parser can just verify the syntax rules and ignore the block argument itself (which has to be evaluated at runtime).
When the & is not preceded by an object (unary) and is immediately followed by by anything else (&nil in this case). It will be parsed as an attempted to_proc method call otherwise it will be treated as a method call on the receiver e.g.
nil&nil
#=> false
Is equivalent to nil.&(nil)
So in your case ([1,3].any? &nil || :even?) it is being parsed as [1,3].any?(&(nil || :even?)) because & (to_proc method call) has a lower precedence than logical || and needs to know the result of nil || :even? to proceed.
However, operational and (&) requires a receiver (not unary) but does hold a higher precedence than the logical or (||) e.g. [1,3].any? &nil&nil || :even? which would evaluate to
[1,3].any?(&(nil.&(nil) || :even?)
#=> false
# Or
[1,3].any?(&(nil & nil || :even?)
#=> false
Where strangely enough it is clear that operational and (&) has a higher precedence than logical or (||)
Further examples
true & nil || :even?
#=> :even?
true & :even?
#=> true
[1,3].any? &true&true || :even?
#=> TypeError: wrong argument type TrueClass (expected Proc)
# Okay no problem
class TrueClass
def to_proc
->(*_) { true }
end
end
[1,3].any? &true&true || :even?
#=> true
[1,3].any? &true || :even?
#=> true
Even stranger is that the unary & allows for nil but essentially ignores it from block_given? but any other object that does not implement to_proc raises a TypeError
def no_block
block_given? ? yield('Yes') : 'No'
end
no_block &nil
#=> "No"
no_block &false
#=> TypeError: wrong argument type FalseClass (expected Proc)

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.

Negating expressions

Is there any difference between these two statements:
! (name == "bob")
and
name != "bob"
To me, it seems like they both do the same thing. How are they different, if at all?
They are almost the same.
! (name == "bob") calls two methods ! and ==. you can write it like name.==('bob').!.
name != "bob" calls only one method !=. name.!=('bob').
Unless you redefine !=, you don't have to worry about these two options. In most cases, they are the same.
Here is an example how you can break it:
name = 'bob'
def name.!= s
true
end
name.!=('bob') # => true
name.==('bob').! # => false
You may expect the first result to be false because the 'bob' on the left side (variable name) equals 'bob' on the right side, but it does not because the method is redefined.
Those would return the same result in any example I could think of, but placing the ! before the expression can be helpful when checking other things (e.g. !my_array.include?("bob").

Why does Ruby expression with double ampersand using a return statement cause a syntax error

def foo
true && return false
end
def bar
true and return false
end
foo method causes a syntax error. bar doesn't. Why?
Assuming I want to evaluate one-liners with a return statement (similar to a certain commonly used shell/bash expression), would this be a recommended way to do it, or is there a more recommended approach?
By operator associativity strength,
true && return false
evaluates to
(true && return) false
of which
true && return
is valid, but the false after it is invalid. You cannot have two statements lined up without anything in between.
Side Note
It is worth noting that and and && are not equivalent.
and is a flow control operator while && is a Boolean operator. What is the difference?
One example of the differences, using and, you can assign values.
value = nil and value / 42
This would fail if you tried it as below
value = nil && value / 42
Original Question 1
Assuming I want to evaluate one-liners with a return statement (similar to a certain > commonly used shell/bash expression), would this be a recommended way to do it, or is there > a more recommended approach?
The way I have always seen this done in Ruby is this:
value if conditional
This will return the value if the conditional is met and nil otherwise. No need for the return statement if this is the last command in the function!
If you are using this for an early exit to a function, I prefer using an unless. For instance...
return unless arguments_are_valid
Original Question 2
foo method causes a syntax error. bar doesn't. Why?
It's a matter of operator precedence. See the example below showing how they are evaluated.
(true && return) false
(true) and (return false)
Because of the && operator precedence, the following line
true && return false
evaluates as
(true && return) false
that does not makes sense. In order to execute your code you need to use
def foo
true && (return false)
end
and doesn't suffer of the same issue because and has lower precedence than &&.
if there is need for shorten statements use
def foo
return false if true
end
def bar
return false if true
end
return is not a function :) therefore it doesn't make sense to say
when true and return is ok send a false

Resources