Why is splat argument in ruby not used all the time? - ruby

I know splat arguments are used when we do not know the number of arguments that would be passed. I wanted to know whether I should use splat all the time. Are there any risks in using the splat argument whenever I pass on arguments?

The splat is great when the method you are writing has a genuine need to have an arbitrary number of arguments, for a method such as Hash#values_at.
In general though, if a method actually requires a fixed number of arguments it's a lot clearer to have named arguments than to pass arrays around and having to remember which position serves which purpose. For example:
def File.rename(old_name, new_name)
...
end
is clearer than:
def File.rename(*names)
...
end
You'd have to read the documentation to know whether the old name was first or second. Inside the method, File.rename would need to implement error handling around whether you had passed the correct number of arguments. So unless you need the splat, "normal" arguments are usually clearer.
Keyword arguments (new in ruby 2.0) can be even clearer at point of usage, although their use in the standard library is not yet widespread.

For a method that would take an arbitrary amount of parameters, options hash is a de facto solution:
def foo(options = {})
# One way to do default values
defaults = { bar: 'baz' }
options = defaults.merge(options)
# Another way
options[:bar] ||= 'baz'
bar = options[bar]
do_stuff_with(bar)
end

A good use of splat is when you're working with an array and want to use just the first argument of the array and do something else with the rest of the array. It's much quicker as well than other methods. Here's a smart guy Jesse Farmer's use of it https://gist.github.com/jfarmer/d0f37717f6e7f6cebf72 and here is an example of some other ways I tried solving the spiraling array problem and some benchmarks to go with it. https://gist.github.com/TalkativeTree/6724065
The problem with it is that it's not easily digestible. If you've seen and used it before, great, but it could slow down other people's understanding of what the code is doing. Even your own if you haven't looked at it in a while hah.

Splat lets the argument be interpreted as an array, and you would need an extra step to take it out. Without splat, you do not need special things to do to access the argument:
def foo x
#x = x
end
but if you put it in an array using splat, you need extra step to take it out of the array:
def foo *x
#x = x.first # or x.pop, x.shift, etc.
end
There is no reason to introduce an extra step unless necessary.

Related

Ruby - when I should use parenthesis or not when calling a function/method

