Thank you for the information.
I played with the idea a little.
defmodule Foo do
defp thenf(value, fun) do
fun.(value)
end
@compile {:inline, theni: 2}
defp theni(value, fun) do
fun.(value)
end
defmacrop thenm(value, fun) do
quote do
unquote(fun).(unquote(value))
end
end
def ok1(val) do
val
|> thenf(&{:ok, &1})
end
def ok2(val) do
val
|> theni(&{:ok, &1})
end
def ok3(val) do
val
|> thenm(&{:ok, &1})
end
end
Then I tried decompile the erlang byte code for ok1/1
which calls thenf/2
(normal function)
Foo
|> :code.get_object_code()
|> elem(1)
|> :beam_disasm.file()
|> elem(5)
|> Enum.find(fn tuple ->
list = Tuple.to_list(tuple)
match?([:function, :ok1 | _], list)
end)
and it provides
{:function, :ok1, 1, 11,
[
{:line, 1},
{:label, 10},
{:func_info, {:atom, Foo}, {:atom, :ok1}, 1},
{:label, 11},
{:test_heap, {:alloc, [words: 0, floats: 0, funs: 1]}, 1},
{:make_fun3, {Foo, :"-ok1/1-fun-0-", 1}, 0, 109652265, {:x, 1}, {:list, []}},
{:call_only, 2, {Foo, :thenf, 2}}
]}
You can clearly see it allocates an anonymous function.
I did the same thing for ok2/1
(calls theni/2
which is a inline function) and ok3/1
(calls thenm/2
which is a macro), and they provide
{:function, :ok2, 1, 13,
[
{:line, 2},
{:label, 12},
{:func_info, {:atom, Foo}, {:atom, :ok2}, 1},
{:label, 13},
{:test_heap, 3, 1},
{:put_tuple2, {:x, 0}, {:list, [atom: :ok, x: 0]}},
:return
]}
and
{:function, :ok3, 1, 15,
[
{:line, 3},
{:label, 14},
{:func_info, {:atom, Foo}, {:atom, :ok3}, 1},
{:label, 15},
{:test_heap, 3, 1},
{:put_tuple2, {:x, 0}, {:list, [atom: :ok, x: 0]}},
:return
]}
Looks like calling the inline function theni/2
and the macro thenm/2
generates the same VM instructions.