Hi, I’m relatively new to Elixir, so I apologize if my question falls under the category of “you should not want this behavior”, but I’m currently stuck on a problem that I don’t have the specific vocab to easily google.
Basically, I’m trying to import another module such that the the implementations in the imported module have lower priority than those in the module that contains the import.as a bare example:
defmodule GenericImpl do
fn test(_) do
fn call_test(x) do
defmodule SpecificImpl do
fn test(3) do
I’m trying to find a way for SpecificImpl.call_test(3) to return :B, but for all other inputs to return ;A.
So far, I’ve tried import (which doesn’t make SpecificImpl.call_test exist at all), using “use” to insert the source of GenericImpl directly into SpecificImpl (which makes it always return :A), using defoverrideable (which makes anything SpecificImpl.call_test(3) an error), and using “use” at the bottom of SpecificImp to insert the source from GenericImpl below SpecificImpl. This last one works, but it feels dirty, and I’d like to keep cross-module dependencies at the top of the module if possible.
Is there a proper way to do this?
I believe the answer might be indeed “you probably should not want this”.
Instead, we usually go for one of these two more explicit approaches:
- We fall back to code in another module by referencing it directly:
defmodule SimpleLogger do
def log(term) do
defmodule IntLogger do
def log(integer) when is_integer(integer) do
IO.puts("We have an integer:")
def log(other) do
- If the code you want to fall back to is injected rather than in a separate module, we fall back using
defmodule ExampleServer do
# ... other callback implementations here
def terminate(reason, state) when is_integer(state) and state > 42 do
raise "You should have terminated me sooner!"
def terminate(reason, state) do
Both of these make it (reasonably) clear to the reader of the code what is going on, There is no ‘hidden’ logic.
That said, it is possible to directly inject code to the bottom of a module by using a
before_compile-callback (See the module-documentation of the
Module module no pun intended, I swear!), regardless of where
use is used.
Can you give us a more concrete explanation of what you are trying to accomplish with this pattern?
Your code looks a lot like OOP and inheritance. This is not how things are usually done in elixir. Based on your description I’d go for a behaviour based solution like this:
defmodule ImplBehaviour do
@callback test(term) :: atom
def call_test(impl, term) do
defmodule Generic do
fn test(_) do
defmodule Specific do
fn test(3) do
fn test(term) do
There’s no need for any hierarchy, meta programming or code sharing.
Specific can call
Generic.test/1 and it’s clear to anybody reading the code how/that this is happening.
@Qqwy, the concrete problem I was trying to solve is that I’ve got a handful of GenServer squid that are handling RPC calls via external message brokers. The nitty gritty of the networking is identical in all of them, and they share a number of handle_call()s, but there are a handful of unique ones per application. The goal was to have the client API and those specific handles be implemented in their own modules, and then add those handles on top of the shared ones implemented in the parent module with all the networking fuss.
In that case you could define a custom behaviour with for example a (bad named)
@callback handle_special_call(message, from, state)
Then include the
handle_call implementation through a macro or
use MyBehaviour (which is a macro too).
And in the quoted code returned by your macro you would have something like this:
... previous shared handle_info clauses
def handle_info(msg, from, state) do
handle_special_call(msg, from, state)
def handle_special_call(_msg, _from, _state) do
raise "Hey, you forgot to implement handle_special_call/3"
defoverridable handle_special_call: 3
Or, as others said, you just implement
handle_info/3 normally, and call the parent implementation in your last clause. It’s simpler, it works well, and does not need macros.
This sounds like you’d want a higher level behaviour than
GenServer. Implement that for all your calls and create one implementation of
GenServer which maps genserver callbacks to those higher level callbacks of your RPC calls. Your usecase might look like “composition of handle_call’s”, but it actually isn’t, you’re just operating on a to low level primitive.
You could get this effect with
defoverridable and a little boilerplate:
defmodule Foo do
defmacro __using__(_opts) do
def bar(arg) do
IO.inspect(arg, label: "default bar")
defoverridable [bar: 1]
defmodule UsedFoo do
def bar("wat") do
def bar(x), do: super(x)