Pass anonymous function to NIF

Is it possible to call a function passed to a NIF? Obviously it’s easy to determine if an argument passed to a NIF is a function using enif_is_fun, but there doesn’t seem to be a straightforward way to actually use the passed function. I’ve tried the following approach to no avail:

static ERL_NIF_TERM pass_elixir_function(ErlNifEnv* env, int argc, ERL_NIF_TERM argv[])
{
  unsigned long ptr;

  enif_get_ulong(env, argv[0], &ptr);

  void (*func)(void) = (void (*)(void))ptr;

  (*func)();

  return enif_make_atom(env, "ok");
}

And trying to call it using:

iex> NifTest.pass_elixir_function(fn -> IO.inspect("hello") end)

This just results in a segmentation fault. Is there a better way to approach this?

1 Like

Elixir functions are compiled to run in the BEAM runtime environment complete with all the other modules they rely on. NIFs are natively compiled. So I’d say the answer is no unless you want to implement a whole lot of the BEAM run-time within your NIF, and share a lot of the current run-time context, neither of which would make sense.

Let us know what you are trying to achieve - there may be an altogether different approach.

2 Likes

I assumed as much but wasn’t sure if there might be an easy way around this.

I’m not entirely certain there’s a different approach. I’m working on adding GPU support to an existing library using CUDA. It’s a genetic algorithm library that requires the user to define fitness functions and chromosome generation functions in an implementation module. If I can’t pass evaluation methods to the NIF, the user would have to define them in CUDA, which kind of defeats the purpose of the library.

I’m pretty certain I could achieve this same effect using a Port, but I don’t know if that’s worth it either.

Could you transform your fitness functions into something the NIF can handle? e.g. by reading and transforming the AST into a structure that you can process? I’d imagine you would have to put some pretty heavy constraints on what the functions can do. Another alternative is build your own DSL to define the fitness functions, but you would need to rework the existing library to process that without the NIF.

Have you taken a look at https://github.com/sasagawa888/deeppipe2 for inspiration? Different type of learning system, but there may be some ideas in there for you.

2 Likes

As switching the context between NIF and BEAM code is expensive, it was a deliberate choice to not allow calling back into BEAM from within a NIF.

5 Likes