I’ve started working on “sum type” package to scratch my own itch and would like to get some feedback on the API I’ve chosen. This is how you’d defined a sum type (often referred to as “union”).
For the example I’m gonna use the probably well known “Maybe” sum type:
defmodule Maybe do
import ExUnion
defunion some(value) | none
end
(I actually would’ve loved to do defunion Some(value) | None
but that’s invalid Elixir syntax. )
To ease usage and integration into existing code-bases I’ve decided to transform this as much as possible into existing Elixir structures and patterns. So this snippet above would compile to:
defmodule Maybe do
defmodule Some do
@type t :: %__MODULE__{value: any}
defstruct [:value]
def new(value) do
%__MODULE__{value: value}
end
end
defmodule None do
@type t :: %__MODULE__{}
defstruct []
def new() do
%__MODULE__{}
end
end
@type t :: Some.t() | None.t()
defdelegate some(value),
to: Some,
as: :new
defdelegate none(),
to: None,
as: :new
defguard is_maybe(value) when is_struct(value, Some) or is_struct(value, None)
end
Usage then might look like this:
def some_function(maybe) when is_maybe(maybe) do
case maybe do
%Maybe.Some{value: value} ->
# Do something with value
%Maybe.None{} ->
# Do something else
end
end
Or building:
def returning_maybe(value) do
case value do
nil -> Maybe.none()
non_nil -> Maybe.some(non_nil)
end
end
Is there anything you’d be missing with this API? I’m also planning to support type annotations in the definition, so things like this would be possible:
defmodule Shape do
import ExUnion
defunion rectangle(height :: float, width :: float) | circle(radius :: float) | triangle(base :: float, height :: float)
end