Today I Learned

A Hashrocket project

109 posts about #elixir

Run ExUnit tests in the order they are defined

You might have a test module like this.

ExUnit.start

defmodule TestOrderTest do
  use ExUnit.Case

  test "first" do
    assert true
  end

  test "second" do
    assert true
  end
end

And when you run it, second runs before first!

In general this is fine, because every test should pass no matter which one runs first. In certain circumstances however, lets say you’re working through some Exercism.io challenges, you might want them to run in order.

To do so include the configuration seed: 0

Elixir.configure seed: 0

And tackle every test in the order they’ve been given to you!

Do You Have The Time? - Part 2

In Do You Have The Time?, I demonstrated a way of using an Erlang function to get at and work with time in Elixir. As of Elixir 1.3, there is now a Time module that provides a sigil and some functions for working with time.

We can use Elixir’s Time module to simplify the example from the previous iteration of this TIL:

defmodule TickTock do
  def current_time do
    Time.from_erl!(:erlang.time)
    |> Time.to_string
  end
end

> TickTock.current_time
"19:58:12"

Check For A Substring Match

Using Erlang’s :binary.match function, you can easily check if a string has a matching substring.

> :binary.match("all food is good", "foo")
{4, 3}
> :binary.match("all food is good", "bar")
:nomatch

As you can see, the return value on a successful match is a tuple with the index of where the match starts and the length of the match. If there is no match, the :nomatch atom is returned.

See the match/2 and match/3 docs for more details.

source

Do You Have The Time?

Elixir doesn’t come with any standard ways of getting at or working with time. There are packages like Timex out there that we can pull in to our projects. However, if we don’t have need for a full-featured date/time library, we can opt for a simpler solution.

Erlang can give us the time.

defmodule TickTock do
  def current_time do
    {hh,mm,ss} = :erlang.time
    "#{hh}:#{mm}:#{ss}"
  end
end

> TickTock.current_time
"11:47:13"

Documentation Lookup With Vim and Alchemist

Which argument position is the accumulator for Enum.reduce/3?

How does group_by work?

I find myself fairly frequently jumping from vim to Chrome to do Google searches for Elixir standard lib documentation. It gets the job done, but it is kinda slow and I’d prefer to avoid the context switch.

With alchemist.vim, Elixir documentation lookup is at your finger tips. Just move the cursor over the module or function you are curious about and hit K (from normal mode).

Curious about Enum.reduce? Type it out in your current Vim buffer, move the cursor over it, and hit K.

Updating Values In A Map

When working with maps in any language, you often need a way to update key-value pairs. Furthermore, you will need a way to handle keys that are not already present in the map, generally associating some default value.

In Elixir, the Map module provides the get_and_update/3 function as a way of accomplishing such a task.

Let’s use a score counting example to see it in action:

> scores = %{}
%{}
# jake scores a point
> {_, scores} = Map.get_and_update(scores, :jake, fn(x) -> {x, (x || 0) + 1} end)
{nil, %{jake: 1}}
# chris scores a point
> {_, scores} = Map.get_and_update(scores, :chris, fn(x) -> {x, (x || 0) + 1} end)
{nil, %{chris: 1, jake: 1}}
# jake scores another point
> {_, scores} = Map.get_and_update(scores, :jake, fn(x) -> {x, (x || 0) + 1} end)
{1, %{chris: 1, jake: 2}}
# final scores
> scores
%{chris: 1, jake: 2}

We use (x || 0) + 1 as a way of providing an initial score for new keys.

The update function is expected to return a tuple with the original value and the updated value.

See the docs for more details.

Pipe into operators

Since Elixir operators are macros defined on Kernel or in a submodule of Kernel, we can pipe into them when we call the function on Kernel:

true |> || false # this raises a syntax error

true |> Kernel.||(false) # valid Elixir

Real world example from the Phoenix source: https://github.com/phoenixframework/phoenix/blob/master/lib/phoenix/router/helpers.ex#L31-L36

Reversing A List

