cult3

Working with Functions and Modules in Elixir

Apr 18, 2016

Table of contents:

  1. Defining Functions
  2. What is a Module?
  3. Organising Hierarchical Modules
  4. Defining Private Functions
  5. Importing and Aliasing
  6. Other Function and Modules tricks and tips
  7. Conclusion

Elixir is a functional language and so as you would expect, Functions play an important role when developing Elixir applications! Typically in applications written in a functional language you will see lots of little functions that act on data.

In today’s exploration of Elixir, we’re going to be looking at defining Functions and how we can use Modules to organise them.

Defining Functions

Functions in Elixir follow the same naming conventions as a variables (as we saw in Understanding the Types in Elixir).

Concretely, a Function must start with a lowercase letter or an underscore, and can only contain alphanumeric characters and underscores.

Functions can also contain the characters ? or !. If you are currently a Ruby programmer you will feel right at home using these characters in your function names.

Using ? usually indicates that the function will return a boolean value, and using ! usually indicates that calling this function will throw an error if something goes wrong or that there is some kind of side-effect.

For example you might have a valid? function that checks to see if something valid and therefore it will return a true or false value.

Or you might have a check! function that will perform the check and throw a runtime error if the check does not pass successfully.

Elixir does not enforce that you only return boolean values from a function that ends in ? or that you throw an error when using a function with a ! as these are just conventions of the community.

However, it’s usually best to follow these types of conventions because it makes reading other developer’s Elixir code much easier.

Here is what a basic Function definition looks like:

def sum(a, b) do
  a + b
end

As you can see you define a function using the def construct, the name of the function and a list of arguments. The actual work of the function is defined between the do...end block.

You don’t have to tell Elixir what types you are passing around as Elixir is a dynamic language.

And you don’t have to explicitly return a value from the function as the value of the last expression will automatically be returned for you.

What is a Module?

A Module is basically a way of organising a collection of functions into a namespace.

For those of you who are coming to Elixir from Ruby the usage of Modules should be very familiar. A Module basically acts as a namespace.

If you aren’t familiar with namespacing, it’s basically a technique to organise functions into buckets.

Normally if you had two functions with the same name they would clash. But if there are in different namespaces, they won’t clash.

Let’s write out our first Module. Copy the following code into a new file called calculator.ex:

defmodule Calculator do
  def sum(a, b) do
    a + b
  end
end

Here we have defined a module called Calculator with a single sum function.

As you can see, we define the Module using the defmodule construct.

The name of the Module must conform to certain conventions. It should start with an uppercase letter and it should use Camel Case.

The name itself can contain alphanumeric characters and underscores.

To run this Module you can run the following command from Terminal:

iex calculator.ex

This will compile the Module and drop you into iex with your code loaded and ready to go.

To use your new Module, you would use the following syntax.

Calculator.sum(1, 2)

As you can see, first we use the name of the Module and then the name of the Function we want to invoke, passing the required arguments to the Function.

Organising Hierarchical Modules

If you are an experienced developer, you will know that you very rarely only have one layer to a namespace. You typically end up with multiple Modules organised into a hierarchy.

In Elixir, you will often see periods used to organise Modules into a hierarchy. For example:

defmodule Calculator.Addition do
  def sum(a, b) do
    a + b
  end
end

The period in the above example is basically just a convenience for defining Modules in a hierarchy.

To invoke the sum function you would need to use the full name of the Module:

Calculator.Addition.sum(1, 2)

You could also re-write the example above to use nested Modules:

defmodule Calculator do
  defmodule Addition do
    def sum(a, b) do
      a + b
    end
  end
end

This is exactly the same as the period notation and so you would access the Modules in the same way.

Calculator.Addition.sum(1, 2)

As a side note, an Elixir Module must be defined in a single file, but you can define multiple Modules in a the same file.

For example, you could add another Module like this:

defmodule Calculator do
  defmodule Addition do
  end

  defmodule Subtraction do
  end
end

Defining Private Functions

The Functions in the examples we’ve looked at so far have all been public Functions. This means the Function can be invoked outside of the Module.

Sometimes you will want to define private functions that should only be accessed from inside of the Module.

To define a private function you can use the defp construct. For example:

defmodule Greeting do
  def hello_public do
    IO.puts("Hello from a public function")
  end

  defp hello_private do
    IO.puts("Hello from a private function")
  end
end

Here we have two functions that will print a message to the screen, but one is public and one is private.

If you load this Module into iex and attempt to call the two functions you should get two different responses.

The hello_public will return the message as it should, but if you try to call the hello_private function you will get an error indicating an undefined function.

The private function can only be used inside of the Module. So to see this in action, we can modify the Module to call the private function from the public function:

defmodule Greeting do
  def hello_public do
    hello_private
  end

  defp hello_private do
    IO.puts("Hello from a private function")
  end
end

Now if you reload this Module and call the hello_public function, you should see the response from the hello_private function.

Importing and Aliasing

In the first example from the previous section we had the following Module:

defmodule Greeting do
  def hello_public do
    IO.puts("Hello from a public function")
  end

  defp hello_private do
    IO.puts("Hello from a private function")
  end
end

Here we are calling the puts function on the IO Module.

To remove this duplication we can import the IO module. This means we no longer need to prefix the Module name when calling it’s functions:

defmodule Greeting do
  import IO

  def hello_public do
    puts("Hello from a public function")
  end

  defp hello_private do
    puts("Hello from a private function")
  end
end

Alternatively you can also give a Module an alias:

defmodule Greeting do
  alias IO, as: Say

  def hello_public do
    Say.puts("Hello from a public function")
  end

  defp hello_private do
    Say.puts("Hello from a private function")
  end
end

Other Function and Modules tricks and tips

We’ve covered a lot of the basics of using Functions and Modules in Elixir in today’s tutorial, but we’ve got time for two more little idioms of the language.

Firstly, when calling a Function, the parenthesis are actually optional.

For example, if we had this function:

defmodule Calculator do
  def sum(a, b) do
    a + b
  end
end

You could call the sum function without parenthesis like this:

Calculator.sum(1, 2)

If you are a Ruby developer, this will probably look pretty normal to you.

Leaving off the parenthesis is completely up to you. Personally I prefer to use parenthesis on functions that have arguments and leave them off for functions that don’t have any arguments:

# with
Calculator.sum(1, 2)

# without
Greeting.hello()

Secondly, Functions in Elixir tend to be really small and concise (for reasons we will get explore in the coming weeks).

When a Function is simple you can define it as a single line:

defmodule Calculator do
  def sum(a, b), do: a + b
end

Again, whether you use single line functions are multiple line functions is entirely up to you.

Personally I find single line functions like the one above easier to read and neater to look at, especially when the function is really small.

Conclusion

Functions and Modules will be two of the important building blocks you will use to create Elixir applications. Functions are particular important in a Functional language such as Elixir.

If you are familiar with a language like Ruby, nothing we’ve covered today should be really mind blowing. As you can probably see, some of the nice syntax features and ideas have been used in Elixir.

Next week we will continue to look at some of the important aspects of using Functions in Elixir.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.