TypeCheck v0.2.0 TypeCheck.Builtin View Source

Usually you'd want to import this module when you're using TypeCheck. Feel free to import only the things you need, or hide (using import ... except:) the things you don't.

Link to this section Summary

Built-in Elixir types

Any Elixir value.

Shorthand for range(0..255)

The same as type, but indicates that the result will be used as a boolean.

Any Elixir atom.

Any binary.

Any bitstring

Any boolean

A byte; shorthand for range(0..255)

A char; shorthand for range(0..0x10FFFF)

A list filled with characters; exactly list(char())

A map with exactly the key-value-pairs indicated by keywords

A tuple whose elements are of the types given by list_of_element_types.

Any float.

Alias for function/0.

Any function (of any arity), regardless of input or output types

Any integer.

A list of pairs with atoms as 'keys' and anything allowed as as 'values'.

A list of pairs with atoms as 'keys' and t's as 'values'.

A (proper) list with any type of elements;

A (proper) list containing only elements of type a.

A literal value.

Any Elixir map with any types as keys and any types as values.

A module-function-arity tuple

Any Elixir atom, but indicates that the atom is expected to be used as a module.

Any integer smaller than zero.

Any integer zero or larger.

Matches no value at all.

Any number (either a float or an integer)

Version of one_of that allows passing many possibilities at once.

A union of multiple types (also known as a 'sum type')

Any integer larger than zero.

Any integer in the half-open range range.

Any integer between lower (includsive) and higher (exclusive).

alias for any/0

A tuple of any size (with any elements).

Extensions

A list of fixed size where element_types dictates the types of each of the respective elements.

Adds a 'type guard' to the type, which is an extra check written using arbitrary Elixir code.

Defers type-expansion until the last possible moment.

Any map containing zero or more keys of key_type and values of value_type.

A type with a local name, which can be referred to from a 'type guard'.

A tuple whose elements have any types, but which has exactly size elements.

Link to this section Built-in Elixir types

Any Elixir value.

Will always succeed.

c.f. TypeCheck.Builtin.Any

iex> TypeCheck.conforms!(10, any())
10
iex> TypeCheck.conforms!("foobar", any())
"foobar"

Shorthand for range(0..255)

The same as type, but indicates that the result will be used as a boolean.

Any Elixir atom.

c.f. TypeCheck.Builtin.Atom

iex> TypeCheck.conforms!(:ok, atom())
:ok
iex> TypeCheck.conforms!(:foo, atom())
:foo
iex> TypeCheck.conforms!(10, atom())
** (TypeCheck.TypeError) `10` is not an atom.

Any binary.

A binary is a bitstring with a bitsize divisible by eight.

c.f. TypeCheck.Builtin.Binary

Any bitstring

c.f. TypeCheck.Builtin.Bitstring

Any boolean

(either true or false.)

c.f. TypeCheck.Builtin.Boolean

A byte; shorthand for range(0..255)

c.f. range/1

A char; shorthand for range(0..0x10FFFF)

c.f. range/1

A list filled with characters; exactly list(char())

c.f. list/1 and char/0

A map with exactly the key-value-pairs indicated by keywords

where all keys are required to be literal values, and the values are a type specification.

Desugaring of literal maps like %{a_key: value_type, "other_key" => value_type2}.

Represented in Elixir's builtin Typespecs as

%{required(:a_key) => value_type1, required("other key") => value_type2}

(for e.g. a call to fixed_map([a_key: value_type1, {"other key", value_type2}]))

Link to this function

fixed_tuple(list_of_element_types)

View Source

A tuple whose elements are of the types given by list_of_element_types.

Desugaring of writing tuples directly in your types: {a, b, c} desugars to fixed_tuple([a, b, c]).

Represented in Elixir's builtin Typespecs as a plain tuple, where each of the elements are the respective element of list_of_types.

C.f. TypeCheck.Builtin.Tuple

Any float.

C.f. TypeCheck.Builtin.Float

Alias for function/0.

Any function (of any arity), regardless of input or output types

c.f. TypeCheck.Builtin.Function

Any integer.

C.f. TypeCheck.Builtin.Integer

A list of pairs with atoms as 'keys' and anything allowed as as 'values'.

Shorthand for list({atom(), any()})

iex> x = [a: 1, b: 2]
iex> TypeCheck.conforms!(x, keyword())
[a: 1, b: 2]

iex> y = [a: 1, b: 2] ++ [3, 4]
iex> TypeCheck.conforms!(y, keyword())
** (TypeCheck.TypeError) `[{:a, 1}, {:b, 2}, 3, 4]` does not check against `list({atom(), any()})`. Reason:
  at index 2:
    `3` does not check against `{atom(), any()}`. Reason:
      `3` is not a tuple.

A list of pairs with atoms as 'keys' and t's as 'values'.

Shorthand for list({atom(), t})

A (proper) list with any type of elements;

shorthand for list(any())

C.f. list/1 and any/0

A (proper) list containing only elements of type a.

C.f. TypeCheck.Builtin.List

iex> TypeCheck.conforms!([1,2,3], list(integer()))
[1,2,3]

iex> TypeCheck.conforms!(:foo, list(integer()))
** (TypeCheck.TypeError) `:foo` does not check against `list(integer())`. Reason:
  `:foo` is not a list.

iex> TypeCheck.conforms!([1, 2, 3.3], list(integer()))
** (TypeCheck.TypeError) `[1, 2, 3.3]` does not check against `list(integer())`. Reason:
  at index 2:
    `3.3` is not an integer.