To efficiently work with and transform lists in Elixir, you will likely need utilize a list reversing function from time to time. Your best bet is to reach for the Erlang implementation which is available as part of the lists module.

Here are a couple examples of how to use it:

> :lists.reverse([1,2,3])
[3, 2, 1]
> :lists.reverse([1, :a, true, "what", 5])
[5, "what", true, :a, 1]

Note: though I said “transform lists” above, what is actually going on is that a new version of the list representing my transformation is being created, per Elixir’s functional nature.

`Enum.chunk_by`

I discovered a pretty cool Elixir function this weekend, Enum.chunk_by.

Backstory: I wanted to group a list of strings by equality, while preserving the list order.

Enum.chunk_by takes an enumerable and a function, and breaks that enumerable into an enumerable when the function returns a new or different result.

Here’s a simple example:

iex> Enum.chunk_by(['A', 'A', 'A', 'B', 'A'], fn(l) -> l end)
[['A', 'A', 'A'], ['B'], ['A']]

Anytime l changes, a new list is created.

Slightly more complex:

iex> Enum.chunk_by([1, 2, 3, 4, 5], fn(n) -> rem(n, 3) == 0 end)
[[1, 2], [3], [4, 5]]

The function only returns something different (true) on 3, so 3 is assigned to its own list.

Simplify System.cmd with W sigil

Say we have the following shell command to get the the unix timestamp of the last commit:

$ git log -1 --date=short --pretty=format:%ct

1470067380

In elixir. One might do it like so:

System.cmd("git", ["log", "-1", "--date=short", "--pretty=format:%ct]) 
|> elem(0)

#=> "1470067380"

And here is just a simpler syntax for the same thing:

System.cmd("git", ~w[log -1 --date=short --pretty=format:%ct])
|> elem(0)

#=> "1470067380"

This is just a little clean and feels more like the original command.

Ruby-Like `split` in Elixir

Elixir’s split function is a bit different from the Ruby version you might be familiar with.

Here’s Ruby’s split:

2.1.0 :001 > "FOOBAR".split("")
 => ["F", "O", "O", "B", "A", "R"]

And Elixir’s:

iex(1)> String.split("FOOBAR", "")
["F", "O", "O", "B", "A", "R", ""]

Whoa, what’s that extra "" doing in there? Drink in the Elixir. A small amount of message board reading has led me to conclude this was very deliberate and isn’t going to be changed.

Here’s one way to get a Ruby-like split:

iex(1)> String.split("FOOBAR", "", trim: true)
["F", "O", "O", "B", "A", "R"]

alias is lexically scoped

Aliases are lexically scoped, allowing you to alias a module inside of a function and that alias will only exist in that function’s scope. Also TIL: You can even alias over Elixir standard lib module names.

defmodule Cartography do
  def north_america do
   alias Earth.NorthAmerica.Map
   #Map is now Earth.NorthAmerica.Map
   # You can get to the normal Map module using Elixir.Map
  end

  def foo do
    #Map is still Elixir.Map
  end
end

Named Captures with Elixir Regular Expressions

I can’t believe I’m just now learning about this! How neat!

iex(0)> string = "Here is my phone number: 999-111-1234"
"Here is my phone number: 999-111-1234"
iex(1)> regex = ~r/number: (?<phone>[\d|-]+)/
~r/number: (?<phone>[\d|-]+)/
iex(2)> Regex.named_captures(regex, string)
%{"phone" => "999-111-1234"}

Write a regex capture with ?<> in the beginning and get back a map of your captures with Regex.named_captures/2

Invoke Elixir Functions with Apply

I had a weird problem yesterday: I wanted to pass a function into another function and then invoke it, all for the sake of a friendly API.

Elixir’s apply to the rescue. Here’s the rough implementation.

defmodule Example do
  def do_things(index, rotation, operator) do
    apply(Kernel, operator, [index, rotation])
  end
end

iex> Example.do_things(10, 20, :+)
30
iex> Example.do_things(10, 20, :-)
-10

apply also accepts your own functions:

defmodule Example do
  def do_things(index) do
    apply(__MODULE__, :make_bigger, [index])
  end

  def make_bigger(a) do
    a * 10000
  end
end

iex> Example.do_things(100)
1000000

The module name (Example) will work in place of __MODULE__, if you prefer. This seems like a pretty powerful feature.

Tab Completion in IEx

IEx has tab completion of library functions, just like IRB. To see all the built-in Stream functions, just type Stream. and hit TAB, and you’ll get output like this:

iex(1) Stream.
Reducers        chunk/2         chunk/4         chunk_by/2
concat/1        concat/2        cycle/1         dedup/1
dedup_by/2      drop/2          drop_while/2    each/2
filter/2        filter_map/3    flat_map/2      interval/1
into/3          iterate/2       map/2           reject/2
repeatedly/1    resource/3      run/1           scan/2
scan/3          take/2          take_every/2    take_while/2
timer/1         transform/3     transform/4     unfold/2
uniq/2          with_index/2    zip/2

This is a nice feature when you’re experimenting in the REPL.

h/t Micah Cooper

Find and Open Port with Elixir

iex(1)> {:ok, port} = :gen_tcp.listen(0, []) #listen on an available port
{:ok, #Port<0.1465>}
iex(2)> {:ok, port_number} = :inet.port(port) #get the port number of that port
{:ok, 63470}
iex(3)> port_number #here is the port number!
63470
iex(4)> Port.close port #go ahead and close that port if you want
true

Useful for maybe automating the deployment of a plug app.

An Agent reference can be pid or name.

Each function of the Agent module takes an agent as the first argument:

get(agent, fun, timeout \\ 5000)

but this can be a couple of different things. The documentation defines the agent type as:

agent :: pid | {atom, node} | name

#The agent reference

name ::
  atom |
  {:global, term} |
  {:via, module, term}

#The agent name

So really five different types of values are valid. The pid is a value returned from calling start_link.

> {:ok, agent_pid} = Agent.start_link(fn -> [:thing] end)
> Agent.get(agent_pid, fn (state) -> hd(state) end)
:thing

The agent can be referenced by a name atom also. In the documentation example that name atom is the __MODULE__ for the module that wraps access to the agent. The name for the agent is declared by passing a name option into the start_link function call.

> Agent.start_link(fn -> [:thing] end, name: __MODULE__)
> Agent.get(__MODULE__, fn (state) -> hd(state) end)
:thing

I’m note sure about the {:global, term} and {:via, module, term} values that seem to be valid. I would love to see examples for those.

Dynamically Generating Atoms

Atoms are constants where their name is their own value.

The use of atoms like :ok and :error show up all over the place in Elixir. These are atoms that tend to be statically defined. Atoms can also be dynamically defined using string interpolation.

For example, I can generate a handful of atoms by mapping over a range of integers.

> Enum.map(1..5, &(:"some_atom_#{&1}"))
[:some_atom_1, :some_atom_2, :some_atom_3, :some_atom_4, :some_atom_5]

Note: atoms are not garbage collected. If you dynamically generate atoms in excess, you may run your VM out of heap space.

Periodic messages in Elixir

Today I needed to implement a task that happens once every 24 hours. I immediately thought, well this should be a cron job, but my co-worker suggested using the send_after method in a GenServer instead.

    Process.send_after(self, {:"$gen_cast", :get_data}, interval)

The third argument is a milliseconds argument that determines when the call will be processed. The $gen_cast is a hackish type of thing to get the GenServer to handle the call with handle_cast.

The periodism should start in the init and whenever handle_cast is called, it should schedule a new time that the message will be sent.

  def init(state) do
    work
    Process.send_after(self, {:"$gen_cast", :get_data}, interval)
    {:ok, state}
  end

  def handle_cast(:get_data, state) do
    Process.send_after(self, {:"$gen_cast", :get_data}, interval)
    work
    {:noreply, state}
  end

H/T Micah Cooper

`hd` in Guard Tests

The hd method in Elixir returns the head of a list. It’s kind of like List.first, but raises an error on an empty list.

iex(0)> hd [1,2,3]
1
iex(1)> hd []
** (ArgumentError) argument error
    :erlang.hd([])

Browsing the docs, I learned something interesting: hd is allowed in guard tests.

Here’s a contrived implementation of this behavior:

# example.ex
defmodule TestModule do
  def header(list) when hd(list) == 1, do: IO.puts "We did it"
end
iex(0)> TestModule.header([1,2,3])
We did it
:ok

When the guard’s conditions aren’t met, an error is raised:

iex(1)> TestModule.header([2,3,4])
** (FunctionClauseError) no function clause matching in TestModule.header/1
    iex:13: TestModule.header([2, 3, 4])

h/t Chris Erin & Micah Cooper

Assert An Exception Is Raised

Elixir’s ExUnit comes with a number of different ways to make assertions in your tests. One of those functions is assert_raise which allows you to test that a particular exception is raised when the given function is invoked.

Using assert_raise/2 looks something like this:

assert_raise FunctionClauseError, fn ->
  Enum.chunk([1,2,3], 0)
end

The assert_raise/3 form is also available which allows you to test both the type of exception and the resulting message.

assert_raise FunctionClauseError, ~r/^no function clause matching/, fn ->
  Enum.chunk([1,2,3], 0)
end

Using the regex sigil for the second argument is generally a good way to go to keep tests from getting too brittle.

Capture IO.puts in ExUnit tests

Whenever writing a CLI you often have to communicate something to the user via IO.puts, and while some languages make it complicated to capture output in a test, Elixir makes it extremely straight-forward.

All we have to do is import the ExUnit.CaptureIO module into our test which exposes the capture_io method.

Example

Implementation:

defmodule MyApp.CLI do
  def main(argv), do
    # parse args ...
    # return :help if the -h switch is supplied
    # pass to process
    argv
    |> parse_args
    |> process
  end
  
  def process(:help) do
    IO.puts "usage: my_app <arg1> <arg2>"
  end
end

IO capturing test:

defmodule CliTest do
  use ExUnit.Case
  
  import ExUnit.CaptureIO
  
  test "prints usage instructions when the help switch is supplied" do
    execute_main = fn ->
      MyApp.CLI.main(["-h"])
    end
    
    assert capture_io(execute_main) =~ "usage:"
  end
end

For more information read the ExUnit.CaptureIO documentation

String Interpolation With Just About Anything

Coming to Elixir from Ruby, I am used to being able to interpolate literally anything into a string. In Elixir, this is not the case.

By default, it handles strings, atoms (including nil, true, false and module name aliases like String – which are all just atoms behind the scenes), integers, floats, and some lists. That’s it.

There are two approaches you can take to interpolate everything else into a string. The easier approach is to use Kernel.inspect/2.

> IO.puts "A map #{inspect %{a: 1, b: 2}}"
A map %{a: 1, b: 2}

The other approach is to implement the String.Chars protocol for the thing that you are trying to print. You can read more about that in Elixir String Interpolation for Rubyists.

Pattern Matching In Anonymous Functions

Pattern matching shows up everywhere in Elixir, even where you may not be expecting it. When declaring an anonymous function, you can use pattern matching against different sets and shapes of input parameters to invoke different behaviors.

Here is an example of how you might use this:

> handle_result = fn
  {:ok, result} -> IO.puts "The result is #{result}"
  :error -> IO.puts "Error: couldn't find anything"
end
#Function<6.50752066/1 in :erl_eval.expr/5>

> Map.fetch(%{a: 1}, :a) |> handle_result.()
The result is 1
:ok
> Map.fetch(%{a: 1}, :b) |> handle_result.()
Error: couldn't find anything
:ok

source

Quitting IEx

There are two ways to quit out of an Interactive Elixir shell. The standard way is with Ctrl-c. This gives you a list of options, one of which is a for abort. This will terminate your IEx session and drop you back on the command line where the process started.

Additionally, IEx also understands Ctrl-\ which is control key that will terminate just about any interactive environment. This command will cause IEx to immediately exit with no prompt. Note: IEx does not, however, respond to Ctrl-d. source

Change resource param in Phoenix routes

Phoenix, like Rails, gives you the option to specify a resource in your routes in order to generate the typical CRUD actions we deal with in a controller. The dynamic segment which identifies the resource is :id by default.

resources "orders", OrderController

The update route would look like this if we run mix phoenix.routes.

order_path  PATCH  /orders/:id    MyApp.OrderController :update

If our table doesn’t use “id” as a primary key, or we want to change the field we use to find our resource, we can specify it as an option in our route:

resources "orders", OrderController, param: "some_field"

Now our route is updated:

order_path  PATCH  /orders/:some_field    MyApp.OrderController :update

https://github.com/phoenixframework/phoenix/blob/master/lib/phoenix/router.ex#L482

Execute Raw SQL In An Ecto Migration

If you are performing a database migration with Ecto, perhaps the most straightforward approach is to use Ecto’s DSL. However, the DSL may not always be the best choice. Being able to write raw SQL gives you more control. It will also enable you to use database features that may not be directly or easily available through the DSL.

Raw SQL can be included in a Ecto migration with a combination of Elixir’s heredoc syntax and the Ecto.Migration#execute/1 function. You’ll also need to provide both an up and down function to ensure that your migrations are reversible.

defmodule MyApp.Repo.Migrations.CreatePostsTable do
  use Ecto.Migration

  def up do
    execute """
      create table posts (
        id serial primary key,
        title varchar not null,
        body varchar not null default '',
        inserted_at timestamptz not null default now(),
        updated_at timestamptz not null default now()
      );
    """
  end

  def down do
    execute "drop table posts;"
  end
end

Word Lists For Atoms

The ~w sigil works similarly to Ruby’s %w (word array notation). It allows you to create a list of words (i.e. strings).

> ~w(one two three)
["one", "two", "three"]

It sets itself apart though with some modifiers. The default behavior matches the s modifier (for strings).

> ~w(one two three)s
["one", "two", "three"]

Where it gets more interesting is with the a modifier allowing you to create a list of atoms.

> ~w(one two three)a
[:one, :two, :three]

Note: there is a third modifier, c, for char lists.

> ~w(one two three)c
['one', 'two', 'three']

source

3 parts of routing a websocket

First, there’s a socket declaration in the lib/appname/endpoint.ex file.

  socket "/socket", AppName.UserSocket

Second, there’s a transport declaration in the web/channels/user_socket.ex file.

  transport :ws, Phoenix.Transports.WebSocket

Now, the websocket will be available at the url ws://localhost:4000/socket/ws the final path segment of the url being added by the first argument of the transport declaration.

Finally, the channel topic is also declared in the user_socket.ex file.

channel "app_topic:*", AppName.MyChannel

Now every json body that has a topic attribute of app_topic will be routed to MyChannel.AppName where it will be handled appropriately based on the event attribute.

{"topic":"app_topic:something","event":"phx_join","payload":{},"ref":"1"}

List Functions For A Module

During an iex session, I can do a little introspection on modules using either the __info__/1 function or Erlang’s module_info/0 function. In particular, I can pass :functions to either one to get a list of the functions for that module.

This is what __info__/1 looks like for the functions of the List module:

> List.__info__(:functions)
[delete: 2, delete_at: 2, duplicate: 2, first: 1,
 flatten: 1, flatten: 2, foldl: 3, foldr: 3, insert_at: 3,
 keydelete: 3, keyfind: 3, keyfind: 4, keymember?: 3,
 keyreplace: 4, keysort: 2, keystore: 4, keytake: 3,
 last: 1, replace_at: 3, to_atom: 1, to_existing_atom: 1,
 to_float: 1, to_integer: 1, to_integer: 2, to_string: 1,
 to_tuple: 1, update_at: 3, wrap: 1, zip: 1]

source

Getting a date

Elixir 1.3 has introduced calendars and calendar types. Lets check out how to get a Date type!

You can use the new function and pass in year, month and day separately.

iex > Date.new(2008, 6, 28)
{:ok, ~D[2008-06-28]}

If you want a nonsensical date you’ll get an :error atom back.

iex > Date.new(2008, 6, 31)
{:error, :invalid_date}

Elixir knows how to handle ISO8601 date formatted strings. Don’t forget to zero pad that month value!

iex > Date.from_iso8601("2008-6-28")
{:error, :invalid_format}
iex > Date.from_iso8601("2008-06-28")
{:ok, ~D[2008-06-28]}

To support Erlang the from_erl function will accept a tuple.

iex > Date.from_erl({2008, 6, 28})
{:ok, ~D[2008-06-28]}

It’s interesting that the date is inspected as a sigil.

iex > {:ok, d} = Date.from_erl({2008, 6, 28})
{:ok, ~D[2008-06-28]}
iex > d
~D[2008-06-28]

Hey you can create a date with the new sigil syntax!

iex > ~D[2008-06-28]
~D[2008-06-28]

Matching *within* function parameter declaration

The below contains a “matching” expression within the function declaration:

iex > defmodule Hulk do
... > def print({1, a} = tt) do
... > IO.inspect(a)
... > IO.puts("**********")
... > IO.inspect(tt)
... > end
... > end

This allows the function access to the variable that was bound while matching and the full value passed in to the function. It returns:

iex > Hulk.print({1, 3})
3
**********
{1, 3}

This is particularly useful for Maps which is the only datatype to support partial matching (eg %{a: a} = %{a: 1, b: 2}) so that the unknown and unmatched portion of the map will be available.

This is a common idiom in pheonix while declaring actions in controllers:

def show(conn, %{"messenger" => messenger} = params) do

The above is from the pheonix docs

Using __using__ for use with the `use` macro

You might run across the use macro while doing something like.

use GenServer

This sets up the module to be able to do special things with the special module GenServer.

Behind the scenes GenServer implements

defmacro __using__(_opts) do

to ensure that the module is setup correctly for use with GenServer.

You can experiment with implementing __using__ inside of IEX like so:

iex > defmodule Fruit do
... > defmacro __using__(_opts) do
... > quote do
... > IO.puts "Good to see you've added Fruit to your meal"
... > end
... > end
... > end

iex > defmodule Meal do
... > use Fruit
... > end
"Good to see you've added Fruit to your meal"

Binary pattern matching

You might be familiar with the most popular form of Elixir pattern matching involving tuples:

iex > {:ok, x} = {:ok, 1000}
{:ok, 1000}
iex > x
1000

You can also pattern match binaries:

iex > <<1, y, 2>> = <<1, 255, 2>>
<<1, 255, 2>>
iex > y
255

This gets powerful when you’re able to match binary formats.

iex > <<x :: binary-1, y :: binary-2, z :: binary>> = <<1, 255, 254, 2>>
<<1, 255, 254, 2>>
iex > x
<<1>>
iex > y
<<255, 254>>
iex > z
<<2>>

Here’s an article about using binary pattern matching to parse a png file.

Transform values when using SweetXML xmap

Assume I have the following XML


xml = """
<response>
  <users>
    <user>
      <firstName>Micah</firstName>
      <lastName>Cooper</lastName>
    </user>
    <user>
      <firstName>Joe</firstName>
      <lastName>Hashrocket</lastName>
    </user>
 </users>
</response>
"""

I can write a small module to map this to something really cool.

defmodule XmlMapper do
  import SweetXml
  @schema [
      names: [ ~x[//response/users/user]l,
        name: ~x[concat(./firstName, " ", ./lastName)]s |> transform_by(&String.upcase/1)
      ]
    ]

  def map(xml_string) do
    SweetXml.xmap(xml_string, @schema)
  end
end

.map will find a list of user elements, concatenate the firstName and lastName, then upcase the whole thing and return it in a map.

iex> XmlMapper.map(xml)
%{names: [%{name: "MICAH COOPER"}, %{name: "JOE HASHROCKET"}]}

That’s doing a lot with a little.

Determine type? no. Get info tho with `i`

There is no direct way to get a variable type in Elixir. For instance type("") is not something that exists although there are 15 Kernel methods for specific type determination such as:

iex > is_number(1)
true
iex > is_binary("") # Note that elixir checks for binary instead of string, a string is a binary.
true
iex > is_atom(:hey)
true

There is an i function available only in iex that will print out useful information about a variable.

iex > i("")
Term
  ""
Data type
  BitString
Byte size
  0
Description
  This is a string: a UTF-8 encoded binary. It's printed surrounded by
  "double quotes" because all UTF-8 encoded codepoints in it are printable.
Raw representation
  <<>>
Reference modules
  String, :binary

It’s implemented as a protocol check it out.

I discovered it here.

Weird Operator Uses in Elixir

= is match, not assignment (sometimes they’re equivalent)

<> is string concatenation

[a | b] is a pipe used within squares and appends the left hand side to the right hand side when the right hand side is a list.

++ is an append operator for lists

[1, 2, 3] ++ [4] == [1, 2, 3, 4]

-- is a subtraction operator for lists.

[1, 2, 3] -- [1, 2] == [3]

in checks to see if an element is included in a list.

1 in [1] == true

=== works with numbers and returns false when comparing numbers of different types that would otherwise be equivalent.

(1 === 1.0) == false; (1 == 1.0) == true

^ is the pin operator, it switches a variable from assignable to matchable.

iex > a = 1; {^a, b} = {1, 2};
{1, 2}
iex > a = 1; {^a, b} = {2, 2};
** (MatchError) no match of right hand side value: {2, 2}
iex > a = 1; {a, b} = {2, 2};
{2, 2}

Accessing a single element of a list by index

Getting a element form a list by index is a common language idiom.

In ruby you would use the squares [] method (similar to Java, C#, etc.).

> [:a, :b, :c, :d][2]
:c

But Elixir is not object oriented and instead provides the Enum module that has many functions that can operate on a list. Three functions return an element by index.

iex > Enum.at([:a, :b, :c, :d], 2)
:c
iex > Enum.fetch([:a, :b, :c, :d], 2)
{ :ok, :c }
iex > Enum.fetch!([:a, :b, :c, :d], 2)
{ :ok, :c }

They behave differently when the index doesn’t correspond to the list.

iex > Enum.at([:a, :b, :c, :d], 9999)
nil
iex > Enum.fetch([:a, :b, :c, :d], 9999)
:error
iex > Enum.fetch!([:a, :b, :c, :d], 9999)
** (Enum.OutOfBoundsError) out of bounds error
    (elixir) lib/enum.ex:722: Enum.fetch!/2

I don’t understand how the statuses (:ok, :error) might be used in application code yet by I’m curious and can’t wait to find out!

Elixir with macro `<-` and `=`

So Elixir with macro accepts Matching clauses <- and Bare expressions =. Both match patterns and do not leak variables assigned inside these structures.

with {:ok, width} <- Map.fetch(%{width: 10}, :width),
  do: {:ok, 2 * width}
#=> {:ok, 20}

with {:ok, width} = Map.fetch(%{width: 10}, :width),
  do: {:ok, 2 * width}
#=> {:ok, 20}

So what is the difference between these?

When a match fails, matching clauses <- returns failed result but bare expressions = raises a MatchError:

with {:ok, width} <- Map.fetch(%{height: 10}, :width),
  do: {:ok, 2 * width}
#=> :error

with {:ok, width} = Map.fetch(%{height: 10}, :width),
  do: {:ok, 2 * width}
#=> ** (MatchError) no match of right hand side value: :error

Another difference is that when guard is only available for matching clause.

with {:ok, width} when is_number(width) <- Map.fetch(%{width: 10}, :width),
  do: {:ok, 2 * width}
#=> {:ok, 20}