Is there a clear standard or guide to use or not use parenthesis when calling a function/method?
For example, the following code:
class List < Jets::Stack
sqs_queue(:dead_letter)
end
Should I or shouldn't I use parenthesis? Other example:
class ExampleJob < ApplicationJob
def sqs_event ref(:dead_letter)
end
end
vs.
class ExampleJob < ApplicationJob
def sqs_event ref :dead_letter
end
end
Is there a official guideline I can follow?
There isn't an official standard for Ruby code best practices. However, a set of preferred styles has evolved in the Ruby community. It's a good idea to follow those preferred styles, just because it makes your code more easily readable by other Ruby programmers.
Nick Roz has given you a link to the style guide. I would also recommend that you consider installing and using rubocop. It will give you feedback on when and when not to parenthesize arguments, many other formatting matters such as proper indenting, and which of the often several different syntax options to choose in a particular situation.
To answer your specific question about whether or not to use parentheses for method arguments, the answer is yes, in general. Exceptions are, as the guide says, "methods that have 'keyword' status in Ruby." An example is puts (actually the Kernel.puts method). Most people don't use parentheses here, but the guide states that they are optional.
Another example, as Nick has said (although "methods with keyword arguments" isn't quite correct; in that case the arguments are symbols that represent methods rather than keywords), is attr_accessor, which is actually Module.attr_accessor.
So, as a general rule, if it looks like a "command" (a "keyword," if you will), but is actually a method, omit the parentheses. Otherwise, use them. And if you're not sure of the difference, get in the habit of using rubocop.
In Ruby it is usually optional.
Ruby tends towards minimalism so they are often avoided.
Sometimes they are required such as in rspec expect where
expect a.to be true
has to be
expect(a).to be true
Using no parens or empty parens when calling a method that has a parameter results in ArgumentError unless you a default for the param, i.e.
def a(x=1)
The other consideration is when you want to call a method on the result of something, when you'll need want that method to clearly be on the final result, i.e.
"br" + "own".upcase
brOWN
However
("br" + "own").upcase
BROWN
Finally, as I'm talking about clarity, sometimes it may be better to have them, even when not strictly needed. This is generally in compound expressions, rather than relying on operator precedence, etc. Or if you want an expression that specifically does not get executed by standard operator precedence and you want your own grouping and order of operations, for example:
irb(main):007:0> 5 + 6 * 2
=> 17
irb(main):008:0> (5 + 6) * 2
=> 22
As Nick indicated, the one complication is super where super or super() pass on parms but super(a,b) calls super with... a,b as params
Yes there is
I suppose you are looking for community guidelines since there is not style guides from Ruby core team.
Well, whenever you call a method you should use parenthesis, otherwise it becomes unclear
# bad
x = Math.sin y
# good
x = Math.sin(y)
# bad
array.delete e
# good
array.delete(e)
# bad
temperance = Person.new 'Temperance', 30
# good
temperance = Person.new('Temperance', 30)
However it is recommended to skip them when there is no arguments.
Be careful with super and super() they are different. super without brackets passes all the parameters implicitly. super() with empty brackets omits all the parameters
The only exception that comes to my mind is some kind of custom DSL, there must be some rules or preferences for DSL itself e.g.
validates :name, presence: true
It is also true for methods with keyword arguments:
attr_reader :name, :age
According to Matz:
If arguments are given to a method, they are generally surrounded by
parentheses,
object.method(arg1, arg2)
but they can be omitted if doing so does not cause ambiguity.
object.method arg1, arg2

Specify Ruby method namespace for readability

This is a bit of a weird question, but I'm not quite sure how to look it up. In our project, we already have an existing concept of a "shift". There's a section of code that reads:
foo.shift
In this scenario, it's easy to read this as trying to access the shift variable of object foo. But it could also be Array#shift. Is there a way to specify which class we expect the method to belong to? I've tried variations such as:
foo.send(Array.shift)
Array.shift(foo)
to make it more obvious which method was being called, but I can't get it to work. Is there a way to be more explicit about which class the method you're trying to call belongs to to help in code readability?
On a fundamental level you shouldn't be concerned about this sort of thing and you absolutely can't tell the Array shift method to operate on anything but an Array object. Many of the core Ruby classes are implemented in C and have optimizations that often depend on specific internals being present. There's safety measures in place to prevent you from trying to do something too crazy, like rebinding and applying methods of that sort arbitrarily.
Here's an example of two "shifty" objects to help illustrate a real-world situation and how that applies:
class CharacterArray < Array
def initialize(*args)
super(args.flat_map(&:chars))
end
def inspect
join('').inspect
end
end
class CharacterList < String
def shift
slice!(0, 1)
end
end
You can smash Array#shift on to the first and it will work by pure chance because you're dealing with an Array. It won't work with the second one because that's not an Array, it's missing significant methods that the shift method likely depends on.
In practice it doesn't matter what you're using, they're both the same:
list_a = CharacterArray.new("test")
list_a.shift
# => "t"
list_a.shift
# => "e"
list_a << "y"
# => "sty"
list_b = CharacterList.new("test")
list_b.shift
# => "t"
list_b.shift
# => "e"
list_b << "y"
# => "sty"
These both implement the same interfaces, they both produce the same results, and as far as you're concerned, as the caller, that's good enough. This is the foundation of Duck Typing which is the philosophy Ruby has deeply embraced.
If you try the rebind trick on the CharacterList you're going to end up in trouble, it won't work, yet that class delivers on all your expectations as far as interface goes.
Edit: As Sergio points out, you can't use the rebind technique, Ruby abruptly explodes:
Array.instance_method(:shift).bind(list_b).call
# => Error: bind argument must be an instance of Array (TypeError)
If readability is the goal then that has 35 more characters than list_b.shift which is usually going dramatically in the wrong direction.
After some discussion in the comments, one solution is:
Array.instance_method(:shift).bind(foo).call
Super ugly, but gets across the idea that I wanted which was to completely specify which instance method was actually being called. Alternatives would be to rename the variable to something like foo_array or to call it as foo.to_a.shift.
The reason this is difficult is that Ruby is not strongly-typed, and this question is all about trying to bring stronger typing to it. That's why the solution is gross! Thanks to everybody for their input!

Why is there no `.split!` in Ruby?

It just seems pretty logical to have it when there's even a downcase!. Has anyone else run into this use case in Ruby?
For the curious, I'm trying to do this:
def some_method(foo)
foo.downcase!.split!(" ")
## do some stuff with foo later. ##
end
some_method("A String like any other")
Instead of this:
def some_method(foo)
foo = foo.downcase.split(" ")
## do some stuff with foo later. ##
end
some_method("A String like any other")
Which isn't a really big deal...but ! just seems cooler.
Why is there no .split! in Ruby?
It just seems pretty logical to have it when there's even a downcase!.
It may be logical, but it is impossible: objects cannot change their class or their identity in Ruby. You may be thinking of Smalltalk's become: which doesn't and cannot exist in Ruby. become: changes the identity of an object and thus can also change its class.
I don't see this "use case" as very important.
The only thing a "bang method" is doing is saving you the trouble of assigning a variable.
The reason "bang methods" are the exception instead of the rule is they can produce confusing results if you don't understand them.
i.e. if you write
a = "string"
def my_upcase(string)
string.upcase!
end
b = my_upcase(a)
then both a and b will have transformed value even if you didn't intend to change a. Removing the exclamation point fixes this example, but if you're using mutable objects such as hashes and arrays you'll have to look out for this in other situations as well.
a = [1,2,3]
def get_last_element(array)
array.pop
end
b = get_last_element(a)
Since Array#pop has side effects, a is now 1,2. It has the last element removed, which might not have been what you intended. You could replace .pop here with [-1] or .last to get rid of the side effect
The exclamation point in a method name is essentially warning you that there are side effects. This is important in the concept of functional programming, which prescribes side effect free code. Ruby is very much a functional programming language by design (although it's very object oriented as well).
If your "use case" boils down to avoiding assigning a variable, that seems like a really minor discomfort.
For a more technical reason, though, see Jorg Mittag's answer. It's impossible to write a method which changes the class of self
this
def some_method(foo)
foo = foo.downcase.split(" ")
end
some_method("A String like any other")
is the same as this
def some_method(foo)
foo.downcase.split
end
some_method("A String like any other")
Actually, both of your methods return the same result. We can look at a few examples of methods that modify the caller.
array.map! return a modified original array
string.upcase! return a modified original string
However,
split modifies the class of the caller, changing a string to an array.
Notice how the above examples only modify the content of the object, instead of changing its class.
This is most likely why there isn't a split! method, although it's pretty easy to define one yourself.
#split creates an array out of a string, you can't permanently mutate(!) the string into being an array. Because the method is creating a new form from the source information(string), the only thing you need to do to make it permanent, is to bind it to a variable.

Ruby initialization idiom using symbols

I'm doing some work in an existing Ruby code base, and I'm quite new to Ruby. I see this initialization idiom pretty often:
def initialize(input)
#number = input[:number]
#color = input[:color]
end
I guess I have two questions. One is whether this is a common idiom and if so, what's good about it? The second is, what is actually happening here? Is the implication that input is an array? A hash? The underlying mechanics aren't clear to me.
Yes, it's common. Generally when you see code like this, it means that input is a Hash. Occasionally someone might pass in an object that acts like a hash, though, and we can still expect this code to work. We can tell that input is definitely not an array, because arrays require Integers to be used as their index, but :number and :color are Symbols.
Whenever you see something starting with a colon like :number, that is a Ruby symbol.
Whenever you see something starting with a # like #number, that is the name of an instance variable. Instance variables are used to store data inside objects for later use.
Suppose we have a class Foo defined like this:
class Foo
def initialize(input)
#number = input[:number]
#color = input[:color]
end
end
In this case, we can create a new object like this:
Foo.new(number: 4, color: 'red')
The code above is equivalent to:
input_hash = { :number => 4, :color => 'red' }
Foo.new(input_hash)
One nice thing about this pattern is that you can tell exactly what each input variable is being used for because it will be written next to a descriptive symbol, and also it doesn't matter what order you put the input variables in.
Improvements
If you want to improve this code, you might consider using a new feature of Ruby called keyword arguments. Alternatively, you might also consider Hash#fetch so you can have more control over what happens when one of the keys is missing, instead of just storing a nil value in your object. I would also recommend that you check the input hash for any unexpected keys and raise an exception if they are found.
Is this a common idiom and if so, what's good about it?
It's not common, but it's acceptable. When there are more than n parameters being passed in, where "n" is often > 3, we should use a hash.
What's good about it? Look at the code. Is it simple and readable? Does it make sense that an input parameter of :number is being assigned to an instance variable of the same name?
what is actually happening here? Is the implication that input is an array? A hash? The underlying mechanics aren't clear to me.
It's a hash where :number and :color are keys.

What is the difference between these two method parameter definitions?

I'm starting to learn Ruby. I read that arguments where passed by reference to a method,
however I don't understand the difference between these two methods.
def print(text)
puts text
end
and
def print(*text)
puts text
end
Using a * means that we are passing a pointer like in C?
The *text is what's called the splat operator in Ruby. It basically means if you pass multiple arguments to the second print they will get slurped into the single text variable.
See The Splat Operator in Ruby
The * before a parameter name in a Ruby parameter list is used for variable length arguments, so they are similar to the ... in C/C++ for varargs.
def vlaFunc(*args)
puts args
end
vlaFunc(1,2,3)
# output is [1,2,3]
There are no pointers in Ruby, * in this context is generally referred to as the "splat" operator:
http://4loc.wordpress.com/2009/01/16/the-splat-operator-in-ruby/
http://theplana.wordpress.com/2007/03/03/ruby-idioms-the-splat-operator/
In this case the method can take an arbitrary number of arguments, which will be available in the array text.
First you have two nice methods started there. But I would say try to avoid using puts inside them. You don't need it anyway. A method will always yield the last statement evaluated. something = text would get the job done. And I don't need to answer now about the differences.
Your first two replies are very good there. But you may want to try something like this
j = *[] #=> nil in 1.8 but [] in 1.9
It's been the new kid on the block for a time now. Guess what it does?

Resources