TypeCheck.Builtin (TypeCheck v0.8.0) View Source
Contains TypeCheck specifications for all 'built-in' Elixir types.
These are all the types described on the 'Basic Types', 'Literals' and 'Builtin Types' sections of the Elixir 'Typespecs' documentation page.
See TypeCheck.DefaultOverrides
for the 'Remote Types' supported by TypeCheck.
Usually you'd want to import this module when you're using TypeCheck.
This is done automatically when calling use TypeCheck
.
If necessary, feel free to hide (using import ... except:
)
the things you don't need.
Ommissions
The following types are currently still missing from this module. This will change in future versions of the library. The hope is to at some point support all of them, or as close to it as feasible.
From the 'Basic Types':
port()
reference()
maybe_improper_list(content_type, termination_type)
nonempty_improper_list(content_type, termination_type)
nonempty_maybe_improper_list(content_type, termination_type)
From the 'Literals':
- Special function syntax. Only
function()
is currently supported. - Special bitstring-patterns. Only
binary()
andbitstring()
are currently supported. - The
optional(any()) => any()
syntax in maps. Currentlyfixed_map
accepts maps with extra keys.
From the 'Builtin Types':
nonempty_charlist()
iodata()
identifier()
iolist()
node()
timeout()
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.
See none/0
.
Any integer zero or larger.
Matches no value at all.
Shorthand for nonempty_list(any()).
A nonempty_list is any list with at least one element.
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')
Matches any process-identifier.
Any integer larger than zero.
Any integer in the half-open range range
.
Any integer between lower
(includsive) and higher
(exclusive).
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.
Checks whether the given value implements the particular protocol.
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
Specs
any() :: TypeCheck.Builtin.Any.t()
Any Elixir value.
Will always succeed.
iex> TypeCheck.conforms!(10, any())
10
iex> TypeCheck.conforms!("foobar", any())
"foobar"
Specs
arity() :: TypeCheck.Builtin.Range.t()
Shorthand for range(0..255)
iex> TypeCheck.conforms!(1, arity())
1
iex> TypeCheck.conforms!(1000, arity())
** (TypeCheck.TypeError) `1000` does not check against `0..255`. Reason:
`1000` falls outside the range 0..255.
Specs
as_boolean(TypeCheck.Type.t()) :: TypeCheck.Type.t()
The same as type
,
but indicates that the result will be used
as a boolean.
iex> TypeCheck.conforms!(:ok, as_boolean(atom()))
:ok
iex> TypeCheck.conforms!(10, as_boolean(atom()))
** (TypeCheck.TypeError) `10` is not an atom.
Specs
atom() :: TypeCheck.Builtin.Atom.t()
Any Elixir 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.
Specs
binary() :: TypeCheck.Builtin.Binary.t()
Any binary.
A binary is a bitstring with a bitsize divisible by eight.
Specs
bitstring() :: TypeCheck.Builtin.Bitstring.t()
Any bitstring
Specs
boolean() :: TypeCheck.Builtin.Boolean.t()
Any boolean
(either true
or false
.)
Specs
byte() :: TypeCheck.Builtin.Range.t()
A byte; shorthand for range(0..255)
c.f. range/1
iex> TypeCheck.conforms!(1, byte())
1
iex> TypeCheck.conforms!(255, byte())
255
iex> TypeCheck.conforms!(256, byte())
** (TypeCheck.TypeError) `256` does not check against `0..255`. Reason:
`256` falls outside the range 0..255.
Specs
char() :: TypeCheck.Builtin.Range.t()
A char; shorthand for range(0..0x10FFFF)
c.f. range/1
iex> TypeCheck.conforms!(?a, char())
97
iex> TypeCheck.conforms!(-1, char())
** (TypeCheck.TypeError) `-1` does not check against `0..1114111`. Reason:
`-1` falls outside the range 0..1114111.
Specs
charlist() :: TypeCheck.Builtin.List.t(TypeCheck.Builtin.Range.t())
A list filled with characters; exactly list(char())
iex> TypeCheck.conforms!('hello world', charlist())
'hello world'
iex> TypeCheck.conforms!("hello world", charlist())
** (TypeCheck.TypeError) `"hello world"` does not check against `list(0..1114111)`. Reason:
`"hello world"` is not a list.
Specs
fixed_map(key_value_type_pairs :: keyword()) :: TypeCheck.Builtin.FixedMap.t()
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}])
)
Specs
fixed_tuple(types :: [TypeCheck.Type.t()]) :: TypeCheck.Builtin.FixedTuple.t()
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
.
Specs
float() :: TypeCheck.Builtin.Float.t()
Any float.
Specs
fun() :: TypeCheck.Builtin.Function.t()
Alias for function/0
.
iex> TypeCheck.conforms!(&div/2, fun())
&:erlang.div/2
Specs
function() :: TypeCheck.Builtin.Function.t()
Any function (of any arity), regardless of input or output types
c.f. TypeCheck.Builtin.Function
iex> TypeCheck.conforms!(&div/2, function())
&:erlang.div/2
iex> TypeCheck.conforms!(&Application.get_env/3, function())
&Application.get_env/3
iex> TypeCheck.conforms!(42, function())
** (TypeCheck.TypeError) `42` is not a function.
Specs
integer() :: TypeCheck.Builtin.Integer.t()
Any integer.
C.f. TypeCheck.Builtin.Integer
iex> TypeCheck.conforms!(42, integer())
42
iex> TypeCheck.conforms!(42.0, integer())
** (TypeCheck.TypeError) `42.0` is not an integer.
iex> TypeCheck.conforms!("hello", integer())
** (TypeCheck.TypeError) `"hello"` is not an integer.
Specs
keyword() :: TypeCheck.Builtin.List.t(TypeCheck.Builtin.FixedTuple.t())
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.
Specs
keyword(TypeCheck.Type.t()) :: TypeCheck.Builtin.List.t(TypeCheck.Builtin.FixedTuple.t())
A list of pairs with atoms as 'keys' and t's as 'values'.
Shorthand for list({atom(), t})
Specs
list() :: TypeCheck.Builtin.List.t(TypeCheck.Builtin.Any.t())
A (proper) list with any type of elements;
shorthand for list(any())
Specs
list(TypeCheck.Type.t()) :: TypeCheck.Builtin.List.t(TypeCheck.Type.t())
A (proper) list containing only elements of type a
.
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.
Specs
literal(term()) :: TypeCheck.Builtin.Literal.t()
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
- for integers, atoms and booleans: the primitive value itself.
- for binaries, a more general
binary()
is used as Elixir's builtin typespecs do not support literal UTF-8 binaries as literal values.
Specs
map() :: TypeCheck.Builtin.Map.t()
Any Elixir map with any types as keys and any types as values.
Specs
mfa() :: TypeCheck.Builtin.FixedTuple.t()
A module-function-arity tuple
C.f. fixed_tuple/1
Specs
module() :: TypeCheck.Builtin.Atom.t()
Any Elixir atom, but indicates that the atom is expected to be used as a module.
iex> TypeCheck.conforms!(String, module())
String
iex> TypeCheck.conforms!(:array, module())
:array
iex> TypeCheck.conforms!("hello", module())
** (TypeCheck.TypeError) `"hello"` is not an atom.
c.f. atom/0
Specs
neg_integer() :: TypeCheck.Builtin.NegInteger.t()
Any integer smaller than zero.
See none/0
.
Specs
non_neg_integer() :: TypeCheck.Builtin.NonNegInteger.t()
Any integer zero or larger.
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
.
Shorthand for nonempty_list(any()).
A nonempty_list is any list with at least one element.
Specs
number() :: TypeCheck.Builtin.Number.t()
Any number (either a float or an integer)
Matches the same as integer | float
but is more efficient.
Specs
one_of(types :: [TypeCheck.Type.t()]) :: TypeCheck.Builtin.OneOf.t()
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
.
Specs
one_of(TypeCheck.Type.t(), TypeCheck.Type.t()) :: TypeCheck.Builtin.OneOf.t()
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).
Matches any process-identifier.
Note that no checks are made to see whether the process is alive or not.
Also, the current property-generator will generate arbitrary PIDs, most of which will not point to alive processes.
Specs
pos_integer() :: TypeCheck.Builtin.PosInteger.t()
Any integer larger than zero.
Specs
range( %Range{first: term(), last: term(), step: term()} | TypeCheck.Builtin.Range.t() ) :: TypeCheck.Builtin.Range.t()
Any integer in the half-open range range
.
Desugaring of a..b
.
(And represented that way in Elixir's builtin Typespecs.)
Specs
range(lower :: integer(), higher :: integer()) :: TypeCheck.Builtin.Range.t()
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
Specs
term() :: TypeCheck.Builtin.Any.t()
alias for any/0
Specs
tuple() :: TypeCheck.Builtin.Tuple.t()
A tuple of any size (with any elements).
Link to this section Extensions
Specs
fixed_list(element_types :: [TypeCheck.Type.t()]) :: TypeCheck.Builtin.FixedList.t()
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.
Specs
guarded_by(TypeCheck.Type.t(), term()) :: TypeCheck.Builtin.Guarded.t()
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.
Specs
impl(module()) :: TypeCheck.Builtin.ImplementsProtocol.t()
Checks whether the given value implements the particular protocol.
For this type-check to work, Protocol Consolidation needs to be active.
Data generation
TypeCheck tries to generate values of any type implementing the protocol. These generators can generate any built-in type for which the protocol is implemented (with the exception of functions, and datetimes).
It can also generate your custom structs, as long as:
- They contain a TypeCheck type called
t
. In this case, any values adhering tot
will be generated. - They don't have a
t
TypeCheck type, but contain anew/0
function. In this case, a single value is generated each time: the result of callingYourStructModule.new/0
.
A deliberate choice was made not to automatically generate values for any module by using struct/0
,
because this would not respect the @enforce_keys
option that might be given to structs.
Specs
lazy(ast :: TypeCheck.Type.t()) :: TypeCheck.Builtin.Lazy.t()
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 spec
s 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.
Specs
map(TypeCheck.Type.t(), TypeCheck.Type.t()) :: TypeCheck.Builtin.Map.t()
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}
.
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.
Specs
tuple(non_neg_integer()) :: TypeCheck.Builtin.FixedTuple.t()
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()}
.
Link to this section Functions
Specs
do_fixed_list([TypeCheck.Type.t()]) :: TypeCheck.Builtin.FixedList.t()
Specs
do_fixed_tuple([TypeCheck.Type.t()]) :: TypeCheck.Builtin.FixedTuple.t()