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 we do in other languages 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

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

In a module you 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:

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.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.