Reasons why a FunctionNotFound error would occur exclusively in a transaction

I’m dealing with a project, where a function A from module B is called from the two following places:

  1. a “regular” function - which is called from within a Phoenix controller.

  2. an anonymous expression which is passed to the following macro.

defmacro other_function(anonymous_function) do
  quote do
    MyRepo.transaction(unquote(anonymous_function), timeout: 60000)
  end
end

When 2) is called, an UndefinedFunctionError occurs, because of the call to function A.

I understand that unquoted bit of macros aren’t evaluated/checked at compile time, thus why there is n compilation problem. However, since the other non place calling A has no problem, I can’t understand why it only occurs in the Macro, during the transaction. The place where anonymous function is defined has full access to module B, so I’m guessing I’m not understanding how to use macros/transactions in some way.

Any help would be greatly appreciated.

I suspect it has to do with binding and unquote fragments

the :bind_quoted option is recommended every time one desires to inject a value into the quote.

do you really need a macro for what you’re trying to do? why do you need to mess up with the AST?
I’m assuming that the macro you provided is a simplification of what you need, but if it’s just as you showed, you’re good with a plain function doing that.

Currently :bind_quoted isn’t being used. As I understand, that is for extracting the value of variables passed to the macro, for when you do something with them. In this case, the macro seems to only exist to wrap the transaction function with the given timeout. I think I need to sleep on it and have another go at that documentation, because I’ve read it so many times and am still not getting it.

full disclosure, I didn’t write this codebase, and am having trouble getting a response from the original author -so I can’t really speak for the intentions of it. As far as I can tell, it exists to simplify explicitly specifying the transaction timeout, and not a lot else.

def timeout_transaction(function) do
MyRepo.transaction(function, timeout: 60000)
end

should do it

1 Like

you can use the guard is_function/2 to check if the function is a 0-arity function(that is what transaction expects).
https://hexdocs.pm/elixir/Kernel.html#is_function/2

  1. Write a test to make sure the previous variant of the code works.
  2. Change it to pure function format without macros as @cevado shows.
  3. Re-run tests.
  4. ???
  5. Profit.
1 Like

The suggestions are highly appreciated!
Turns out the problem was, the app where the function was in a different one to where the function was defined - both of the apps run and compile under the same umbrella.
I added the function’s app to the dependencies defined in the mix.exs file for the other app, and the problem is solved :slight_smile: