jmitchell

jmitchell

Writing a library for use in both Elixir and Erlang

What is the best practice for implementing a library for use in both Elixir and Erlang? My goals include:

  1. minimize code duplication
  2. feels idiomatic to both Elixir and Erlang users
  3. no syntactic function call remapping like :lists.sort [3, 2, 1] or 'Elixir.String':downcase(Bin).
  4. integrates seamlessly with common package management and build solutions
  5. doesn’t break any features like maybe hot code reloading (or anything else, really)
  6. written primarily in Elixir because I like defmacro.

Without knowing better, the first strategy I’d probably try is implementing an Elixir library and making an Erlang wrapper. This seems good, except I’d have to manually keep the wrapper synced with the Elixir code. Are there recommended wrapper generators for this?

I’m not familiar enough with Elixir/Erlang/BEAM to know whether that approach might break any features. If so, would writing the library primarily in Erlang be any better?

I don’t have a specific project in mind yet. Just mulling over how I would approach this when the time comes. Thanks, everyone.

Marked As Solved

PragTob

PragTob

So, @josevalim came around and suggested another way to do this which I like better as it is simpler:

elixirdoc = """
...
"""

erlangdoc = """
...
"""

for {module, moduledoc} <- [{Benchee, elixir_doc}, {:benchee, erlang_doc}] do
  defmodule module do
    @moduledoc moduledoc
    # all defs here without using
  end
end

It is implemented in this benchee PR.

Also Liked

michalmuskala

michalmuskala

What I saw frequently in Erlang codebases using Elixir is the use of macros to alias elixir modules into something more consumable. You could provide an erlang header file that would provide some of those macros, eg:

-define(string, 'Elixir.String')
# later in code
?string:downcase(Bin).

On the other hand, I don’t see anything wrong with calling erlang-style modules from Elixir. You could use those:

defmodule :foo do
  # ...
end
# use in elixir
:foo.bar(1, 2, 3)
# use in erlang
foo:bar(1, 2, 3).

In general I would say that a wrapper is the worst approach - it doesn’t add any significant value and has considerable downsides and risks regarding it getting out of sync.

vic

vic

Asdf Core Team

So if @jmitchell would like to be able to call Foo.bar nicely from both Elixir and foo.bar from Erlang, would this be an acceptable approach to expose the api for both ?

defmodule Foo.Impl do
  defmacro __using__(_) do
    quote do
      def bar(), do: # ...
     # more of Foo public API
   end
  end
end

defmodule :foo do
   @moduledoc "For use from erlang"
   use Foo.Impl
end

defmodule Foo do
   @moduledoc "Alchemist way"
   use Foo.Impl
end
PragTob

PragTob

Not sure about the compiled artifact size but as it’s just one top level module that does a bunch of delegates to other things I don’t think the size impact (even if it duplicates all that code) is worth worrying about as I’d say it’s maybe ~1% of the overall code size.

I like the solution because it’s very simple and uses the simplest constructs to make it work - i.e. no macros.

Where Next?

Popular in Questions Top

gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
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
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
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
hpopp
To simplify some tasks at work, I wrote and published this package yesterday. It’s a simple macro that enables Access behaviour on struct...
New
Codball
Mix format works fine if run from the cmd. I’ve followed this to facilitate the implementation into VSC which involves downloading an ext...
New
Mooodi
Given a string, how can I get access to its character by index? Enum.at("my_string", 2) doesn't work. Or rather, not char, but a substr...
New
wernerlaude
In AR this is so simple @articles = current_user.articles How to do in Ecto? def index(conn, _params) do current_user = conn.assig...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New

Other popular topics Top

Qqwy
Update: How to use the Blogs &amp; Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3268 119930 1237
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
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

We're in Beta

About us Mission Statement