Erlang Elixir interoperability discovery - is this ok?

This might be known widely already, but its new to me and I couldn’t find anything about this in on the interwebs. I’ve been trying to call Elixir in some Erlang code for quite some time, and its always gone similar to this:

defmodule Test.Tester do
   def hello() do
      IO.puts "hello"
   end
end

erl > 'Test.Tester':hello

BUT I made the discovery of the following interesting behavior today:

defmodule :tester do
   def hello() do
      IO.puts "Hello"
   end
end
:tester.hello

Which compiles and prints “Hello” just fine.

While not idiomatic forsure, does anyone see any issue with using this extensively to add new functionality in Elixir to an existing Erlang code base?

edit: My mistake for the wrong section!

Test.Tester in elixir is actually the atom :"Elixir.Test.Tester", so to call it from erlang use Elixir.Test.Tester:hello.

4 Likes

Just a quick clarification, you will need to use single quotes around the module name. 'Elixir.Test.Tester':hello().

2 Likes

Yes, that was what I was trying to avoid, just because the quotes are cumbersome, and the atom can be aliased to a module name.

I haven’t tested, but you could probably do -define(TEST_TESTER, 'Elixir.Test.Tester')., then your calls would be ?TEST_TESTER:hello(). I’m not really sure if that is any better though.

There is erl_alias and I’m pretty sure there was another package beeing able to do similar things.

I have seen multiple libraries that use the following concept to allow a module to be usable both in Elixir and Erlang in an idiomatic way:

defmodule Test.Tester do
  def hello() do
    IO.puts "Hello!"
  end
end

defmodule :test_tester do
  @elixir_module Test.Tester
  @args fn num ->  Stream.iterate(0, fn x -> x + 1 end) |> Stream.map(&(Macro.var(:"arg#{&1}", nil))) |> Enum.take(num) end
  for {name, arity} <- @elixir_module.__info__(:functions) do 
    defdelegate(unquote(name)(unquote_splicing(@args.(arity))), to: @elixir_module)
  end
end
2 Likes