Packages
croma
0.3.2
0.13.0
0.12.0
0.11.3
0.11.2
0.11.1
0.11.0
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.3
0.7.2
0.7.1
0.7.0
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.1
0.5.0
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
Elixir macro utilities to make type-based programming easier
Current section
Files
Jump to
Current section
Files
README.md
Croma
=====
Elixir macro utilities.
- [API Documentation](http://hexdocs.pm/croma/)
- [Hex package information](https://hex.pm/packages/croma)
[](https://hex.pm/packages/croma)
[](https://hex.pm/packages/croma)
[](https://travis-ci.org/skirino/croma)
[](https://coveralls.io/r/skirino/croma?branch=master)
[](http://inch-ci.org/github/skirino/croma)
[](https://github.com/skirino/croma/issues)
[](https://github.com/skirino/croma/pulls)
## Usage
- Add `:croma` as a mix dependency.
- `$ mix deps.get`
- Add `use Croma` to import macros defined in this package.
- Hack!
## Defining functions
### `Croma.Defpt.defpt`
- Unit-testable `defp` that is simply converted to
- `def` if `Mix.env == :test`,
- `defp` otherwise.
### `Croma.Defun`
- Type specification oriented function definition
- Example 1
```ex
import Croma.Defun
defun f(a :: integer, b :: String.t) :: String.t do
"#{a} #{b}"
end
```
is expanded to
```ex
@spec f(integer, String.t) :: String.t
def f(a, b) do
"#{a} #{b}"
end
```
- Example 2
```ex
import Croma.Defun
defun dumbmap(as :: [a], f :: (a -> b)) :: [b] when a: term, b: term do
([] , _) -> []
([h | t], f) -> [f.(h) | dumbmap(t, f)]
end
```
is expanded to
```ex
@spec dumbmap([a], (a -> b)) :: [b] when a: term, b: term
def dumbmap(as, f)
def dumbmap([], _) do
[]
end
def dumbmap([h | t], f) do
[f.(h) | dumbmap(t, f)]
end
```
- There are also `defunp` and `defunpt` macros for private functions.
- Known limitations:
- Pattern matching against function parameters should use `(param1, param2) when guards -> block` style.
- Overloaded typespecs are not supported.
## `Croma.Monad`
- An interface definition of the monad typeclass.
- Modules that `use Croma.Monad` must implement the following interface:
- `@type t(a)` with a type parameter `a`.
- `@spec pure(a: a) :: t(a) when a: any`
- `@spec bind(t(a), (a -> t(b))) :: t(b) when a: any, b: any`
- By using the concrete implementations of the above interface, `Croma.Monad` provides the default implementations of the following functions:
- As Functor:
- `@spec map(t(a), (a -> b)) :: t(b) when a: any, b: any`
- As Applicative:
- `@spec ap(t(a), t((a -> b))) :: t(b) when a: any, b: any`
- `@spec sequence([t(a)]) :: t([a]) when a: any`
- Note that the order of parameters in `map`/`ap` is different from that of Haskell counterparts, in order to leverage Elixir's pipe operator `|>`.
- `Croma.Monad` also provides `bind`-less syntax similar to Haskell's do-notation.
For example,
```ex
MonadImpl.m do
x <- mx
y <- my
pure f(x, y)
end
```
is converted to
```ex
MonadImpl.bind(mx, fn x ->
MonadImpl.bind(my, fn y ->
MonadImpl.pure f(x, y)
end)
end)
```
### `Croma.Result`
- `Corma.Result.t(a)` is defined as `@type t(a) :: {:ok, a} | {:error, any}`.
This module implements `Croma.Monad` interface.
### `Croma.ListMonad`
- Implementation of `Croma.Monad` for lists.
`Croma.ListMonad.t(a)` is just an alias to `[a]`.
## Working with structs
### `Croma.Struct`
- Utility module to define structs with type specification and validation functions.
```ex
iex> defmodule I do
...> @type t :: integer
...> def validate(i) when is_integer(i), do: {:ok, i}
...> def validate(_), do: {:error, {:invalid_value, [__MODULE__]}}
...> def default, do: 0
...> end
...> defmodule S do
...> use Croma.Struct, fields: [i: I]
...> end
...> S.validate([i: 5])
{:ok, %S{i: 5}}
...> S.validate(%{i: "not_an_integer"})
{:error, {:invalid_value, [S, I]}}
...> {:ok, s} = S.new([])
{:ok, %S{i: 0}}
...> S.update(s, [i: 2])
{:ok, %S{i: 2}}
...> S.update(s, %{"i" => "not_an_integer"})
{:error, {:invalid_value, [S, I]}}
```
- Some helper modules for "per-field module"s that are passed as options to `use Croma.Struct` (e.g. `I` in the above example) are available.
- Wrappers of built-in types such as `Croma.String`, `Croma.Integer`, etc.
- Utility modules such as `Croma.SubtypeOfString` to define "subtypes" of existing types.
- Ad-hoc module generators defined in `Croma.TypeGen`.
### `Croma.StructCallSyntax`
- A new syntax (which uses `~>` operator) for calls to functions that take structs as 1st arguments.
```ex
iex> defmodule S do
...> defstruct [:a, :b]
...> def f(s, i) do
...> s.a + s.b + i
...> end
...> end
...> import Croma.StructCallSyntax
...> s = %S{a: 1, b: 2}
...> s~>f(3) # => S.f(s, 3)
6
```