Doctests: FTW!

I’ve been working on new releases of a couple of my published Hex packages recently, as well as some new packages that should be soon published fairly soon.

All of them have doctests, sometimes a lot of them.

I think doctests are great; I’m a fan. “But there’s a problem”

Read more


TL;DR: Code is Data!

Backstory

Originally this post was part of another post. But the whole post was getting too big, so I’ve pulled it out here.

Although really just part one, its really stand-alone and may be of more general interest.

The repo has the examples.

Metaprogramming with Macros

I guess anybody who knows anything, has read anything or used Elixir at all will be aware the core features of the language are implemented as a collection of macros – the special forms.

Elixir’s macro support is incredibly useful and I’d recommend anybody interested in learning some of the nitty-gritty to read Chris’s excellent book. Alternatively, there are also many excellent blog articles out there (like Sasa’s), and I’ve even had a go myself.

At the heart of macros are quoted forms, Elixir’s name for its abstract syntax tree (AST) representation.

When you run a macro, Elixir passes the arguments as quoted form(s) and expects quoted form(s) as the result (which will then be compiled). Inside the macro though, its just regular Elixir code, albeit manipulating ASTs.

I tend to think of the relationship (contract) between the Elixir compiler and a macro as simply this:

I’ll maybe pass you some asts, do what you want with them but give me back some asts and I’ll compile them for you.

But macros are not the only or best answer to some metaprogramming needs.

Read more


TL;DR: Sharing and Reusing Functions in Elixir is easy. Unless its a Callback.

There’s a problem?

Using a public Elixir function defined in one module (e.g. DonorA) in another (e.g. Recipient1) is easy: just call it with the fully qualified name:

1
2
3
4
5
6
7
8
9
10
defmodule DonorA do
  def donor_a_fun_j do
    :donor_a_fun_j
  end
end
defmodule Recipient1 do
  def recipient_a_fun_p do
    DonorA.donor_a_fun_j
  end
end

Proof of the pudding:

1
2
3
test "test_donor_a_recipient_1_donor_a_fun_j" do
  assert :donor_a_fun_j = Recipient1.recipient_a_fun_p
end

Fine. Good. Job Done. End of Post. Thanks for reading.

No wait …

Read more


When I first started learning Elixir, I didn’t begin to appreciate the power of pattern matching until I’d read Sasa’s book and Dave’s book. Chapter 5 in Dave’s book had some really good prose explaining pattern matching the arguments of anonymous and (Chapter 6) named functions.

But it took me a while to assimilate what the Elixir (and Erlang) books were telling me, so I started writing my Elixir functions rather ignorant of how I could better structure them.

But as ignorance was displaced by experience with Elixir, I started to use pattern matching for functions all over the place, once I’d “seen” that many of my functions were variations (variants) on the same theme, really families of functions with the same purpose.

After a while of using bread’n’butter function matching, I found I’d developed a style of using pattern matching to create a DSL with its own set of verbs.

Then I’d write dsl programs using the verbs. Coffee anyone?

Read more


TL;DR: A macro to generate a Map API for a GenServer’s state or a Module with Agent-held state

The Trouble with State

Modules often need to keep state and share it but, as getting started says

Elixir is an immutable language where nothing is shared by default.

In Elixir there are two basic ways of sharing state: processes and ETS (which I wont consider further).

Arguably the most common way of implementing process-held state is GenServer. But for a module that does little more than hold state, GenServer can be overkill. As an alternative, an Agent provides a simpler, alternative way for a module to hold and manage its state.

But neither GenServer nor Agent provide any built-in API calls to manage the content of the state, not surprisingly as the state’s value can be anything.

More often than not though, I want the state to be a map, and sometimes with keys that are (sub)maps. So I usually want to make Map-like API calls — get, put, take, update etc — both for the state itself and any submaps.

Previously, I’ve written the wrappers manually. This is not hard but is just a bit tedious to do given the possible combinations of state wrappers, submap wrappers, arities, etc (lots and lots of cut’n’paste’edit!). So recently I’ve bitten the bullet and written a first cut of a macro to do the heavy lifting.

The interface I wanted for my applications’ modules was to simply use the module (that I’ve called Amlapio) that makes the API wrapper functions.

As many will already know use calls the __using__ macro in the module being used, and the code generated by __using__ is injected into the caller module.

Here’s an example (from the repo’s tests) of a module using an Agent to hold its state.

The wanted submap wrappers are defined by the agent: [:buttons, :menus, :checkboxes] option.

