View Source TypeCheck.External (TypeCheck v0.13.7)

Working with regular Elixir and Erlang @spec and @type definitions.

Experimental

This module is experimental. The API of the various functions in this module are not yet stable; they might change slightly in future minor versions of the library.

If you encounter a problem, please, open an issue.

Summary

Functions

Non-raising version of TypeCheck.External.apply!/4. Ensures values conform to the given type.

The same as Kernel.apply/3 but ensures that values conform the given type.

Ensure at runtime that arguments at result of function call conform the function spec.

Extract TypeCheck type from the regular Elixir (or Erlang) @spec of the given function.

Extract TypeCheck type from the regular Elixir (or Erlang) @type with the given name.

Functions

Link to this function

apply(type, module, function, args)

View Source
@spec apply(TypeCheck.Type.t(), module(), atom(), list()) ::
  {:ok, any()} | {:error, TypeCheck.TypeError.t()}

Non-raising version of TypeCheck.External.apply!/4. Ensures values conform to the given type.

Returns {:error, reason} on failure, {:ok, function_call_result} otherwise.

iex> alias TypeCheck.Builtin, as: B
iex> type = B.function([B.number], B.number)
iex> TypeCheck.External.apply(type, Kernel, :abs, [-13])
{:ok, 13}
iex> {:error, err} = TypeCheck.External.apply(type, Kernel, :abs, [false])
iex> err.message
"At lib/type_check/external.ex:176:\n    `false` is not a number."
Link to this function

apply!(type, module, function, args)

View Source
@spec apply!(TypeCheck.Type.t(), module(), atom(), list()) :: any()

The same as Kernel.apply/3 but ensures that values conform the given type.

The first argument must be a function type of the function to be called.

In case of type error, raises an exception.

Examples

iex> alias TypeCheck.Builtin, as: B
iex> type = B.function([B.number], B.number)
iex> TypeCheck.External.apply!(type, Kernel, :abs, [-13])
13
iex> TypeCheck.External.apply!(type, Kernel, :abs, ["hello"])
** (TypeCheck.TypeError) At lib/type_check/external.ex:176:
    `"hello"` is not a number.
Link to this macro

enforce_spec!(expr)

View Source (macro)
@spec enforce_spec!(Macro.t()) :: Macro.t() | no_return()

Ensure at runtime that arguments at result of function call conform the function spec.

The function spec is extracted at compile time from the regular Elixir (or Erlang) @spec.

Examples

iex> import TypeCheck.External
iex> enforce_spec!(Kernel.abs(-13))
13
iex> enforce_spec!(Kernel.abs("hi"))
** (TypeCheck.TypeError) At lib/type_check/external.ex:176:
    `"hi"` is not a number.
Link to this function

fetch_spec(module, function, arity)

View Source
@spec fetch_spec(module(), atom(), arity()) :: TypeCheck.Type.t()

Extract TypeCheck type from the regular Elixir (or Erlang) @spec of the given function.

Examples

iex> import TypeCheck.External
iex> {:ok, type} = fetch_spec(Kernel, :abs, 1)
iex> type
#TypeCheck.Type< (number() -> number()) >
iex> {:ok, type} = fetch_spec(Atom, :to_string, 1)
iex> type
#TypeCheck.Type< (atom() -> binary()) >
iex> fetch_spec(Kernel, :non_existent, 1)
{:error, "cannot find spec for function"}
Link to this function

fetch_type(module, type, var_types \\ [])

View Source
@spec fetch_type(module(), atom(), [TypeCheck.Type.t()]) :: TypeCheck.Type.t()

Extract TypeCheck type from the regular Elixir (or Erlang) @type with the given name.

To fetch a generic type, you must pass a list of types to be placed instead of generic variables. If you don't know these types, just punch in any() as many times as you need. For example, to fetch the type of Range.t(left, right), you need to pass [number(), number()] or [any(), any()].

Examples

Fetching a regular type:

iex> import TypeCheck.External
iex> {:ok, type} = fetch_type(String, :t)
iex> type
#TypeCheck.Type< binary() >

Fetching a generic type:

iex> import TypeCheck.External
iex> import TypeCheck.Builtin
iex> {:ok, type} = fetch_type(:elixir, :keyword, [number()])
iex> type
#TypeCheck.Type< list({atom(), number()}) >
iex> {:ok, type} = fetch_type(Range, :t, [integer(), integer()])
iex> type
#TypeCheck.Type< %Range{first: integer(), last: integer(), step: pos_integer() | neg_integer()} >

Fetching non-existent type causes an error:

iex> import TypeCheck.External
iex> fetch_type(String, :non_existent)
{:error, "cannot find type with the given name"}