defmodule Bio.Behaviours.Converter do
@moduledoc """
Defines behavior for modules to act as a converter between sequences.
The core of this module is to provide the default `to/1` function that returns
the error tuple for undefined conversions. This alleviates the need of the
module needing to implement, and eliminates the possibility of the function
`to/1` raising due to no matching clauses.
To achieve this, the module uses a block for defining the user-side `to/1`
calls. The module is used as such:
``` elixir
defmodule SomeConversion do
use Bio.Behaviours.Converter do
def to(SomeModule), do: &your_elementwise_converter/1
defp your_elementwise_converter(element) do
# conversion logic
end
end
end
```
This defines the elemnt-wise converter that will be used by
`Bio.Sequence.Polymer.convert/3`. The function will be applied to every
element of the base type that uses this conversion module. So if we wanted to
use this converter for `SomeSequence`:
``` elixir
defmodule SomeSequence do
@behaviour Bio.Behaviours.Sequence
@impl Bio.Behaviours.Sequence
def converter, do: SomeConversion
# implementation of other callbacks
end
```
Now you can simply call:
``` elixir
SomeSequence.new("some data")
|> Bio.Polymer.convert(SomeModule)
```
"""
@doc """
Defines the converter's element-wise conversion function
This is called within the `Bio.Polymer.convert/3` function to acquire the
element-wise conversion function for sequence to another.
"""
@callback to(thing :: module()) :: {:ok, (term() -> term())} | {:error, :undef_conversion}
defmacro __using__(opts) do
block = Keyword.get(opts, :do, nil)
quote do
@behaviour Bio.Behaviours.Converter
unquote(block)
def to(module), do: {:error, :undef_conversion}
end
end
end