Compile time code vs run time

Hey,
I’m trying to understand how distinguish between code that run on compile time and how do you follow that code execution.
For example, how can I know which part of Phoenix view is executing at compile time.

The rule of thumb is, that code inside of a def/defp is run during runtime, while code outside of it, is run during compile time.

An exception to this rule are macros, that is one of the reasons why they are annotated accordingly in the documentation. Regardless where they are, they are expanded during compile time, and therefore anything outside of a quote in the macro is therefore also run during compile time.

Templates in phoenix gets compiled into elixir functions during compiletime, though nothing in the template should be evaluated during compiletime, except for macros, as already mentioned.

There is also this blogpost: https://medium.com/@fxn/how-does-elixir-compile-execute-code-c1b36c9ec8cf

Can’t tell much about its quality, didn’t read it, but it seems to be from the same author who explained the parallel compiler very well in another blog post.

4 Likes

Thank you!
That make sense.

While @NobbZ answer is more or less correct there are few exceptions:

  • Macros are functions that returns AST (do not need to use quote) and can execute any code. In other words these are definition of “here be dragons”
  • Not all code in def/defp blocks will be executed in runtime

For example with default configuration c:init/1 in Plug.Builder will be executed in compile time, so this will print Hello there! in the compile time:

defmodule FooPlug do
  def init(opts) do
    IO.puts("Hello there!")

    opts
  end

  def call(conn, _opts) do
    IO.puts("General Kenobi")
    conn
  end
end

defmodule Pipeline do
  use Plug.Builder

  plug FooPlug
end

As you can always call “regular” functions in macros as well:

defmodule Foo do
  defmacro print_in_compiletime(msg) do
    IO.puts(msg)
    nil
  end
end

defmodule Bar do
  import Foo

  def foo do
    print_in_compiletime "Hello there!"
  end
end

Additionally you can force execution of some code during compile time by using unquote within function body:

defmodule Foo do
  def foo do
    IO.puts(unquote(IO.inspect("What the hell")))
  end
end
4 Likes

Things aren’t that simple… so I might have a library that I use and it will call my callbacks/functions at compile time and I have no idea. Except if I look at the source code or documentation.
Can I cause a bug if I don’t aware that a function is executing at compile time?