hst337

hst337

How to get an AST and Macro.Env of a function by name and arity?

Summary

Is there any way to write a function which can be invoked in compile time and which can return an AST and Macro.Env of given function.

Example

We have a module

defmodule MMM do
  def fff(x), do: x + 1
end

And we can execute some function in compile time which will work like

iex> Macro.get_ast(MMM, :fff, 1)
{
 {:def, [context: Elixir, import: Kernel],
  [
    {:f, [context: Elixir], [{:x, [], Elixir}]},
    [do: {:+, [context: Elixir, import: Kernel], [{:x, [], Elixir}, 1]}]
  ]},
  %Macro.Env{}
}

PS

I know there is no such function, but how can something like this be implemented? What problems should I expect while implementing this function?

Most Liked

ityonemo

ityonemo

this is possible using Elixir compiler tracer hooks. I am pretty sure it broadcasts the AST and __ENV__ of a function being compiled.

https://hexdocs.pm/elixir/Code.html#module-compilation-tracers

Worst case scenario, collect em’ all, serialize them using :erlang.term_to_binary, and then stash everything into some file in /priv

OvermindDL1

OvermindDL1

In the erlang side things like -compile({inline,24}). are specified to inline module-local functions with a given weight or less, 24 is the default weight, so most ‘shortish’ functions will get inlined directly and always. It can be overridden though with a different weight, like 1000 if you want to inline almost everything, or you can force inline specific functions by giving it a list.

Do note though, inlining larger can help with some optimizations, but overall it usually doesn’t and it’s best to leave it at default as the size overhead doesn’t help the speed at that point, and calling module-local functions require no indirected call so their only cost is a stack frame at that point (if that’s even needed).

OvermindDL1

OvermindDL1

Such a function like that doesn’t exist because the AST is long long gone at that point. You could always make your own macro, say defmodulex or so and just use it instead of defmodule and have it build a new hidden function called __ASTS__/1 where you pass it an option of what to return and it can return the AST for whatever you asked for. Basically compile the compile and add in that function that just returns the AST of the module that was passed in?

Better question though, ‘why’ do you want it?

hst337

hst337

I am trying to investigate ability to perform some simple automatic function inlining optimizations in compile time
I thought about defmodulex-style approach, but this looks ugly and can’t be acheived with functions in dependencies

I thought that there might be some way to override :elixir_module module (unload it before compilation and load modified version which stores AST per module), or create some compiler tracer, which will try to store all functions in some ets or process (but I don’t know if tracers are capable of doing this)

OvermindDL1

OvermindDL1

You actually can, but this is definitely more work… ^.^;

First task would be to fork that file, then make the changes you want, then load your ‘loader’ module first and replace the built-in module with that by forcing it to load via a call in the Code module (forget the name but it’s not hard to figure out).

Traces can’t really do that no, well, maybe if you hook the file loading but then you’d have to do a whole lot more work, so not recommended… ^.^;

EDIT1: Are you sure that inlining functions is really that necessary with the new OTP-24 JIT?

EDIT2: Also, what if a module gets hot-replaced, how will you update your inlined versions?

Where Next?

Popular in Questions Top

New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
sabri
Can someone explain the settings of pool_size of Ecto in config file? and what is the recommend size? Thanks
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

Other popular topics Top

JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1140 51847 244
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

We're in Beta

About us Mission Statement