Current section
Files
Jump to
Current section
Files
lib/rtypes.ex
defmodule RTypes do
@moduledoc """
The module defines the `derive/1` macro which can be used to derive a
run-time checker for the given type.
The module also defines a function `derive/3` which can be used at run
time. However, it must be given arguments as module, type name, and a list of
type args.
## Usage
### Using `derive/1` macro
```
iex> require RTypes, as: RTypes
iex> is_port_number = RTypes.derive(:inet.port_number())
iex> is_port_number.(8080)
true
```
Note that the macro expects the argument as in `module.type(arg1, arg2)`. That
is a module name followed by `.` and the type name, followed by type
parameters enclosed in parenthesis.
### Using `derive/3` function
```
iex> is_keyword_list = RTypes.derive(Keyword, :t, [{:type, 0, :pos_integer, []}])
iex> is_keyword_list.(key1: 4, key2: 5)
true
```
"""
defp expand_type_args(args) when is_list(args) do
Enum.map(args, fn arg ->
{mod, type_name, type_args} =
case arg do
{{:., _, [{:__aliases__, _, [mod]}, type]}, _, type_args} ->
{mod, type, type_args}
{{:., _, [mod, type]}, _, type_args} ->
{mod, type, type_args}
{type, _, type_args} ->
{nil, type, type_args}
end
{mod, type_name, type_args}
case mod do
nil ->
{:type, 0, type_name, expand_type_args(type_args)}
_mod_name ->
{:remote_type, 0, [{:atom, 0, mod}, {:atom, 0, type_name}, expand_type_args(type_args)]}
end
end)
end
defmacro derive(code) do
{mod, type_name, args} =
case quote(do: unquote(code)) do
{{:., _, [{:__aliases__, _, [mod]}, type]}, _, type_args} ->
{mod, type, type_args}
{{:., _, [mod, type]}, _, type_args} ->
{mod, type, type_args}
end
typ = Macro.escape(RTypes.Extractor.extract_type(mod, type_name, expand_type_args(args)))
quote bind_quoted: [typ: typ] do
fn term ->
RTypes.Checker.check!(term, typ)
end
end
end
def derive(mod, type_name, type_args) do
typ = RTypes.Extractor.extract_type(mod, type_name, type_args)
fn term ->
RTypes.Checker.check!(term, typ)
end
end
end