ademenev

ademenev

How to find out the arity of function passed to a macro?

I am building a macro that accepts a function. Depending on the arity of the function, I need the macro to generate different code. So far I have come up with this:

  defmacro mymacro(fun) do
    arity =
      case fun do
        {:&, _, [{:/, _, [_, arity]}]} ->
          arity

        {:fn, _, [{:->, _, [args, _]}]} ->
          length(args)
      end

    case arity do
      1 ->
        quote do
          fun.(:arg1)
        end

      2 ->
        quote do
          fun.(:arg1, :arg2)
        end
    end
  end

It works like this:

mymacro(fn a, b -> {a,b} end)
mymacro(fn a -> {a} end)
mymacro(&Atom.to_string/1)

But it has 3 problems:

  1. it does not work with anonymous functions defined using the & shorthand. I do not see a reliable way to find the arity of such functions
  2. It would not work if the argument passed to the macro is another macro because that would be just arbitrary AST
  3. It would not work if the argument passed to the macro has to be evaluated at runtime because that would be just arbitrary AST

I can live with the limitation #3.

For the other 2, I could use Code.eval_quoted to obtain the function and determine the arity, but AFAIK that’s considered bad practice and should be avoided

Another option would be to determine the arity at runtime, like this

  defmacro mymacro2(fun) do
    quote do
      f = unquote(fun)
      {:arity, arity} = Function.info(f, :arity)

      case arity do
        1 ->
          f.(:arg1)

        2 ->
          f.(:arg1, :arg2)
      end
    end
  end

but in this case dialyzer gives warnings about unmatched cases

Of course these macros are just examples to demonstrate the problem, and my actual macros are different. And in my particular case, I can think of some workarounds. So it just became kind of academic question for me, is there a generic good way to find out the arity of function passed as macro parameter at compile time?

Any hints?

Most Liked

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Hey @ademenev is there a reason you actually need a macro? You can

case fun do
  fun when is_function(fun, 2) ->
    fun.(foo)
  fun when is_function(fun, 1) ->
    fun.(foo, bar)
end

I don’t see anything here that really requires a macro yet.

Where Next?

Popular in Questions Top

Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
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
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
Werner
Hi, I’m using Ubuntu 18.04 and after updating to OTP-24.0 yesterday i have this warning when I run “mix local.hex”: 14:57:30.512 [warn] ...
New
Kagamiiiii
Student & New to elixir. Nice language. I want to convert a english character, e.g. “a”, which is stored in a variable, to it’s asci...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
sabri
Can someone explain the settings of pool_size of Ecto in config file? and what is the recommend size? Thanks
New

Other popular topics Top

belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
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
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
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
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
sergio
I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into t...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
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

We're in Beta

About us Mission Statement