Blocks -- Is there any way to define a function that does take a do .. end block?

In Ruby, which is very similar to Elixir I do this:

def test
 yield
end

test do
 puts("sup there")
end

Here, the yield keyword will be replaced by the statements (in this case, puts("sup there"), but inside it I can put pretty much whatever I want) inside the block.

How to do this in Elixir?

1 Like

Welcome to the forum!

It’s not possible to define a function that does this. You can define a macro that does this, but the behaviour is extremely different to the behaviour in Ruby.

To achieve what you’ve described you would use an anonymous function:

test fn ->
  puts("sup there")
end

This is conceptually the same as a block/proc in Ruby.

2 Likes

thanks man!

1 Like

Perhaps this also approximates what you are looking for?

def test(do: block)
 block
end

test do
 puts("sup there")
end
2 Likes

No, that won’t work. It might work when you make test/1 a macro rather than a function and put some quote/unquote in.

@NobbZ I’ve been using that in my codebase and it seems to work fine. I use it in functions that create an easy DSL for html emails.

Inside a module my example above can be run in iex. Is it perhaps working, but something I should avoid?

1 Like

It works, but the evaluation order won’t entirely match the “Ruby blocks” intuition, especially when things like else get involved:

defmodule DoTest do
  def foo(do: x, else: y) do
    IO.inspect({x, y}, label: "in foo")
  end
end

DoTest.foo do
  IO.puts("in do")
else
  IO.puts("in else")
end

## prints:
# in do
# in else
# in foo: {:ok, :ok}

The parser makes the transformation from do ... end to do: ... consistently, but plain def always evaluates arguments (versus defmacro which passes arguments as AST)

9 Likes