Use of the bang? - ruby

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.

Related

Does it matter which way a string method is used?

Codeacademy teaches that you can chain multiple methods together as such:
user_input.method1.method2.method3
However, in a later lesson they display some methods like this:
user_input = gets.chomp
user_input.downcase!
I combined them:
user_input = gets.chomp.downcase!
When I use it this way:
user_input = gets.chomp.downcase!
if user_input.include? "s"
...
I receive an error "undefined method `include?'". If I change it to the following, it works fine:
user_input = gets.chomp
user_input.downcase!
if user_input.include? "s"
...
I'm at a loss. I'm concerned whether or not this is a quirk with their console or if this is just how I should be doing it in Ruby. If someone could tell me which way is right, I'd appreciate it. If both are right, that's OK too.
Firstly, in case you do not yet fully understand, assignment of values to variables are done through =, and that you could inspect what variable type it is by appending .class to anything.
Consider the following:
name = 'John'
puts name
# => John
puts name.class
# => String
Now, secondly, it should be noted that the return values of ALL methods are ALL different. But all of them can be identified into two types:
Methods that:
return self
return anything other than self
Example for 1.
-- methods that return self, which you could say methods that return the same type of object which in our specific case, a String
name = 'John'
puts name
# => 'John'
puts name.class
# => String
downcased_name = name.downcase
puts downcased_name
# => john
puts downcased_name.class
# => String
deleted_downcased_name = downcased_name.delete('h')
puts deleted_downcased_name
# => jon
puts deleted_downcased_name.class
# => String
# All of the above can be just simplified into:
deleted_downcased_name2 = 'John'.downcase.delete('h')
puts deleted_downcased_name2
# => jon
puts deleted_downcased_name2.class
# => String
Notice that deleted_downcased_name and deleted_downcased_name2 are the same, because you could treat the chained methods as if you are chaining the return values which is 'John' -> 'john' -> 'jon'.
Example for 2
-- methods that return anything but self, which you could say methods that return a different type.
In our specific case, String's downcase! returns either a String or NilClass (reference here)
returning String if the string changes, or
returning nil if string is already downcased to begin with (no change).
or another String's method: start_with? (reference here)
returning true or false
This is where chaining of methods will not work (raises an error), when you try to use a String method as a chain to nil value.
Consider the following
name = 'mary'
puts name
# => 'mary'
puts name.class
# => String
downcased_name = name.downcase!
puts downcased_name
# => nil
puts downcased_name.class
# => NilClass
downcased_name.delete('h')
# => This will raise the following error
# NoMethodError: undefined method `delete' for nil:NilClass
The error above is because downcased_name is a type of NilClass where you are expecting it to be a type of String. Therefore you cannot chain any string method on it anymore. You can only chain String methods on a String type of value. Similarly, you can only chain Number methods on a Number type of value.
Whenever in doubt, you could always check the documentation to check what a method does, and what its return value and type.
The problem you are encountering is with the bang method downcase!.
This is basically saying "mutate the original string so that it is downcase".
The important part is that this returns nil. As such you are actually calling include? on nil.
If you use the non bang method downcase instead, it is saying "downcase the previously chained thing but do not mutate the original". The key difference is that it returns the result rather than nil.
Here is an example:
str = "ABCD"
str.downcase!
=> nil
str
=> "abcd"
str = "ABCD"
str.downcase
=> "abcd"
str
=> "ABCD" # Note str is still unchanged unless you set str = str.downcase
Welcome to Ruby! While your apprenticeship at Codeacademy may be limited, you'll continue to refer to language API documentation throughout your career. API documentation is a description of what the language (or a library) does for you. In this case, you're using downcase! which, as one commenter points out, does not always return a String. When it takes no action, it returns nil. Nil is an Object in Ruby (like everything else), but the 'include?' method isn't defined for nil, which explains your error. (It's one of the most common errors in Ruby, learn its meaning.)
So, in fact, what's breaking here isn't your method chain. It's that one of the intermediate methods isn't returning a value of the type you expect (nil instead of some kind of String).
Chaining non destructive methods like:
string.chomp.downcase...
has the advantage that the code is concise, but is not efficient if you are not interested in the original state of the object, and just want the final result because it creates intermediate objects during the chain.
On the other hand, applying destructive methods sequentially to the same object:
string.chomp!
string.downcase!
...
is more efficient if you do not need to keep the original state of the object, but is not concise.
Combining methods that may return an object of a different class (particularly nil) as:
string = gets.chomp!.downcase!...
is wrong because the result can become nil at some point in the chain.
Applying a potentially nil-returning method at only the last position as you did:
string = gets.chomp.downcase!
is still not useful if you expect string to always be a string, and can easily lead to an error as you did.
If you want to chain these methods in you example, perhaps you can do this:
user_input = gets.tap(&:chomp!).tap(&:downcase!)
if user_input.include?("s")
...

Precedence of and/or versus method arguments in 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

Ruby defined?( 42[0][:foo] ) && defined?( 93[0]["bar"] ) == true. Why?

Short story:
"Why does defined?(59[0][:whatever]) evaluate to true?"
Long story:
I came across some strange behaviour lately which threw me off.
I was developing a method that did some washing of the data:
#Me washing input data:
def foo(data)
unless data && defined?(data[0]) && defined?(data[0][:some_param])
method2(data[0][:some_param])
else
freak_out()
end
end
I usually write tests where I spew in all kinds of junk data to make sure nothing strange happens:
describe "nice description" do
it "does not call method2 on junk data" do
expect(some_subject).to_not receive(:method2)
random_junk_data_array.each do |junk|
some_subject.foo(junk)
end
end
end
Well, method2 was called here. It happened when junk was a fixnum.
I am using ruby 2.1.0, and I can see that Fixnum has a #[] method which fetches the bit at that position, nice.
But why is fixnum[0][:some_param] considered to be defined?
defined? expression tests whether or not expression refers to anything recognizable (literal object, local variable that has been initialized, method name visible from the current scope, etc.). The return value is nil if the expression cannot be resolved. Otherwise, the return value provides information about the expression.
Let me explain with an example :-
defined?("a") # => "expression"
# this returns "method", as there is a method defined on the class String. So, the
# method invocation is possible, and this is a method call, thus returning `"method"`.
defined?("a".ord) # => "method"
# it return nil as there is no method `foo` defined on `"a"`,
# so the call is not possible.
defined?("a".foo) # => nil
Now coming to your point :-
As you said data[0] gives a Fixnum instance, and of-course Fixnum#[] exist. Thus fixnum_instance[:some_param] is also a valid method call. It just testing if the method is defined or not. If defined, it will tell yes this is a "method" expression. Otherwise nil. Not actually will check if the method call succeeded or not.
In Ruby all objects has truthy values except nil and false, thus "method" being a string object also has the truthy value, thus your condition got succeeded.

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.

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