4 Functions and Modules
4.2 Struct
defmodule Candidate do
defstruct [name: "qiushi", age: 18]
end
c = %Candidate{name: "tom"}
#> %Candidate{name: "tom", age: 18}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}
end4.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
endBehind 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
endis compiled into
defmodule Example do
require Feature
Feature.__using__(option: :value)
endSince 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])
#> 3By 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])
#> 3If 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/1In 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)
endWithout alias
defmodule Example do
def greeting(name), do: Sayings.Greetings.basic(name)
endIf 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)
endIt’s even possible to alias multiple modules at once:
defmodule Example do
alias Sayings.{Greetings, Farewells}
end