Skip to content

Instantly share code, notes, and snippets.

@Tomboyo
Last active December 7, 2020 07:23
Show Gist options
  • Save Tomboyo/ea855ba46fefd2c693c41c576481b0b8 to your computer and use it in GitHub Desktop.
Save Tomboyo/ea855ba46fefd2c693c41c576481b0b8 to your computer and use it in GitHub Desktop.
Tersely create long pattern-match expressions for Elixir lists

The ListMacro.list macro in list_macro.ex allows us to create match expressions on lists in which we can capture any elements we want and ignore others, without typing out long sequences of underscores.

For example, if we want to match the first and last element of a 12 element list, we can do this:

list([first, 10, last]) = Enum.to_list(1..12)
IO.inspect([first, last])
#=> [1, 12]

instead of this:

[first, _, _, _, _, _, _, _, _, _, _, last] = Enum.to_list(1..12)
IO.inspect([first, last])
#=> [1, 12]

Any combination of variables and numbers is allowed. And yes, it still works with pins, too:

pin_me = 3
list([a, b, ^pin_me, 2]) = [1, 2, 3, 4, 5]
IO.inspect([a, b])
#=> [1, 2]

list([a, b, ^pin_me, 2]) = [1, 2, 7, 4, 5]
** (MatchError) no match of right hand side value: [1, 2, 7, 4, 5]

We could use a variety of regex-like patterns to further refine the generated match expression. Whatever the case, this macro shows how to achieve the basic idea.

Thanks!

defmodule ListMacro do
defmacro list(opts) do
free_variable = quote do ; _ end
quote do
unquote(Enum.flat_map(opts, fn
x when is_integer(x) -> List.duplicate(free_variable, x)
x -> [ x ]
end))
end
end
end
defmodule Example do
require ListMacro
import ListMacro
def try_it_out() do
list([a, 2, b, 2, c]) = [:a, :b, :c, :d, :e, :f, :g]
IO.inspect([a, b, c])
#=> [:a, :d, :g]
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment