vshesh
How to properly implement dynamic dispatch?
Hi everyone,
I’m struggling to understand behaviours and dynamic dispatch. The example in the getting started with elixir page doesn’t make any sense to me:
defmodule Parser do
@callback parse(String.t) :: {:ok, term} | {:error, String.t}
@callback extensions() :: [String.t]
def parse!(implementation, contents) do
case implementation.parse(contents) do
{:ok, data} -> data
{:error, error} -> raise ArgumentError, "parsing error: #{error}"
end
end
end
Where does the implementation come from?
I tried using this myself when writing a module and I am doing something wrong, just not clear what. I don’t understand how to use the implementation’s functions when writing the handle_call method:
defmodule Program do
use GenServer
@callback inputs() :: [atom]
@callback init(term) :: any
@callback handle_data(any, map, map) :: term
@callback emit(term) :: map
@impl GenServer
def init(arg) do
## How do I use the implementation's inputs function? there's no implementation passed in here
__MODULE__.inputs
|> Enum.each(fn x -> Phoenix.PubSub.subscribe :inputs, x end)
{:ok, {%{}, __MODULE__.init(arg)}}
end
@impl true
def handle_call(%OSC.Message{address: address, arguments: arguments}, _, {inputs, state}) do
data = get_latest_reading(address, arguments)
newinputs = %{inputs | address => data}
# Same question in this area
if map_size(newinputs) === length __MODULE__.inputs do
newstate = __MODULE__.handle_data(state, inputs, newinputs)
__MODULE__.emit(newstate)
|> Enum.map(fn {k, v} -> Phoenix.PubSub.broadcast(:outputs, k, v) end)
{:noreply, {newinputs, newstate}}
else
{:noreply, {newinputs, state}}
end
end
def get_latest_reading(address, arguments) do
receive do
%OSC.Message{address: ^address, arguments: args} ->
get_latest_reading(address, args)
after 0 -> arguments
end
end
end
Marked As Solved
eksperimental
You are about right.
This is the real implementation.
defmodule X do
@callback one(any) :: integer
@callback two(any) :: integer
defmacro __using__(_options) do
quote do
@behaviour X
def three(o, t) do
one(o) + two(t)
end
end
end
end
defmodule XImpl do
use X
@impl X
def one(string) when is_binary(string),
do: String.length(string)
@impl X
def two(string) when is_binary(string),
do: String.length(string)
end
iex(1)> XImpl.three("abcd", "x")
5
If you just want to use use X, you need to add @behaviour X inside your using macro
Also Liked
pickme467
Hi,
I believe there is no magic in dynamic dispatch. What might be missing in the example is how to use parse!
As I understand it you use parse the following way (assuming you have JSONParser defined):
Parser.parse!(JSONParser, "some string")
I hope that helps,
Pawel
eksperimental
I think you are mixing up concepts. Behaviours is just a way to define callbacks that must be implemented, and some can be optional. use can be used in behaviours and protocols to define generic definitions of these callbacks, but nothing stops you from using use out of these situations.
As @pickme467 there is no magic in behaviours, the magic happens with use unless you read the source code you never know what’s happening behind the scenes.
You could achieve JSONParser.parse!("some string"), you would have to define it in your Parser.__using__/1 macro and call use Parser







