Dario Ghilardi

Dario Ghilardi

Stories and articles for software developers.

Modules and named functions in Elixir

In every object oriented programming language we model the domain with classes. Every object we generate from our classes is a container of state represented by your instance variables, and behavior, represented by your methods. But Elixir is a functional programming language therefore it doesn’t have classes. It also doesn’t allow you to store state in objects. But of course it allows the definition of behavior, using functions.

Let’s start with the description of the most common scenario: two simple named functions inside a module.

defmodule Names do
  def full_name(name, surname) do
    name <> "" <> surname
  end

  def capitalized_name(name) do
    String.capitalize(name)
  end
end

A module is nothing more than a functions container, a way to group functions together. Our module Names, defined above, groups together the functions full_name/2 and capitalized_name/1.

Like with classes, in a module we can set the visibility of a function: using def we define a function that is available to all callers, using defp we define a function that is visible only inside the module itself.

Function arguments

In a module we can define multiple functions with the same name, and then leverage on pattern matching for them to be selected, as you can see in the following example:

defmodule Names do
  def full_name(name) do
    String.capitalize(name)
  end

  def full_name(name, surname) do
    String.capitalize(name) <> " " <> String.capitalize(surname)
  end
end

Names.full_name("john")               # John
Names.full_name("john", "doe")        # John Doe
Names.full_name("john", "doe", "jr")  # (UndefinedFunctionError) undefined function: Names.full_name/3

A function may also have default arguments:

defmodule Names do
  def full_name(name, surname, title \\ "Mr") do
    title <> " " <> String.capitalize(name) <> " " <> String.capitalize(surname)
  end
end

Names.full_name("john", "doe")            # Mr John Doe
Names.full_name("melissa", "doe", "Ms")   # Ms Melissa Doe

Function clauses

Elixir supports function clauses, as explained in the following example:

defmodule Names do
  def name(name, married)  when married == true do
    "Mrs" <> " " <> String.capitalize(name)
  end

  def name(name, married)  when married == false do
    "Ms" <> " " <> String.capitalize(name)
  end
end

Names.name("melissa", true)  # Mrs Melissa
Names.name("Hilary", false)  # Ms Hilary

After the when statement of the function definition we restrict the pattern matching to the evaluation of the clause, based on the argument value. If the clause is not matched, Elixir proceeds to the following function evaluation in order to find a match, throwing an error if it doesn’t find any.

Function notations and capturing

While referring to a function in this post, I frequently used the function_name/arity notation, where arity is just the number of parameters for the defined function. Turns out this notation is also useful in case of function capturing, to store the function in a variable:

defmodule Names do
  def full_name(name, surname) do
    String.capitalize(name) <> " " <> String.capitalize(surname)
  end
end

fun = &Names.full_name/2
# &Names.full_name/2

fun.("John", "Doe")
# "John Doe"

That’s all for today.

More information on this topics are available in the official Elixir modules guide. Taking a look is worth the effort.

Improve your skills week by week.

Subscribe to my free newsletter to receive original quality content weekly.

By opting in here, you agree to receive value-adding, non-spammy, exclusive content that will help improve your frontend skills, with very few offers, if any. You also agree to the privacy policy.