Elixir Gotchas

28 Sep 2020

.ex vs .exs

  • .ex - compiled
  • .exs - interpreted (don't need to be compiled for each change)

use vs import vs require vs alias

  • Use - gives full control to another module and calls its __using__ macro
  • Import - references a function from another module with its name only
  • Require - ensures a module is compiled first (needed if they contain macros)
  • Alias - creates additional names for modules (useful for lengthy module names)

Accessing Collection Elements

We can access map values with the access operator.

> user = %{name: "example", email: "example@example.com"}
> user[:name]
"example"

The access operator does not work on regular lists or tuples.

> list = ["a","b","c"]
> list[1]
** (ArgumentError) the Access calls for keywords expect the key 
   to be an atom, got: 1
    (elixir 1.10.4) lib/access.ex:311: Access.get/3

Accessing list elements is done with Enum library functions.

> list = ["a","b","c"]
> Enum.at(list, 1)
"b"
> Enum.fetch(list, 1)
"b"
> Enum.fetch!(list, 1)
"b"

Enum.fetch/2 will return :error if the given index is out of bounds.

> Enum.fetch(list, 3)
:error

Enum.fetch!/2 will throw an error if the given index is out of bounds.

> Enum.fetch!(list, 3)
** (Enum.OutOfBoundsError) out of bounds error
    (elixir 1.10.4) lib/enum.ex:881: Enum.fetch!/2

The access operator does work on keyword lists.

> list = [name: "bob"]
> list[:name]
"bob"

Anonymous Functions

Anonymous function declarations begin with fn and end with end.

A common mistake is forgetting the end token.

> hello = fn x -> IO.puts "hello, #{x}" end 

Anonymous functions are invoked with the dot operator.

> hello.("world")
hello, world

Module Attributes

Modules can contain something that looks like instance variables from Ruby.

These are called module attributes.

defmodule Example do
  @default_config %{format: "txt", tabs: false}
  @moduledoc """
  Documentation for `Example`.
  """
end

They can be read but not modified at runtime.

Default Parameters

Default parameters are denoted with \\ instead of =.

def hello(str \\ "world") do
  IO.puts "hello, #{str}"
end

Structs

Elixir provides a type of map with compile-time checks called a struct.

defmodule User do
  defstruct username: "example", email: "example@example.com"
end
> user = %User{}

Although a struct looks similar to a map, we cannot access its values with the access operator.

> user[:email] 
** (UndefinedFunctionError) function User.fetch/2 is undefined (User does not 
   implement the Access behaviour)
    (http_test 0.1.0) User.fetch(%User{email: "example@example.com", 
   username: "default_username"}, :email)
    (elixir 1.10.4) lib/access.ex:286: Access.get/3

Instead we use the dot operator.

> user.email
"example@example.com"