Generate `with` statement with macros

Hi,

I have a function with with statement:

def validate_all do
  with :ok <- validate(:a),
       :ok <- validate(:b),
       :ok <- validate(:c),
       :ok <- validate(:d),
       :ok <- validate(:e)
  do
    IO.inspect "ok"
  else
    err -> IO.inspect(err)
  end
end

That works just fine, but I want to generate with clauses at compile time depending on config. Right now I’m stuck with something like this:

Not working example:

@validations [:a, :b, :c, :d, :e]

withstatemets = @validations |> Enum.map( fn validation ->
  quote do  
    :ok <- validate(unquote(validation))
  end
end

def validate_all do
  with unquote(withstatemets) do
  do
    IO.inspect "ok"
  else
    err -> IO.inspect(err)
  end
end

It this possible?

Thanks for you help!

As with is (partially) just wrapper over case you can write it as:

defmacrop do_validate_all([]), do: quote do: IO.inspect("ok")
defmacrop do_validate_all([validation | rest]) do
  quote do
    case validate(unquote(validation)) do
      :ok -> validate_all(unquote(rest))
      err -> IO.inspect(err)
    end
  end
end

def validate_all do
  do_validate_all(@validations)
end

Maybe a simple Enum.reduce_while would give the same result without the metaprogramming?

Unless learning metaprogramming is the goal of course :slight_smile:

6 Likes

Great, this is what I was looking for. I’m trying to avoid macros and this is the solution.

Thanks!

2 Likes