How to call a function at compile time?

Hi all
I’ve read an article about Plug and somewhere in the article you find the sentence:

One interesting characteristic of module plugs is that init/1 is executed in compile time, while call/2 happens at run time.

My question is, how can I call a function before it will be compiled? I know elixir provide an attribute @before_compile but it has nothing to do with?

And a code, that makes me confuse:

def call(%Plug.Conn{request_path: "/" <> name} = conn, opts) do
  send_resp(conn, 200, "Hello, #{name}")
end

Can someone explain me, how this will be pattern match?

Thanks

2 Likes

There is probably some macro that can call init/1 at compile time. Macros are basically a pieces of code, that execute at compile time, and they can modify/write/remove other code, by manipulating abstract syntax tree of code of the program. Check out this simple snippet for basic macro, note we do not execute the compiled program anywhere:

$ cat macro.ex    
defmodule Foo do
  defmacro say_hi do
    IO.puts "Hi, I printed at compile time!" # this is a function call
  end
end

defmodule Bar do
  require Foo
  Foo.say_hi
end

$ elixirc macro.ex
Hi, I printed at compile time!

So, there seem to be two unrelated questions here. You don’t actually need a macro in order to call something at compile time, you can simply be in something like a module body which is evaluated at compile time. So for example if you do

defmodule Foo do
  IO.puts "yo"
end

"yo" will get printed at compile time, because it calls the IO.puts function at compile time.

How does the question about the Plug.Conn pattern match relate? What do you mean “how this will be pattern match”?

1 Like

Thanks, Ben. I was referring to the use case with Plug, where init/1 gets called by macro, I think here:
https://github.com/elixir-lang/plug/blob/master/lib/plug/builder.ex#L198

But you are right, it does not have to be macro.

Important to note is that while you can call any functions and macro at compile time, these cannot be functions/macros defined in the module you are currently compiling.

First of all, thanks for response.
What do I mean with pattern matching is, I do not understand the comparison of this code

%Plug.Conn{request_path: "/" <> name} = conn, opts

What does it mean?

Above code snippet is only understandable in the following context:

def call(%Plug.Conn{request_path: "/" <> name} = conn, opts) do
  # ...
end

This is a definition of the call method, with two parameters (call/2). These parameters are named conn and opts in above code.

I myself usually find it nicer to write it like:
def call(conn = %Plug.Conn{request_path: "/" <> name}, opts)
because I find it more idiomatic, but as far as I know there is no difference (inside a function’s parameter list! Outside, one can only assign something when it is to the left side of the match operator(=)! This is exactly why I find it more idiomatic to swap them around. )

Lets break it down:

This definition of call takes two parameters:

1: conn = %Plug.Conn{request_path: "/" <> name}. This ensures that:
a) we set the result to a variable conn.
b) This function clause will not match when we pass anything that is not a %Plug.Conn{} as the first parameter.
c) Furthermore, this %Plug.Conn{} is required to have a request_path: field that starts with a “/”.
d) Also, anything that follows the / in the request_path: field is stored in the variable name.

2: opts. No magic happening here.

I hope that provides some clarity to you :slight_smile:!

1 Like

@Qqwy it is almost clear but can you please write an example module, that shows the scenario?
Thanks