elixir Elixir #elixir #ruby #ElixirWithARubyist

Learn Elixir with a Rubyist, episode V

Concurrency, Processes and Recursion

We finally got into the number five! First of all, sorry for taking so long to publish this article, a bunch of things happened at once, I had some health issues plus some conferences I had to speak at, but I come with good news, the series is back, and I hope to keep the rate of 1 article a week from now on.

In this article we will talk about Concurrency, Processes and Recursion, it’s time to start taking some traction and put some of the stuff we saw on the lastest articles together.

If you like the series, please let me know on Twitter! And if you haven’t checked the other episodes yet you can do it following the links bellow:

Episode I - Elixir, Pipe Operator and Pattern Matching
Episode II - Actor Model, Modules and functions
Episode III - Maps, Functions + Pattern Matching = ❤️
Episode IV - Elixir Type, Data Structures and Underscore


Wants to be on top of the Proper Series?

Together with the articles I keep a mailing list, you can sign up by putting your email bellow. You’ll be notified every time a new article is released, I also try to put some cool stuff on the emails so you can check things like open source projects, conferences info, and eventual stuff we can work together!


Concurrency & Processes

We have discussed concurrency in the other articles already, how the Erlang VM enables you to take advantage of concurrency in such a graceful and powerful way, and that’s only possible thanks to the actor model and the way the VM handles the processes and its communications (for further info around Actor Model check episode II).

Processes started and executed on the Erlang VM are completely different from the usual processes you see on your operation system, it’s really lightweight, and the VM can run a bunch of them at the same time, thanks to the separation between all processes and the functional paradigm, that allow us to avoid race conditions and to actually have concurrent code safely.

Every time you run any elixir code, from executing a script to running a web application, a new process is started, and this process can also spawn other processes.

A process can be as little as a single function, but others can be really complex and even coordinate other processes, as the ones known as supervisors, we’ll talk about those in future articles.

Let’s start by checking some of the modules and functions that can create new processes, the easiest and simplest one is probably the spawn function, a function from the Erlang standard library, let’s check how it works:

# Starting a new process can be really simple, one of the
# simplest ways is by using the `spawn/3` function.
# (`/3` specify that it expects 3 arguments)

defmodule SpawnExample do

  # This is the main funciton of this module, 
  # the one that starts our application.
  def start do
    IO.puts "============= Starting Application"

    # We call a function that is responsible for 
    # spawning our new process
    spawn_proc
  end

  # This is the function the new process is supposed to execute.
  # Once the process executes this function it will 
  # die automatically.
  def proc_func do
    IO.puts "* Started Process"
    # Wait for 1 second, for the sake of simulating some computing.
    :timer.sleep(1000)
    IO.puts "* Finished Process"
  end

  # `spawn_proc/0` is our private function (that's why we use `defp`)
  # responsible for spawning a new process.
  defp spawn_proc do

    # We spawn a new process, passing the module and the 
    # fucntion this process is supposed to execute.
    # The `__MODULE__` syntax is a reference to this own module
    # and the atom `:proc_func` indicates the function inside this
    # module that should be executed.
    # We are also sending an empty list as third argument, indicating
    # that this `proc_func` method does not expect any arguments.
    spawn(__MODULE__, :proc_func, [])
    IO.puts "============= Finishing Application"
  end
end

# Let's call on main method and see what the output will be.
SpawnExamle.start

# ============= Starting Application 
# ============= Finishing Application 
# * Started Process
# :ok
# * Finished Process
#
# Our application is executed so fast the it's finished
# before our new process is even able to print "* Started Process".
# Our new process is started in parallel and finishes only after our
# main application is already done.

That’s cool, it doesn’t look so awesome yet, but trust me it gets way better once we start multiple processes, but to do it we'll need to dive into our next topic on this article, recursion, not only what it is but also how to implement recursion on Elixir by bringing pattern matching into the play (further info around patter matching on episode I and episode III).

Recursion

Recursion can be tricky to understand when using functional languages. Because of immutability you don’t have the usual tools you are used to, like for and while, so in order to achieve recursion you need to bring other elements, like pattern matching.

We have already implemented functions with pattern matching in other episodes, but lets check how we could use it to achieve recursion and compare it with what we would expect to have in ruby.

Let’s suppose we want to loop through a list, printing its elements and then a finish message, pretty simple right? Let’s check it on ruby:

# Our class definition
class RecursionExample
  
  # usual initialize method, it receives and store
  # the list when instantiating the object.
  def initialize(list)
    @list = list
  end

  # this is the method that actually loops through 
  # the list
  def print_list

    # by usin the `each` method we can easily loop 
    # through the list while using `puts` to print its elements
    @list.each do |element|
      puts element
    end

    # the final message indicating the execution 
    # has finished.
    puts "Finished list"
  end
end

re = RecursionExample.new([1,2,3,4,5])
re.print_list

# As you can see it will correctly loop through
# the elements on the list printing each one.
# Everything done following usual OOP style.
#
# 1
# 2
# 3
# 4
# 5
# Finished list

Now let’s check how we would tackle it within elixir, in a functional way, without tools like each.

# Our module definition
defmodule RecursionExample do

  # This is our main function, responsible for 
  # receiving a list, print its first element,
  # then call itself again passing the rest of the
  # list as the new argument.
  # 
  # This is accomplished thanks to the [head | tail] 
  # implementation, another Erlang feature that basically
  # enables you to separate the first element from the 
  # rest of a list. We will go back to it later.
  #
  def print_list([first_element | rest_of_the_list]) do
  	# Basically prints the first element of the list
    IO.puts first_element

    # Calls itself again, now passing what left over
    # the list as the argument
    print_list(rest_of_the_list)
  end
  
  # Here we see a function with the same name, but by
  # using pattern matching we know that it'll only
  # be called when an empty list is passed as argument,
  # therefore this will be matched by call on the function
  # above, when the all elements of the list were printed.
  #
  def print_list([]) do
  	# Prints the message indicating the execution 
  	# has finished
    IO.puts "Finished list"
  end
end


RecursionExample.print_list([1,2,3,4,5])

# Here we are able to achieve the same result in 
# a functional way. 
# You can see wee have two functions with the same name, 
# as we have done in the past, we are using pattern matching
# to define which function should be called.
# 
# 1
# 2
# 3
# 4
# 5
# Finished list

That’s awesome, by using the same core concepts we seen on last episodes we can easily achieve recursion, and do it in a graceful and clear way, by having different functions. This can make debugging so much easier and also feels really organized.

Now let’s go for the real deal, we want to adapt our initial example used to spawn a new processes, now we want it to support an infinite number of processes (as many as your computer can handle *hint: it’s a freaking lot).

Now that we know how to use recursion it shouldn’t be that hard, let’s check the result:

defmodule SpawnExample do
  def start do
    IO.puts "============= Starting Application"

    # Now our `spawn_proc/1` function receives one argument
    # the number of processes it's supposed to spawn.
    spawn_proc(10)
  end

  
  def proc_func do
    IO.puts "* Started Process"
    :timer.sleep(1000)
    IO.puts "* Finished Process"
  end

  # Now we will use pattern matching to implement recursion
  # into our application, this can be mind bending if
  # you haven't read the previous articles.
  #
  # This basically means that this signature of `spawn_proc/1`
  # will be matched whenever the argument sent to it is `0`
  #
  defp spawn_proc(0) do
    IO.puts "============= Finishing Application"
  end

  # As you might have noticed we have two methods with the same
  # name but different signatures, despite of both getting 
  # one argument, by using pattern matching we can be sure that
  # unless the argument is `0` this is the function that will 
  # be executed
  #
  defp spawn_proc(number_of_procs) do
    spawn(__MODULE__, :proc_func, [])
    
    # After spawning the new process, we decrease the 
    # `number_of_procs` and call this same method again, now with
    # `number_of_procs - 1` as its argument, this is a great
    # example of concurrency + recursion + patter matching.
    # When `number_of_procs - 1` is equal `0` the first `spawn_proc/1`
    # function will be called, finishing the application.
    #
    procs_missing = number_of_procs - 1
    spawn_proc(procs_missing)
  end
end

# Let's start our application and check the output
SpawnExample.start

# Again you can see our application being executed so fast
# that it finishes before our new processes are able to print
# its initial message.
# All 10 processes are started concurrently this time.
#
# ============= Starting Application
# ============= Finishing Application
# * Started Process
# * Started Process
# * Started Process
# * Started Process
# * Started Process
# * Started Process
# * Started Process
# * Started Process
# * Started Process
# * Started Process
# :ok
# * Finished Process
# * Finished Process
# * Finished Process
# * Finished Process
# * Finished Process
# * Finished Process
# * Finished Process
# * Finished Process
# * Finished Process
# * Finished Process    

That’s awesome, now we already know how to spawn a new processes, and use recursion, this is a great case for when working with scripts, and it does takes advantage of some of the main Erlang and Elixir features.

What’s Next?

This is episode V of Elixir with a Rubyist!!! I feel great about this series and really happy to be publishing it again! I’m looking forward for the next episodes, it feels we are starting to get into the fun stuff.

This is a series of short bar-like conversations around Elixir, it’s aimed to help developers that are trying to understand and wrap their heads around it.

On the next episodes we will finally get into the Task module, also discuss [head | tail], and the fun stuff. If you liked please let me know on the comments bellow and over Twitter.

Profile

João M. D. Moura

A Developer, Writer and a Passionate Speaker, that also loves Open Source.
Rails-api member and Elixir lover currently at Packlane.

Last Reads