Ruby is an object oriented language. That doesn’t mean we cannot do some functional programming in Ruby. When you take a look closer to the history of the Ruby language, you can find out, that Ruby was influenced by other languages like Perl, Smalltalk, Eiffel, Ada, Basic or Lisp. Because of this different inspirations we can find in Ruby not only object oriented concepts, but also a little bit of functional programming.
Before we start going deep in functional programming in Ruby. Let’s focus on functional programming its self. What is functional programming? You can find out, that functional programming is a programming paradigm. That means it is a way to classify programming languages based on their features. OK, so what are the features of functional programming language?
- Pure functions (no side-effects) - Function always returns the same result for the same arguments and function can never cause any observable side effects. This is how the function works in the mathematical world.
- Immutability - After the state/value is created, you cannot change it. Instead of changing state or value, you create a new one.
- Referential transparency - this is combining pure function and immutability.
- Memoization - It is a side effect of referential transparency. It can seed up computations by saving the results of previous calls.
- Idempotence - You will get the same results no matter how many times you will call a function. This is also a side effect of referential transparency.
- Higher-order functions - Functions which can get function as argument or return function as a result.
- Currying - Transforming a function that takes multiple arguments into a function that takes one argument and return function. We can say that this is a function generator in some sense.
- Recursion - It is invoking the function by themselves and letting an operation be repeated until it reaches the stop condition or base case.
- Lazy evaluation (non-strict evaluation) - It does not evaluate function arguments unless their values are needed for evaluation of the function itself.
Now we know a little bit about functional programming concepts. We can start going into Ruby functional programming.
Blocks in Ruby
Block it is nameless function in Ruby. We can put it as last argument into function. There can be only one block put as an argument in Ruby method. In Ruby blocks are higher-order functions. A common block in Ruby is for example each
:
[1, 2, 3, 4].each do |item|
p item
end
1
2
3
4
=> [1, 2, 3, 4]
Let’s create our own block:
def my_own_block
p 'before'
yield if block_given?
p 'after'
end
irb> my_own_block
"before"
"after"
=> "after"
As you see, my_own_block
behaves like a normal method because it is a normal method. The magic begins, when we will start using yield
part. What is this yield
? It is a call of our block, our nameless function which we can put as argument to my_own_block
method. We can say that our yield
will be replaced by everything what is in our argument for my_own_block
. Let’s check how we can call this method with block as an argument:
irb> my_own_block { p 5 }
"before"
5
"after"
=> "after"
To call block as argument of the function we need to use curly brackets {}
or do end
clause. We cannot use normal brackets ()
. In this case we will get an error.
irb> my_own_block(p 5)
5
Traceback (most recent call last):
6: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/bin/irb:23:in `<main>'
5: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/bin/irb:23:in `load'
4: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
3: from (irb):13
2: from (irb):13:in `rescue in irb_binding'
1: from (irb):4:in `my_own_block'
ArgumentError (wrong number of arguments (given 1, expected 0))
First of all, we see 5
because in the beginning Ruby runs and calculates argument of the method. Then we get wrong number of arguments (given 1, expected 0)
. That means that block argument is treated in a different way than normal method arguments. So, we cannot run block as a normal argument in this case. Later I will show you how to do that in the right way.
In this example I didn’t tell you one more thing. What is it the block_given?
condition? This helps us with handling a case when you do not put block as an argument. This method checks if block is given or not and prevent us before an exception. If we miss that condition in our method declaration and we will call the method without a block, we will see:
def my_own_block
p 'before'
yield
p 'after'
end
irb> my_own_block
"before"
Traceback (most recent call last):
5: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/bin/irb:23:in `<main>'
4: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/bin/irb:23:in `load'
3: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
2: from (irb):19
1: from (irb):16:in `my_own_block'
LocalJumpError (no block given (yield))
Proc object
Proc is a one of classes in Ruby. A Proc object is an encapsulated block of code, which can be stored in a local variable. It can be later passed to a method or another Proc as an argument and can be called. It is essential for Ruby functional programming features concept.
You already saw one way to declare method with a block. Below I put another way to do that:
def my_block(&block)
p 'before'
p block.class
block.call
p 'after'
end
We can execute it the same way as before:
irb> my_block { p 4 }
"before"
Proc
4
"after"
=> "after"
We see that object inside a block
variable is an instance of Proc
class. We can call it by using a method call
. block.call
behaves the same like yield
in our first example. But Proc
object is even cooler than simple block. We can put many of them as arguments to Ruby method:
def run_proc(first, last)
first.call
last.call
end
proc1 = Proc.new { p 'first' }
proc2 = Proc.new { p 'last' }
irb> run_proc proc1, proc2
"first"
"last"
=> "last"
We can also call Proc
object outside of a method in many different ways:
my_proc = Proc.new do |item|
p "Text: #{item}"
end
irb> my_proc.call 10
"Text: 10"
=> "Text: 10"
irb> my_proc.(20)
"Text: 20"
=> "Text: 20"
irb> my_proc[30]
"Text: 30"
=> "Text: 30"
irb> my_proc === 40
"Text: 40"
=> "Text: 40"
This allows us to do some fancy tricks. Like using Proc
in case
:
proc1 = Proc.new { |number| number % 3 == 0 }
proc2 = Proc.new { |number| number % 3 == 1 }
case 3
when proc1 then p 'proc1'
when proc2 then p 'proc2'
else
p 'not a proc'
end
"proc1"
=> "proc1"
Because case
is using an equality operator ===
to check if the condition is fulfilled and Proc
object is implementing this method, we can do that magic with Proc
inside case
. This is outlier but, because Range
class also is implementing equality operator ===
, we can use it in case
too.
irb> (4..7) === 5
=> true
irb> (4..7) === 8
=> false
Right now I will show you one more way to declare Proc
in your method:
def run_proc
p 'before'
my_proc = Proc.new
my_proc.call
p 'after'
end
irb> run_proc { p 6 }
"before"
6
"after"
=> "after"
When you look at this implementation, it can be for you very strange. We do not declare argument, so how does the Proc.new
know what to do? Normally when you will just do Proc.new
without any block declaration, you will get exception tried to create Proc object without a block
. In this case when Proc.new
has no block code itself, it will seek a block declaration in the current scope. So when we run our run_proc
method with block, everything is working like it should.
Lambdas
A lambda is an anonymous function. It is a function definition that doesn’t have a name. It is not bound to an identifier. You can assign it to variable, but it doesn’t have a name by which you can call it. Anonymous functions are often used as arguments for higher-order functions. In Ruby they are very similar to Proc
object, but there are some small differences. Let’s go through them.
Arguments control
We will declare lambda and Proc
object. By the way the shortcut for Proc.new
is proc
so you can see me switching those two forms in the code.
my_proc = Proc.new { |item| p "==#{item}==" }
my_lambda = lambda { |item| p "==#{item}==" }
This 2 objects are the same class:
irb> my_proc.class
=> Proc
irb> my_lambda.class
=> Proc
Both objects have the same arity:
irb> my_proc.arity
=> 1
irb> my_lambda.arity
=> 1
But there is a difference when you call them without argument. Proc
will be executed and lambda will raise an exception:
irb> my_proc.call
"===="
=> "===="
irb> my_lambda.call
Traceback (most recent call last):
5: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/bin/irb:23:in `<main>'
4: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/bin/irb:23:in `load'
3: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
2: from (irb):77
1: from (irb):62:in `block in irb_binding'
ArgumentError (wrong number of arguments (given 0, expected 1))
We cannot distinguish them by class name, but it is possible to use method lambda?
to do that.
irb> my_proc.lambda?
=> false
irb> my_lambda.lambda?
=> true
I would like to add one more thing to this section. If you declare an array of arguments in lambda. You will get arity -1
.
irb> lambda { |*items| }.arity
=> -1
In the end of this section I will show you one more possible declaration of lambda, by using arrow operator ->
.
my_lambda = -> (item) { p "==#{item}==" }
irb> my_lambda.lambda?
=> true
Using with return
First, we declare method which will call a Proc
object or a lambda.
def run(proc)
p 'before'
proc.call
p 'after'
end
Now we can run method run
with lambda:
irb> run lambda { p 'in'; return }
"before"
"in"
"after"
=> "after"
Everything is fine. Now, let’s go to Proc
object:
irb> run proc { p 'in'; return }
"before"
"in"
Traceback (most recent call last):
6: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/bin/irb:23:in `<main>'
5: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/bin/irb:23:in `load'
4: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
3: from (irb):91
2: from (irb):87:in `run'
1: from (irb):91:in `block in irb_binding'
LocalJumpError (unexpected return)
We see an error. We start with "before"
, then we go to our Proc
object and we print "in"
and after that we get an exception. This is because Proc
object doesn’t return current context. It wants to return context where it was defined. This is the main context (irb console context). Proc
cannot return from that context, so we see an exception. If we change the context, we will see the difference:
def change_context
run lambda { p 'in'; return }
run proc { p 'in'; return }
end
Let’s run our change_context
method:
irb> change_context
"before"
"in"
"after"
"before"
"in"
=> nil
First, we run part with lambda. It prints "before"
, "in"
and "after"
. Then it calls a Proc
object. We see "before"
, "in"
and then we finish our method change_context
. Right now context of Proc
object is context of change_context
method. Because Proc
return form context where it was created, it returns from method change_context
. So, we cannot print second "after"
string.
Closures
Closure is a technique for creating a function based on another function with an environment which have impact on this function during the declaration process. This is one of the ways to generate new functions by using the function. It is related to higher-order functions concept. As it is hard to me to explain this in simple words, the example should help in understanding this concept.
def multiple(m)
lambda { |n| n * m }
end
double = multiple(2)
triple = multiple(3)
# Execute
irb> double[5]
=> 10
irb> triple[5]
=> 15
We declare function multiple
with one argument. Inside this function we use lambda with also one argument, but we are using it with context of multiple
method. We put m
variable inside lambda declaration. Now we can generate functions based on multiple
method. When we put as m
variable 2
this value will go to lambda as sounding environment. Lambda in double
method will be remembered as lambda { |n| n * 2 }
. Right now when you call double
method, you will put 5
as n
variable. 5 * 2
is 10
.
There is one more thing to remember. In Ruby during closure we remember references to variable not the value of it. This is different then in other languages. And this is why you can change the context after it’s declared, but you cannot create (declare) context after the declaration of closure. Let’s check examples below. First, declare closure without context and try to add context later:
my_proc = proc { p first_name }
irb> my_proc.call
Traceback (most recent call last):
5: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/bin/irb:23:in `<main>'
4: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/bin/irb:23:in `load'
3: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
2: from (irb):107
1: from (irb):105:in `block in irb_binding'
NameError (undefined local variable or method `first_name' for main:Object)
We get an error. Now we will try to add missing context:
first_name = "Agnieszka"
=> "Agnieszka"
irb> my_proc.call
Traceback (most recent call last):
5: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/bin/irb:23:in `<main>'
4: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/bin/irb:23:in `load'
3: from /home/agnieszka/.rvm/rubies/ruby-2.6.1/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
2: from (irb):109
1: from (irb):105:in `block in irb_binding'
NameError (undefined local variable or method `first_name' for main:Object)
It is not possible to add context after the declaration of closure. Next example will show you, that we can change context which was declared before:
name = 'Agnieszka'
my_proc = proc { p name }
irb> my_proc.call
"Agnieszka"
=> "Agnieszka"
name = 'Ula'
irb> my_proc.call
"Ula"
=> "Ula"
Method as lambda
We can create a method and use it as lambda. This is how we do that:
def my_method
p 'Hello word'
end
my_proc = method(:my_method)
irb> my_proc.call
"Hello word"
=> "Hello word"
Types of closures in Ruby
We have a few types of closure in Ruby:
- block +
yield
- block +
&b
Proc.new
proc
lambda
method
Where do we use closures?
In pure Ruby:
[1, 2, 3].each { |item| p item }
[1, 2, 3].each_cons(2).map { |x, y| x + y }
[1, 2, 3].map { |item| item.next }
[1, 2, 3].inject(0) { |sum, item| sum + item }
[1, 2, 3].inject(0, :+)
In Ruby on Rails:
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @items }
end
Bonus - Lisp in Ruby
To do Lisp lists in Ruby we will use lazy enumerators and recursion. But first, let’s check how lists look like in Lisp.
(write '(1 2 3 4))
=> (1 2 3 4)
(write (car '(1 2 3 4)))
=> 1
(write (cdr '(1 2 3 4)))
=> (2 3 4)
If you read my article about Elixir basics you can compare car
and cdr
methods to head
and tail
in Elixir. car
will always return first element of the list and cdr
will return list without first element. Let’s prepare a Ruby array to behave like Lisp lists.
car, cdr = [1,[2,[3]]]
irb> car
=> 1
irb> cdr
=> [2, [3]]
Now we will prepare normal enumerator for this kind of lists:
class LispyEnumerable
include Enumerable
def initialize(tree)
@tree = tree
end
def each
while @tree
car, cdr = @tree
yield car
@tree = cdr
end
end
end
This will allow us to do some operations on car
a head of the list and it will overwrite @tree
variable for the next step of our loop with cdr
tail of list. We can call it like:
list = [1,[2,[3]]]
irb> LispyEnumerable.new(list).each { |item| p item }
1
2
3
=> nil
For now our list is not lazy and our enumerator is also not lazy. To change that we will use lambdas.
class LazyLispyEnumerable
include Enumerable
def initialize(tree)
@tree = tree
end
def each
while @tree
car, cdr = @tree.call
yield car
@tree = cdr
end
end
end
We changed only one line. We calculate (execute) our next element of the list by calling @tree.call
. To be able to use this enumerator, we need to also change a structure of our list itself.
list = lambda { [1, lambda { [2, lambda { [3] } ] } ] }
irb> LazyLispyEnumerable.new(list).each { |item| p item }
1
2
3
=> nil
Right now we don’t see any difference. To see that, let me change list declaration. If it will have strict evaluation, we will see all prints for numbers in the beginning and then the numbers. But if this is a lazy evaluation, then we will see it one by one.
list = lambda do
p '1 is called'
[1, lambda do
p '2 is called'
[2, lambda { p '3 is called'; [3] }]
end]
end
irb> LazyLispyEnumerable.new(list).each { |item| p item }
"1 is called"
1
"2 is called"
2
"3 is called"
3
=> nil
As you can see we have lazy enumerator. Now is the time for the Fibonacci sequence with lazy evaluation:
def fibo(a, b)
lambda { [a, fibo(b, a + b)] }
end
LazyLispyEnumerable.new(fibo(1, 1)).each do |item|
puts item
break if item > 100
end
1
1
2
3
5
8
13
21
34
55
89
144
=> nil
I put there break point, but without it Ruby can do these calculations into infinity (theoretically). In some way this is not the real recursion. We can say that this is an infinite loop. If we do that with the real recursion, the code will look like:
def recursive_fibo(n)
return 1 if n == 0
return 1 if n == 1
recursive_fibo(n -1) + recursive_fibo(n - 2)
end
irb> recursive_fibo(11)
=> 144
In this situation if we remove our gourds we will get an error SystemStackError (stack level too deep)
. This will be also slower than lazy evaluation.
In the end I will only mention that since Ruby version 2.0 we can use lazy enumerators inside a Ruby language, like this:
irb> (1..Float::INFINITY).lazy.select(&:even?).first(5)
=> [2, 4, 6, 8, 10]
If you want more information about them, check out Lazy enumerator documentation.
That’s all for today. I hope you like it. If you have any questions put them in the comments below. I will try to answer them. See you next time!
Bibliography
- An Introduction video about Procs, Lambdas and Closures in Ruby
- Presentation about functional programming in Ruby
- Article about closures in Ruby
- Ruby documentation
Need help?
If you're looking for a Ruby developer with over a decade of experience, don't hesitate to contact me.
I have experience in a variety of domains, with a focus on short user feedback loops and teamwork. I can help you build a great product.