To generate state wrappers the submaps have been set to nil: agent: nil

Generating wrappers for only a subset of Map functions can be given using the funs options e.g, for the state wrappers, funs: [:get, :put, :pop]

Finally, a namer function has been given to name the state wrappers. It is passed two arguments: the map name (e.g. buttons for a submap, but nil for a state wrapper) and the Map function name (e.g. pop).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
defmodule ExampleAgent1 do

  # generate wrappers for three submaps
  use Amlapio, agent: [:buttons, :menus, :checkboxes]

  # generate *only* get, put and pop wrappers for the state itself and
  # use a namer function to name the wrappers "agent_state_get",
  # "agent_state_put" and "agent_state_pop"
  use Amlapio, agent: nil, funs: [:get, :put, :pop],
    namer: fn _map_name, fun_name ->
    ["agent_state_", to_string(fun_name)] |> Enum.join |> String.to_atom
  end

  # create the agent; note the default state is an empty map
  def start_link(state \\ %{}) do
    Agent.start_link(fn -> state end)
  end

end

These tests from the repo show how the submap wrappers would be used, pretty much how you’d expect. (The repo has tests for the state wrappers as well.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
test "agent_submap1" do

  buttons_state = %{1 => :button_back, 2 => :button_next, 3 => :button_exit}
  menus_state = %{menu_a: 1, menu_b: :two, menu_c: "tre"}
  checkboxes_state = %{checkbox_yesno: [:yes, :no], checkbox_bool: [true, false]}
  agent_state = %{buttons: buttons_state, 
                  menus: menus_state, checkboxes: checkboxes_state}

  # create the agent
  {:ok, agent} = ExampleAgent1.start_link(agent_state)

  # some usage examples

  assert :button_back == agent |> ExampleAgent1.buttons_get(1)
  assert :button_default == 
    agent |> ExampleAgent1.buttons_get(99, :button_default)

  assert agent == agent |> ExampleAgent1.menus_put(:menu_d, 42)
  assert menus_state |> Map.put(:menu_d, 42) == agent |> ExampleAgent1.agent_state_get(:menus)

  assert {[:yes, :no], agent} == 
    agent |> ExampleAgent1.checkboxes_pop(:checkbox_yesno)

end

Creating wrappers for a GenServer’s state is very similar. However, each wrapper has two “parts”: an api function and a handle_call function.

The api wrapper for e.g. `buttons_get/3` looks like this:

1
2
3
4
# api wrapper for buttons_get
def buttons_get(pid, button_name, button_default \\ nil) do
 GenServer.call(pid, {:buttons_get, button_name, button_default})
end

… while the matching handle_call looks like:

1
2
3
4
def handle_call({:buttons_get, button_name, button_default}, _fromref, state) do
  value = state |> Map.get(:buttons, %{}) |> Map.get(button_name, button_default)
  {:reply, value, state}
end

Here’s example of generating wrappers for a GenServer.

Remember: all handle_call functions must be kept together in the source else the compiler will complain.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
defmodule ExampleGenServer1 do

  # its a genserver
  use GenServer

  # generate API wrappers for three submaps
  use Amlapio, genserver_api: [:buttons, :menus, :checkboxes]

  # generate *only* get, put, pop and take wrappers for the state itself and
  # use a namer function to name the wrappers "state_get",
  # "state_put", "state_pop", and "state_take"
  use Amlapio, genserver_api: nil, funs: [:get, :put, :pop, :take],
    namer: fn _map_name, fun_name ->
    ["state_", to_string(fun_name)] |> Enum.join |> String.to_atom
  end

  # create the genserver; note the default state is an empty map
  def start_link(state \\ %{}) do
    GenServer.start_link(__MODULE__, state)
  end

  # << more functions>>

  # handle_calls start here

  # generate the handle_call functions for three submaps' wrappers
  use Amlapio, genserver_handle_call: [:buttons, :menus, :checkboxes]

  # generate the handle_call functions for the state wrappers.
  use Amlapio, genserver_handle_call: nil, funs: [:get, :put, :pop, :take],
    namer: fn _map_name, fun_name ->
    ["state_", to_string(fun_name)] |> Enum.join |> String.to_atom
  end

end

There are tests got the GenServer example in the repo but they look almost identical to the Agent example.

If you just want to give it a whirl, that pretty much it. Its available on Hex and just needs to be added to the dependencies in mix.exs in the usual way.

1
{:amlapio, "~> 0.1.0"}

But if you are interested in a high-level explanation of how I implemented Amlapio, read on.

Read more