A literal value.

Desugaring of using any literal primitive value (like a particular integer, float, atom, binary or bitstring) directly a type.

For instance, 10 desugars to literal(10).

Represented in Elixir's builtin Typespecs as the primitive value itself.

C.f. TypeCheck.Builtin.Literal

Any Elixir map with any types as keys and any types as values.

C.f. TypeCheck.Builtin.Map

A module-function-arity tuple

C.f. fixed_tuple/1

Any Elixir atom, but indicates that the atom is expected to be used as a module.

c.f. atom/0

Any integer smaller than zero.

C.f. TypeCheck.Builtin.NegInteger

See none/0.

Any integer zero or larger.

C.f. TypeCheck.Builtin.NonNegInteger

Matches no value at all.

none() is not very useful on its own, but it is a useful default in certain circumstances, as well as to indicate that you expect some place to not return at all. (instead for instance throwing an exception or looping forever.)

C.f. TypeCheck.Builtin.None.

Any number (either a float or an integer)

Matches the same as integer | float but is more efficient.

C.f. TypeCheck.Builtin.Number

Link to this function

one_of(list_of_possibilities)

View Source

Version of one_of that allows passing many possibilities at once.

A union of multiple types (also known as a 'sum type')

Desugaring of types separated by | like a | b or a | b | c | d. (and represented that way in Elixir's builtin Typespecs).

c.f. one_of/2.

A union of multiple types (also known as a 'sum type')

Desugaring of types separated by | like a | b or a | b | c | d. (and represented that way in Elixir's builtin Typespecs).

Any integer larger than zero.

C.f. TypeCheck.Builtin.PosInteger

Any integer in the half-open range range.

Desugaring of a..b. (And represented that way in Elixir's builtin Typespecs.)

C.f. TypeCheck.Builtin.Range

Any integer between lower (includsive) and higher (exclusive).

Desugaring of lower..higher. (And represented that way in Elixir's builtin Typespecs.)

C.f. range/1

alias for any/0

A tuple of any size (with any elements).

C.f. TypeCheck.Builtin.Tuple

Link to this section Extensions

Link to this function

fixed_list(element_types)

View Source

A list of fixed size where element_types dictates the types of each of the respective elements.

Desugaring of literal lists like [:a, 10, "foo"].

Cannot directly be represented in Elixir's builtin Typespecs, and is thus represented as [any()] instead.

Link to this function

guarded_by(type, guard_ast)

View Source

Adds a 'type guard' to the type, which is an extra check written using arbitrary Elixir code.

Desugaring of some_type when guard_code.

The type guard is a check written using any Elixir code, which may refer to names set in the type using named_type/2.

If this type guard fails (by returning a non-truthy value), the type will not check.

For user-friendly error-handling, don't let your type guards throw exceptions.

C.f. TypeCheck.Builtin.Guarded

Cannot be represented in Elixir's builtin Typespecs, and is thus represented as type (without the guard) instead.

Link to this macro

lazy(type_call_ast)

View Source (macro)

Defers type-expansion until the last possible moment.

This is used to be able to expand recursive types.

For instance, if you have the following:

defmodule MyBrokenlist do
  type empty :: nil
  type cons(a) :: {a, mylist(a)}
  type mylist(a) :: empty() | cons(a)

  spec new_list() :: mylist(any())
  def new_list() do
    nil
  end

  spec cons_val(mylist(any()), any()) :: mylist(any)
  def cons_val(list, val) do
    {val, list}
  end
end

then when TypeCheck is expanding the specs at compile-time to build the type-checking code, mylist(a) will call cons(a) which will call mylist(a) which will call cons(a) etc. until infinity. This makes compilation hang indefinitely.

To be able to handle types like this, use lazy:

defmodule MyFixedList do
  type empty :: nil
  type cons(a) :: {a, lazy(mylist(a))}
  type mylist(a) :: empty() | cons(a)

  spec new_list() :: mylist(any())
  def new_list() do
    nil
  end

  spec cons_val(mylist(any()), any()) :: mylist(any)
  def cons_val(list, val) do
    {val, list}
  end
end

This will work as intended.

Since lazy/1 defers type-expansion (and check-code-generation) until runtime, the compiler is not able to optimize the type-checking code.

Thus, you should only use it when necessary, since it will be slower than when using the inner type direcly.

In builtin typespecs

lazy/1 does not exist in Elixir's builtin typespecs (since builtin typespecs does not expand types it does not need to handle recursive types in a special way). Therefore, lazy(some_type) is represented as some_type directly in ELixir's builtin typespecs.

Link to this function

map(key_type, value_type)

View Source

Any map containing zero or more keys of key_type and values of value_type.

Represented in Elixir's builtin Typespecs as %{optional(key_type) => value_type}.

C.f. TypeCheck.Builtin.Map

A type with a local name, which can be referred to from a 'type guard'.

This name can be used in 'type guards'. See the module documentation and guarded_by/2 for more information.

Desugaring of name :: type (when :: is used inside a type.).

Cannot directly be represented in Elixir's builtin Typespecs, and is thus represented as type (without the name) instead.

A tuple whose elements have any types, but which has exactly size elements.

Represented in Elixir's builtin Typespecs as a plain tuple, with size elements, where each of the element types is any().

For instance, tuple(3) is represented as {any(), any(), any()}.