Shortcut to match patterns on the same key value in the Elixir map
I do quite a few templates in this style:
def action(%{start_date: start_date, amount: amount, notify: notify %}) do
# some action
end
In most cases, the name I choose for the parameters has the same name on the map. Is there a shortcut for specifying this case of pattern matching without repeating the same name for key and value?
Something along the line of this pseudocode:
def action(%{start_date: %s, amount: %s, notify: %s}) do
IO.inspect(start_date)
# some action
end
source to share
AFAIK, there is nothing out of the box, but you can just create a macro for yourself to serve this purpose:
defmodule M do
defmacro struct(params) do
{:%{}, [], Enum.map(params, fn e -> {e, {e, [], Elixir}} end)}
end
end
defmodule Test do
require M # to use macro
def action(M.struct([:a, :b]) = params),
do: IO.inspect params, label: "Params are"
end
Test.action(%{a: 42, b: :ok})
#โ Params are: %{a: 42, b: :ok}
Test.action(%{a: 42})
** (FunctionClauseError) no function clause matching in Test.action/1
The above code is of course only MCVE, you probably need to leverage it somehow to handle corner cases (and probably have a clearer readable macro that behaves smarter than just spitting out the AST and considers the binding etc.), but I believe that explains the idea.
source to share
I presented a sigil ~m{...}
to achieve a destructive purpose.
~m{foo bar} = %{foo: 1, bar: 2}
foo #=> 1
bar #=> 2
This is how I implement the sigil
defmodule DestructingAssignment do
defmacro __using__(_) do
quote do: import unquote(__MODULE__)
end
defmacro sigil_m({:<<>>, _line, [string]}, []) do
spec = string
|> String.split
|> Stream.map(&String.to_atom/1)
|> Enum.map(&{&1, {&1, [], nil}})
{:%{}, [], spec}
end
end
Using
defmodule Foo do
use DestructingAssignment
def foo(~m{bar}) do
# Do whatever you want with bar
IO.inspect(bar)
end
end
Foo.foo(%{bar: 1, baz: 2})
1
source to share