Having written elixir for a while I got curious to see what all can be done in
ruby, in a functinoal way mostly in comparison to Elixir. Below are some of the
ruby methodologies that I found. I am skipping lambdas and procs for now since
they are widely discussed at many places. The snippets are self explanatory.
Splat *
and destructuring a list
Never knew I could deconstruct an array like this.
first, *rest = [1,2,3]
# => first == 1
# => rest == [2,3]
The above led me to reimagine #map
in a functional way.
arr = [1,2,3]
fn = ->(x) { |x| x*2 }
def remap(arr, fn)
return if arr == []
first, *rest = [1,2,3]
first, *rest = arr
[].push(fn.(first)).push(remap(rest, fn))
end
Shorthand proc invocation
Ruby has procs and lambdas, for this post we are not digging on those two topics
much.
Creating a proc
double = Proc.new { |x| x*2 }
Creating a lambda
double = ->(x) { x*2 }
lambda = lambda { "Lambda" }
Back to the current section, you would know this:
def main
[1,2,3].map(&:+)
end
The above snippet retruns the sum of the elements.
Let's decrypt (&:+)
&
is shorthand for to_proc
def to_proc
proc { |obj, *args| obj.send(self, *args) }
end
:
is just a symbol notation (Read on ruby symbols)
+
the operator
Now essnetially, you could look at it as & :+
, so a symbol :+
is passed.
Now, as per the definition of to_proc the symbol is called upon the object
itself.
So if you pass a symbol, it is turned into a proc.
Symbol#to_proc
was added to address this common pattern
arr.map do { |x| x.upcase }
This then becomes:
arr.map(&:upcase)
Now in order to create a custom functions that we can pass to map we should use
a lambda. Lambdas are essentially anonymous functions.
If you are familiar with javascript, you would have used lot of anonymous
functions for defining callbacks. On a similar anology we can proceed with a
squaring function.
irb(main):004:0> [1,2,3].map(&(->(x) { x*x}))
=> [1, 4, 9]
Looks cryptic but you can see that we created an anonymous function and passed
it in with &
.
We could take it out and define the function somewhere else giving
irb(main):005:0> fn = ->(x) { x*x }
=> #<Proc:0x00007f89f19b3cf0@(irb):5 (lambda)>
irb(main):006:0> [1,2,3].map(&fn)
=> [1, 4, 9]
Let's take a step back now, we just discussed &
essentially calls #to_proc
on it.
Another important thing to remember, if we use a symbol the corresponding method
is called on the object itself i.e
irb(main):009:0> [1,2,3].map(&:fn)
Traceback (most recent call last):
5: from /Users/manu/.rbenv/versions/2.6.3/bin/irb:23:in `<main>'
4: from /Users/manu/.rbenv/versions/2.6.3/bin/irb:23:in `load'
3: from /Users/manu/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
2: from (irb):9
1: from (irb):9:in `map'
NoMethodError (undefined method `fn' for 1:Integer)
Since Integer
doesn't have a method fn
, it failed.
Hence,
irb(main):011:0> [1,2,3].map(&:odd?)
=> [true, false, true]
irb(main):012:0>
That's it!
Top comments (0)