Elixir methods

The best programming book I've read was Atomic Scala by Bruce Eckel and Dianne Marsh. It was so good that when I went for a job interview I chose to use Scala for the technical test because I was so in love with the language and its programming concepts. Well, that was premature and I bombed the interview but I'm still fond of the book and Scala.

As I learn Elixir, what I'm coming across in the literature doesn't fill me with the same excitement. I've read about Elixir structs, maps, lists and pattern matching too many times. I want to get to a working knowledge of the language and at this point, I don't really care if strings are UTF-8, how large a float is or the Erlang representation behind them.

When I'm on holiday in Spain, what I care about is how to say "Good morning, table for two please" not "Spanish is a Romance language that originated in the Iberian Peninsula and today has over 450 million native speakers in Spain and the Americas. It is a global language and the world's second-most spoken native language, after Mandarin Chinese".

So this is an ode to Atomic scala.

Methods aka. Function aka. Do something

A method groups programming logic that can be executed by name or anonymously. Simple functions are used to build more complex logic structures. You don't need a return statement.

Terms

  • Arity - the number of parameters to a method e.g List.first/1 means one parameter required for the function named first in the List module.
  • Typedef - i.e. @spec - explicit type information used to find errors. Learn more
  • Pattern matching - the method signature is determined by the type of arguments
  • Guard - a conditional that is checked before executing a function. Learn more
  • Default value - i.e. argument \\ value

Explanation

Named functions exist in modules and have the structure:

defmodule Module_name do
 # note the comma before 'do'
  def function_name (arg1), do
  ...
 end

 @spec function_name(type()) :: return_type()
 def function_name (arg1, arg2) when arg1 > 0 do
  ...
 end
end

Private functions start with defp instead of def.

Anonymous functions have two structures with fn and &. E.g.

function_name = fn (arg1, arg2) -> ... end
capture_function = &(&1 ... &2)

A header function is a function with the same name as a named function and is used to set the default value for the named function's argument(s). E.g.

defmodule Module_name do
  def function_name(arg1 \\ default_value)
 def function_name(arg1), do
    ...
  end
end

Examples

@spec multiply(number()) :: number()
defmodule Calculator do
  def multiply(arg1, arg2) when arg1 > 0 do
    arg1 * arg2
  end
end

Calculator.multiply(0, 2) # -> (FunctionClauseError) no function clause matching in Calculator.named_multiply/2
Calculator.multiply(2, 3) # -> 6

anon_multiply = fn (arg1, arg2) -> arg1 * arg2 end
capture_multiply = &(&1 * &2)

anon_multiply.(2, 4) # -> 8
capture_multiply.(5, 4) # -> 20

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

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

  def phrase, do: "Hello, "
end

Greeter.hello() # -> "Hello, world"
Greeter.hello("Mark") # -> "Hello, Mark"
Greeter.hello(["Mark", "Luke"]) # -> "Hello, Mark, Luke"