Problem with function to dynamically define modules

Hi there! I’m trying to create a function that dynamically defines a module based on the passed argument, like so:

defmodule Test do
  def define_module(module_name, value) do
    defmodule module_name do
      def run, do: %{value: value}
    end
  end
end

However, when I run Test.define_module(:Hello, "world"), I get this error:

** (CompileError) iex:9: undefined function value/0 (expected :Hello to define such a function or for it to be imported, but none are available)
    (elixir 1.13.3) src/elixir_locals.erl:115: anonymous fn/4 in :elixir_locals.ensure_no_undefined_local/3
    (elixir 1.13.3) src/elixir_erl_compiler.erl:12: anonymous fn/2 in :elixir_erl_compiler.spawn/1

On the other hand, the following does work:

defmodule Test do
  def define_module(module_name, value) do
    defmodule module_name do
      @value value
      def run, do: %{value: @value}
    end
  end
end

Test.define_module(:Hello, "world")

Is there a way to use the argument as the return of the inner function without making it a module attribute?

Thanks a lot in advance! :slight_smile:

  1. You probably want to use macros rather than functions.
  2. You need to use unquote within a quoted context.
1 Like

def doesn’t capture its environment, so it won’t work directly.

You could do some AST surgery on what def produces:

{:def, [context: Elixir, import: Kernel],
 [
   {:run, [if_undefined: :apply, context: Elixir], Elixir},
   [do: {:%{}, [], [value: PUT_THE_VALUE_HERE]}]
 ]}

but the pattern of “interpolate this compile-time value into the AST” is frequent enough that there’s a built-in mechanism for doing it… module attributes :stuck_out_tongue: