Apr 18, 2016
Table of contents:
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.
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.
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.
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
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.
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
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.
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.