Aetherus

Aetherus

Just out of curiosity. Why is Kernel.then/2 a macro instead of a normal function?

Here’s the source code of Kernel.then/2:

defmacro then(value, fun) do
  quote do
    unquote(fun).(unquote(value))
  end
end

Here’s what I thought what the source code of Kernel.then/2 could be:

def then(value, fun) do
  fun.(value)
end

Showing Posts 1 to 4

adamu

adamu

Check out the pull request that did it. It includes reasons and a link to a mailing list discussion, too.

https://github.com/elixir-lang/elixir/pull/10658

Aetherus

Aetherus OP

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.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

To be a comparable case you need to use theni from another module. As I recall inlining can work differently depending on whether you are inlining a function within the same module or from another module.

Aetherus

Aetherus OP

You are absolutely correct. Thank you.

When I put the callers and the callees into different modules, theni/2 generates the same instructions as thenf/2.

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
mnkhod
So i have been using ash framework for a while and i love it. However currently the issue im having with ash framework is the error handl...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews