Search code examples
elixirsyntactic-sugarkeyword-argument

Keyword arguments with do-block


I have a function that looks something like this.

def test(options \\ []) do
  # Fun stuff happens here :)
end

It accepts several (optional) keyword arguments, including do:. I'd like to be able to call it like this.

test foo: 1 do
  "Hello"
end

However, this gives an error.

** (UndefinedFunctionError) function Example.test/2 is undefined or private. Did you mean one of:

      * test/0
      * test/1

    Example.test([foo: 1], [do: "Hello"])
    (elixir) lib/code.ex:376: Code.require_file/2

As you can see from the error, the syntax above is desugaring to two separate keyword lists. Now, I can call this function using the following slightly inconvenient syntax

Example.test foo: 1, do: (
  "Hello"
)

but is there any way to provide a do-block in addition to other keyword arguments in one function call?


Solution

  • If you are open to using a macro instead of a function this might help you:

    defmodule Example do
      defmacro test(args, do: block) do
        quote do
          IO.inspect(unquote(args))
          unquote(block)
        end
      end
    end
    

    Sample usage:

    iex(2)> defmodule Caller do
    ...(2)>   require Example
    ...(2)> 
    ...(2)>   def foo do
    ...(2)>     Example.test foo: 1 do
    ...(2)>       IO.puts "Inside block"
    ...(2)>     end
    ...(2)>   end
    ...(2)> end
    {:module, Caller,
     <<70, 79, 82, 49, 0, 0, 4, 108, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 147,
       0, 0, 0, 16, 13, 69, 108, 105, 120, 105, 114, 46, 67, 97, 108, 108, 101, 114,
       8, 95, 95, 105, 110, 102, 111, 95, 95, ...>>, {:foo, 0}}
    iex(3)> Caller.foo
    [foo: 1]
    Inside block
    :ok