Protoss (protoss v1.0.0)

Protoss is a utility library that allows you to define protocols implementations which automatically delegate back to the module that is the caller of the protocol.

Automatic delegation of protocol functions

The primary purpose of Protoss is to automatically delegate protocol functions. As an example, here is standard elixir code for implementing a protocol:

defprotocol Proto do
  def unwrap(term)
end

defmodule MyStruct do
  defstruct [:value]
  defimpl Proto do
    def unwrap(term), do: term.value
  end
end

Protoss makes this easier to understand by hiding the defimpl boilerplate module. This is a natural fit for Elixir's convention of having module functions whose first parameter is the module's datatype.

use Protoss
defprotocol Proto do
  def unwrap(term)
end

defmodule MyStruct do
  use Proto
  defstruct [:value]
  def unwrap(term), do: term.value
end

Directly delegated functions.

Protoss also allows you to define functions that get directly delegated to the implementation module, that are then logically associated with the protocol, and whose existence is checked by the compiler using the protocol behaviour.

A common use case would be a from_json function which reifies json data into a struct.

Example:

use Protoss
defprotocol Proto do
  def unwrap(term)
  defdelegate from_json(module, json)
end

defmodule MyStruct do
  use Proto
  defstruct [:value]
  def unwrap(term), do: term.value

  # NOTE the arity of the function
  def from_json(%{"value" => v}), do: %__MODULE__{value: v}
end

Protocol body functions

Finally, protoss allows you to write arbitrary functions in the protocol body that aren't necessarily part of the protocol itself, using the after keyword. This could be useful to reduce code duplication if you have a common processing step that must occur in conjunction with a pseudo-private protocol function, or even if you simply have a thematically relevant function that you would like to incorporate into the same namespace.

use Protoss
defprotocol Proto do
  def _unwrap_impl(term)
after
  def unwrap(struct) do
    struct
    |> _unwrap_impl()
    |> SomeOtherModule.process()
  end
end

defmodule MyStruct do
  use Proto
  defstruct [:value]
  def _unwrap_impl(term), do: term.value
end

Summary

Functions

Link to this macro

defdelegate(_)

(macro)
Link to this macro

defprotocol(proto_module, list)

(macro)