I have a question about code design in Elixir.
I’m writing a library that works with bluetooth and I want to make it general so developers can customize based on their own use case .
An example behaviour is when a new device is discovered, I want to notify the user’s code that a new device is discovered.
So I have few approaches but I’m not happy with any of them.
1: using message passing between processes
defmodule Bluetooth do
def start_discovery(handler_pid) do
#....
#eventually will send a message to `handler_pid` when a device is discovered
#ex: send(handler_pid, {:device_discovered, device})
:ok
end
end
This looks fine if only I could have a behaviour like this
defmodule BluetoothDiscovery do
@callback handle_info({:device_discovered, any}, any) :: any
end
This way users that implement the code explicitly know to implement this function.
I can technically use this but looks weird and compiler won’t complain about missing function if I have another handle_info in my module for example:
defmodule MyBluetoothDiscovery do
use GenServer
@behaviour BluetoothDiscovery
def handle_info(:timeout, state) do
end
end
2: using function callbacks
defmodule Bluetooth do
def start_discovery(handler_module) do
#....
#eventually will call device_discovered function on handler_module
#ex: apply(handler_module, :device_discovered, [device])
:ok
end
end
Now I can define a behaviour and ask users to implement it
defmodule BluetoothDiscovery do
@callback device_discovered(device) :: atom
end
This time the compiler gives me warning if I missed the implementation
defmodule MyBluetoothDiscovery do
use GenServer
@behaviour BluetoothDiscovery
def device_discovered(device) do
#handle new device
end
end
This also looks fine but reminds me of interfaces in OOP land.
Do you have any suggestion?