Using before_compile to generate definitions

I would like to use the before_compile module hook to programmatically generate lenses for some modules.

I have something roughly like that:

defmodule Blabla do

 @before_compile {Blabla.Lens, :__before_compile__}

  def module A do
    defstruct [...]
  end

  def module B do
    defstruct [...]
  end 

end

defmodule Blabla.Lens do
   def __before_compile__(env) do
     # (1) retrieve the keys of the module
     # (2) generate lenses definitions for the keys
   end
end

How can I generate a function definition for the module Lens within the __before_compile__ module?

Do I have to use a __using__ macro instead or is it possible?

What do you mean be retrieving keys of the module and how do you want to create the lenses?

Let’s assume for the moment that I can retrieve the keys of the structs defined in A or B from the __before_compile__ function [1].

I would like to define the lenses like this, for example if A has key k1:

@k1 Lens.make_lens(:k1)

Or do you see a better solution? I was thinking about using a function to retrieve the lens but I am realising this would not work (except maybe with a macro).

I could I define an attribute in Lens from the __before_compiling function?

[1] I can because I am defining them with a google protobuf library and I can access the keys with .keys

There are no submodules in elixir, so you can’t even know if A and B exist at all.

So whatever you want to do with them has to happen explicitly.

Ok. Thanks for the clarification.

How would you do define the k1 attribute in the A module, assuming the hook is defined in A, then?

defmodule A do
  @k1 whatever_you_want
end

But maybe I do get you wrong…

Thanks for your patience and sorry that I cannot express my ideas as clearly as I want.

Given this function:

defmodule Blabla.Lens do
   def __before_compile__(env) do
     # how to define an attribute and function in the `Blabla.Lens` module from this function?
   end
end

When __before_compile__ is executed, how can I define an attribute (and a function) dynamically inside the Blabla.Lens module? I would like this definition to happen within the call to the function. The need to do it dynamically comes from the fact that the struct defines in my post are generated also dynamically from another library given a schema declaration (with protobuf, but it does not really matter here).

As far as I remember, the env variable contains the name of the module that is calling into the hook.

But as the module does not exist yet, you can’t do much with it. You can not call __struct__, nor can you ask for its attributes nor anything else.

But in general, the function has to return an AST, so whatever AST you return will be injected into the module in question.

Also, just to make clear, Blabla.Lens.__before_compile__/1 can not change the module Blabla.Lens anymore, as it has already been compiled. If it were not compiled already you weren’t able to call that function.

If you want to generate parts of Blabla.Lens depending on structs defined in other modules, you need to generate the AST by collection from those struct defining modules during compile time of Blabla.Lens.

You can use __struct__/0 to get an empty struct, remove the __struct__ key, iterate over the remaining keys, generate your code. The usual meta-programming stuff.

Thanks for your answer. I just discovered it needs to return an AST only when it is defined as a macro. But is it possible to generate definitions when __before_compile__ is defined as a function?

Where do you want to generate definitions?

As I already stated (and the docs do as well) __before_compile__ can not change the module it is defined in, but only the module it is called from.

Can you change the module it is called from when __before_compile__ is a function or only when it is a macro?

You can inject code when it’s a macro. I’m not sure what is possible when it’s a function. So far I never really needed the hooks…

Ok, many thanks for all the clarifications!