I know I'm a little bit Ruby-ish or even Java-ish here but here's the case I encounter very often.
I want to define some general module (metaprogramming can be used) which will use some variables/function that will be specified in "children" modules (which will probably use
this general module).
I imagine it like:
defmodule Parent do
defmacro __using__(_) do
quote do
def function do
__MODULE__.other_function
end
end
end
end
and later on:
defmodule Child do
use Parent
def other_function do
# some real work
end
end
I very often need to "abstract" some function and the best it would be by defining some module-variable @var
accessible in "parent" module but I know that it doesn't work like that whatsoever.
Is there any way to call a function that will be defined in "including" module?
I think you're on the right track. If you add a @callback
to define the spec
for the "abstract" function it would be better. I'll explain with a working example:
defmodule Fibonacci do
@callback fib(number :: integer) :: integer
defmacro __using__(_) do
quote do
@behaviour Fibonacci
def fibonacci(n) when not is_integer(n), do: {:error, "Not integer"}
def fibonacci(n) when n < 0, do: {:error, "Negative number"}
def fibonacci(n), do: {:ok, __MODULE__.fib(n)}
def fibonacci!(n) do
case fibonacci(n) do
{:error, reason} -> raise reason
{:ok, n} -> n
end
end
end
end
end
Every module that uses Fibonacci
will need to implement the callback function fib/1
. Also every module that use
the Fibonacci
module will have the function fibonacci/1
that handles errors and fibonacci!/1
that raises errors.
So, let's implement fib/1
with direct recursion:
defmodule Direct do
use Fibonacci
def fib(0), do: 0
def fib(1), do: 1
def fib(n), do: fib(n - 1) + fib(n - 2)
end
And for tail recursion:
defmodule Tail do
use Fibonacci
def fib(0), do: 0
def fib(1), do: 1
def fib(n), do: fib(1, 1, n - 2)
defp fib(_, value, 0), do: value
defp fib(n_2, n_1, n), do: fib(n_1, n_2 + n_1, n - 1)
end
So in iex
we can call the different implementations:
iex(1)> Direct.fibonacci(10)
{:ok, 55}
iex(2)> Tail.fibonacci!(10)
55
iex(3)> Tail.fibonacci!(-10)
** (RuntimeError) Negative number
Also, if you forget to define the fib/1
function in your module, then the compiler will warn you about it:
defmodule Warning do
use Fibonacci
end
* warning: undefined behaviour function fib/1 (for behaviour Fibonacci)
If you want to play with this code, just follow this link http://elixirplayground.com?gist=606fe8c283443f03c7af08f85c888fe3
I hope this answers your question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With