4 Functions and Modules

4.1 Modules

defmodule MyModule do
  def my_function(arg1, arg2) do
    # ...
  end
end

4.2 Struct

defmodule Candidate do
    defstruct [name: "qiushi", age: 18]
end

c = %Candidate{name: "tom"}
#> %Candidate{name: "tom", age: 18}

4.3 Anoynmous Functions

square = fn x -> x * x end
square.(5)
#> 25

4.3.1 Anoynmous Functions with Pattern Matching

logo_file = fn
  (:democratic) -> "donkey.png"
  (:republican) -> "elephant.png"
  (:green) -> "flower.png"
  (:other) -> "other.png"
end

logo_file.(:green)
#> "flower.png"

4.4 Module attributes

Module attributes are computed at compile time.

defmodule Mascot do
  @mascots %{
    democratic: "monkey",
    republican: "elephant",
    green: "plant",
  }

  @parties Map.keys(@mascots)

  def mascot(party) do
    @mascots[party]
  end


  def logo(party, size) when party in @parties do
    m = mascot(party)
    do_logo_size(m, size)
  end

  def logo(party, size) do
    {:error, :unknown_party}
  end

  defp do_logo_size(mascot, :small), do: "#{mascot}_small.png"
  defp do_logo_size(mascot, :normal), do: "#{mascot}_normal.png"
  defp do_logo_size(mascot, :large), do: "#{mascot}_large.png"
  defp do_logo_size(mascot, unknown), do: {:error, :unknown_size}

end

4.5 Protocols

4.6 Use, Import and Alias

4.6.1 Use

The use macro is frequently used as an extension point. This means that, when you use a module FooBar, you allow that module to inject any code in the current module, such as importing itself or other modules, defining new functions, setting a module state, etc.

For example, in order to write tests using the ExUnit framework, a developer should use the ExUnit.Case module:

defmodule AssertionTest do
  use ExUnit.Case, async: true

  test "always pass" do
    assert true
  end
end

Behind the scenes, use requires the given module and then calls the __using__/1 callback on it allowing the module to inject some code into the current context. Some modules (for example, the above ExUnit.Case, but also Supervisor and GenServer) use this mechanism to populate your module with some basic behaviour, which your module is intended to override or complete.

Generally speaking, the following module:

defmodule Example do
  use Feature, option: :value
end

is compiled into

defmodule Example do
  require Feature
  Feature.__using__(option: :value)
end

Since use allows any code to run, we can’t really know the side-effects of using a module without reading its documentation. Therefore use this function with care and only if strictly required. Don’t use use where an import or alias would do.

4.6.2 Import

If we want to import functions rather than aliasing the module we can use import:

iex> import List
#> nil
iex> last([1, 2, 3])
#> 3

By default all functions and macros are imported but we can filter them using the :only and :except options.

To import specific functions and macros, we must provide the name/arity pairs to :only and :except. Let’s start by importing only the last/1 function:

iex> import List, only: [last: 1]
iex> first([1, 2, 3])
#> ** (CompileError) iex:13: undefined function first/1
iex> last([1, 2, 3])
#> 3

If we import everything except last/1 and try the same functions as before:

iex> import List, except: [last: 1]
iex> first([1, 2, 3])
#> 1
iex> last([1, 2, 3])
#> ** (CompileError) iex:3: undefined function last/1

In addition to the name/arity pairs there are two special atoms, :functions and :macros, which import only functions and macros respectively:

import List, only: :functions import List, only: :macros

4.6.3 Alias

Allows us to alias module names; used quite frequently in Elixir code:

defmodule Sayings.Greetings do
  def basic(name), do: "Hi, #{name}"
end

defmodule Example do
  alias Sayings.Greetings

  def greeting(name), do: Greetings.basic(name)
end

Without alias

defmodule Example do
  def greeting(name), do: Sayings.Greetings.basic(name)
end

If there’s a conflict between two aliases or we just wish to alias to a different name entirely, we can use the :as option:

defmodule Example do
  alias Sayings.Greetings, as: Hi

  def print_message(name), do: Hi.basic(name)
end

It’s even possible to alias multiple modules at once:

defmodule Example do
  alias Sayings.{Greetings, Farewells}
end