Learn Elixir — Module and Method/Function

Muhammad Tri Wibowo
5 min readDec 6, 2019

Let’s look this sample code below,

defmodule Coffees do
def hello_coffee do
"hi coffee lovers !"
end
end

at that code have 1 module named “Coffees” and 1 method inside “Coffees” ‘ module named “hello_coffee”

Module in elixir are collections of methods, but it’s different with class in OOP’s approach, module in elixir can’t instantiate as an object.

So, you can’t code like this

̶v̶a̶r̶1̶ ̶=̶ ̶n̶e̶w̶ ̶C̶o̶f̶f̶e̶e̶s̶

see, this diagram below.

method “hello_coffee” return a String.

in elixir, so many built in Modules, one of them is “String” module which is contain method/function “length” that compute total character of given string.

let’s look example below :

iex> String.length("hello")
5

In order to create our own modules in Elixir, we use the defmodule macro. We use the def macro to define functions in that module.

defmodule [module_name] do
def [method/function_name] do
//logic of method/function...
end
end

Method/function arguments

Inside a method/function, we can define argument/s,

syntax :

def method_name([argument1],[argument2],..) do
//logic of method/function...
end

for example :

defmodule Coffees do
def hello_coffee(name) do
"hi coffee lovers ! my name is " <> name
end
end
iex=> Coffees.hello_coffee("akhmad")
"hi coffee lovers ! my name is akhmad"

Function Naming and Arity

In elixir, function can named by the combination of given name and arity (number of arguments). This means you can do things like this:

defmodule Greeter do      # hello/0
def hello(), do: "Hello, anonymous person!"

# hello/1
def hello(name), do: "Hello, " <> name

# hello/2
def hello(name1, name2), do: "Hi, #{name1} and #{name2}"

end
iex> Greeter.hello() "Hello, anonymous person!"
iex> Greeter.hello("Fred") "Hello, Fred"
iex> Greeter.hello("Fred", "Jane") "Hi, Fred and Jane"

We’ve listed the function names in comments above. The first implementation takes no arguments, so it is known as hello/0; the second takes one argument so it is known as hello/1, and so on. Unlike function overloads in some other languages, these are thought of as different functions from each other. (Pattern matching, described just a moment ago, applies only when multiple definitions are provided for function definitions with the same number of arguments.)

Default arguments

functions in Elixir also support default arguments

syntax :

  def func_name(arg1, arg2 [\\ [default_argument_value]])   do
//logic is here
end

argument “\\ [default_argument_value”

example :

defmodule Concat do
def join(a, b, separator \\ " ") do
a <> separator <> b
end
end

iex=> Concat.join("Hello", "world")
"Hello world"
iex=> Concat.join("Hello", "world", "_")
"Hello_world"

Any expression is allowed to serve as a default value, but it won’t be evaluated during the function definition. Every time the function is invoked and any of its default values have to be used, the expression for that default value will be evaluated:

defmodule DefaultTest do
def dowork(x \\ "hello") do
x
end
end
iex> DefaultTest.dowork
"hello"
iex> DefaultTest.dowork 123
123
iex> DefaultTest.dowork
"hello"

Anonymous Function

Just as the name implies, an anonymous function has no name. As we saw in the Enum lesson, these are frequently passed to other functions. To define an anonymous function in Elixir we need the fn and end keywords. Within these we can define any number of parameters and function bodies separated by ->.

Let’s look at a basic example:

iex> sum = fn (a, b) -> a + b end 
iex> sum.(2, 3)
5

The & Shorthand

Using anonymous functions is such a common practice in Elixir there is shorthand for doing so:

iex> sum = &(&1 + &2) 
iex> sum.(2, 3)
5

As you probably already guessed, in the shorthand version our parameters are available to us as &1, &2, &3, and so on.

Public and Private Method/Function

Inside a module, we can define “public” functions with def macro and “private” functions with defp macro. A function defined with def macro can be invoked from other modules while a private function can only be invoked locally.

defmodule Math do
def public_sum(a, b) do
private_sum(a, b)
end

defp private_sum(a, b) do
a + b
end
end

iex=> Math.public_sum(1, 2)
3
iex=> Math.private_sum(1, 2)
** (UndefinedFunctionError)

Functions and Pattern Matching

Behind the scenes, functions are pattern-matching the arguments that they’re called with.

Say we needed a function to accept a map but we’re only interested in using a particular key. We can pattern-match the argument on the presence of that key like this:

defmodule Greeter do 
def hello(%{name: person_name}) do
IO.puts “Hello, “ <> person_name
end
end

These are the results we’ll get when we call Greeter.hello/1 with the fred map:

iex=> fred = %{name: "Fred", age: "95", favorite_color: "Taupe"}
iex=> Greeter.hello(fred)
"Hello, Fred"

It finds that there is a key that corresponds to name in the incoming map. We have a match! And as a result of this successful match, the value of the :name key in the map on the right (i.e. the fred map) is bound to the variable on the left (person_name).

What happens when we call the function with a map that doesn’t contain the :name key?

iex=> someone = %{age: "65", favorite_color: "Green"}
iex=> Greeter.hello(someone)
** (FunctionClauseError) no function clause matching in Greeter.hello/1 The following arguments were given to Greeter.hello/1: # 1 %{age: "65", favorite_color: "Green"} iex:12: Greeter.hello/1

The reason for this behavior is that Elixir pattern-matches the arguments that a function is called with against the arity the function is defined with.

Guards

Function declarations also support guards and multiple clauses. If a function has several clauses, Elixir will try each clause until it finds one that matches.

In the following example we have two functions with the same signature, we rely on guards to determine which to use based on the argument’s type:

defmodule Greeter do
def hello(names) when is_list(names) do
names
|> Enum.join(", ")
|> hello
end

def hello(name) when is_binary(name) do
phrase() <> name
end

defp phrase, do: "Hello, "
end
iex> Greeter.hello "Akhmad"
"Hello, Akhmad"
iex> Greeter.hello ["Sean", "Steve"]
"Hello, Sean, Steve"

is_ prefix (is_foo)

Type checks and other boolean checks that are allowed in guard clauses are named with an is_ prefix.

Examples: Integer.is_even/1, Kernel.is_list/1

These functions and macros follow the Erlang convention of an is_ prefix, instead of a trailing question mark, precisely to indicate that they are allowed in guard clauses.

Note that type checks that are not valid in guard clauses do not follow this convention. Examples: Keyword.keyword?/1, Regex.regex?/1

Function/method with Trailing question mark (foo?)

Functions that return a boolean are named with a trailing question mark.

Examples: Keyword.keyword?/1, Mix.debug?/0, String.contains?/2

However, functions that return booleans and are valid in guards follow another convention, described next.

--

--