Haex
"Haskell as Elixir" - A DSL for implementing Haskell style sum and product data types in Elixir
(pronounced "hacks")
A succinct statement generates a number of modules, and helper functions, along with valid dialyzer type and specs to work with those data structures.
The goal is to make it quicker and easier to work with rich types without
having to implement a lot of boilerplate code to build common patterns. This
is particularly true in the case of Elixir structs, which to be used
properly, require a defstruct
call, @type
declaration (which is almost
but not quite identical), and an @enforce_keys
annotaiton. All of these
can be automated away.
A secondary goal is to encourage good use of types that are understand by dialzyer. This makes it easier to work with and reason about the code, and aids documentation of what functions expect and return.
Basics
import Haex
data Maybe.t(a) :: Nothing | Just.t(a)
Maybe.just("cheese")
# {Maybe.Just, "cheese"}
Maybe.Just.new("cheese")
# {Maybe.Just, "cheese"}
Maybe.nothing()
# Maybe.Nothing
Maybe.Nothing.new()
# Maybe.Nothing
When compiled from file, and loaded in iex...
iex(1)> t Maybe
@type t(a) :: Maybe.Nothing.t() | Maybe.Just.t(a)
iex(2) h Maybe.just
def just(arg1)
@spec just(a) :: t(a) when a: var
iex(3) h Maybe.nothing
def nothing()
@spec nothing() :: t(_a)
This one line statement (Maybe.t(a) :: Nothing | Just.t(a)
) generated code that looks something like...
defmodule Maybe do
@type t(a) :: Maybe.Nothing.t() | Maybe.Just.t(a)
defmodule Nothing do
@opaque t() :: __MODULE__
@spec(new() :: t())
def new() do
__MODULE__
end
end
defmodule Just do
@opaque t(a) :: {__MODULE__, a}
@spec(new(a) :: t(a) when a: var)
def new(arg1) do
{__MODULE__, arg1}
end
end
@spec nothing() :: t(_a) when _a: var
def nothing() do
Nothing.new()
end
@spec just(a) :: t(a) when a: var)
def just(arg1) do
Just.new(arg1)
end
end
...saving you a lot of keyboard wear
Enum Types
data Color :: Red | Green | Blue | BlueSteel
Sum Types
data SocialMediaAccount ::
Twitok.t(String.t())
| Facepalm.t(String.t())
| Watzap.t(String.t())
| Instaban.t(String.t())
Product Types
data Pair.t(a, b) :: Pair.t(a, b)
data Either.t(a, b) :: Left.t(a) | Right.t(b)
data Person::
Person.t(
name: String.t(),
social_media: [SocialMediaAccount.t()],
age: integer(),
height: float(),
favourite_ice_cream: String.t(),
standard_quote: String.t()
)
Installation
If available in Hex, the package can be installed
by adding haex
to your list of dependencies in mix.exs
:
def deps do
[
{:haex, "~> 0.0.1"}
]
end
Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/haex.