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

      

+3


source to share


2 answers


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.

+2


source


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

      

+1


source







All Articles