View Source TypeCheck.Builtin (TypeCheck v0.13.5)

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

Ommissions

TypeCheck strives to implement all of the syntax and builtin types which Elixir itself also supports. Most of them are supported today. The rest will hopefully be supported in the near future.

For an up-to-date comparison of what types TypeCheck does and does not support w.r.t. Elixir's builtin typespecs, see Comparison to Plain Typespecs.

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

A function (of any arity) returning return_type.

A function taking param_types as parameters, returning return_type.

Syntactic sugar for pid() | port() | reference()

Any integer.

Syntactic sugar for binary() | iolist()

A potentially-improper list containing binaries, single characters, or nested iolists.

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.

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

Syntactic sugar for maybe_improper_list(any(), any())

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.

A binary which contains at least one byte.

A bitstring which contains at least one bit.

Shorthand for nonempty_list(char()).

Any list with at least one element, which has to be terminated by something else than [].

Shorthand for nonempty_list(any()).

A nonempty_list is any list with at least one element.

Any list with at least one element, which might be terminated by something else than [].

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.

Matches any port.

Any integer larger than zero.

Any integer in the half-open range range.

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

Matches any reference.

A bitstring of fixed size.

A bitstring with a fixed prefix_size (which might be 0), followed by zero or repetitions of unit_size.

Any kind of struct.

alias for any/0

Builtin type. Syntactic sugar for :infinity | non_neg_integer()

A tuple of any size (with any elements).

Extensions

Allows constructing map types containing a combination of fixed, required and optional keys (and their associatied type-values)

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.

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

@spec any() :: TypeCheck.Builtin.Any.t()

Any Elixir value.

Will always succeed.

c.f. TypeCheck.Builtin.Any

iex> TypeCheck.conforms!(10, any())
10
iex> TypeCheck.conforms!("foobar", any())
"foobar"
@spec 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.
@spec as_boolean(t :: 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.
@spec atom() :: TypeCheck.Builtin.Atom.t()

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.
@spec binary() :: TypeCheck.Builtin.Binary.t()

Any binary.

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

c.f. TypeCheck.Builtin.Binary

@spec bitstring() :: TypeCheck.Builtin.Bitstring.t()

Any bitstring

c.f. TypeCheck.Builtin.Bitstring

@spec boolean() :: TypeCheck.Builtin.Boolean.t()

Any boolean

(either true or false.)

c.f. TypeCheck.Builtin.Boolean

@spec 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.
@spec 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.

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

c.f. list/1 and char/0

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.
@spec 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}]))

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

@spec float() :: TypeCheck.Builtin.Float.t()

Any float.

C.f. TypeCheck.Builtin.Float

@spec fun() :: TypeCheck.Builtin.Function.t()

Alias for function/0.

iex> TypeCheck.conforms!(&div/2, fun())
&:erlang.div/2
@spec 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.
@spec function(return_type :: TypeCheck.Type.t()) :: TypeCheck.Builtin.Function.t()

A function (of any arity) returning return_type.

Desugaring of (... -> return_type)

See function/2 for more info.

c.f. TypeCheck.Builtin.Function

Link to this function

function(param_types, return_type)

View Source
@spec function(
  param_types :: TypeCheck.Builtin.List.t(TypeCheck.Type.t()),
  return_type :: TypeCheck.Type.t()
) :: TypeCheck.Builtin.Function.t()

A function taking param_types as parameters, returning return_type.

Desugaring of (param_type -> return_type), (param_type, param_type2 -> return_type), (param_type, param_type2, param_type3 -> return_type) etc.

Type-checking a function value against a function-type works a bit differently from most other types. The reason for this is that we can only ascertain whether the function-value works correctly when the function-value is called.

Specifically:

  • When a call to TypeCheck.conforms/3 (and variants) or a function wrapped with a @spec is called, we can immediately check whether a particular parameter:
    • is a function
    • accepts the expected arity
  • Then, the parameter-which-is-a-function is wrapped in a 'wrapper function' which, when called:
    • typechecks whether the passed parameters are of the expected types (This checks whether your function uses the parameter-function correctly.)
    • calls the original function with the parameters.
    • typechecks whether the result is of the expected type. (This checks whether the parameter-function works correctly.)
    • returns the result.

In other words, the 'wrapper function' which is added for a type (param_type, param_type2 -> result_type) works similarly to a named function with the spec @spec myfunction(param_type, param_type2) :: result_type.

iex> # The following passes the first check...
iex> fun = TypeCheck.conforms!(&div/2, (integer(), integer() -> boolean()))
iex> # ... but once the function returns, the wrapper will raise
iex> fun.(20, 5)
** (TypeCheck.TypeError) The call to `#Function<...>/2` failed,
    because the returned result does not adhere to the spec `boolean()`.
    Rather, its value is: `4`.
    Details:
      The result of calling `#Function<...>.(20, 5)`
      does not adhere to spec `(integer(), integer() -> boolean())`. Reason:
        Returned result:
          `4` is not a boolean.

c.f. TypeCheck.Builtin.Function

Syntactic sugar for pid() | port() | reference()

iex> TypeCheck.conforms?(self(), identifier())
true
@spec 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.

Syntactic sugar for binary() | iolist()

A potentially-improper list containing binaries, single characters, or nested iolists.

Syntactic sugar for maybe_improper_list(byte() | binary() | iolist(), binary() | [])

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.
@spec literal(a :: TypeCheck.Builtin.Any.t()) :: %TypeCheck.Builtin.Literal{
  value: term()
}

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.
  • For other kinds of values which Elixir's builtin typespecs do not support as literals, we similarly represent it as a more general type.

C.f. TypeCheck.Builtin.Literal

@spec map() :: TypeCheck.Builtin.Map.t()

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

C.f. TypeCheck.Builtin.Map

Link to this function

map(key_type, value_type)

View Source
@spec map(key_type :: TypeCheck.Type.t(), value_type :: 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}, and indeed a desugaring of this.

Note that multiple optional keypairs are not (yet) supported.

C.f. TypeCheck.Builtin.Map

Syntactic sugar for maybe_improper_list(any(), any())

Link to this function

maybe_improper_list(element_type, terminator_type)

View Source
@spec maybe_improper_list(
  element :: TypeCheck.Type.t(),
  terminator :: TypeCheck.Type.t()
) ::
  TypeCheck.Builtin.MaybeImproperList.t()

WIP

A module-function-arity tuple

C.f. fixed_tuple/1

@spec 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

@spec neg_integer() :: TypeCheck.Builtin.NegInteger.t()

Any integer smaller than zero.

C.f. TypeCheck.Builtin.NegInteger

See none/0.

@spec non_neg_integer() :: TypeCheck.Builtin.NonNegInteger.t()

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.

@spec nonempty_binary() :: TypeCheck.Builtin.SizedBitstring.t()

A binary which contains at least one byte.

Shorthand for sized_bitstring(8, 8).

@spec nonempty_bitstring() :: TypeCheck.Builtin.SizedBitstring.t()

A bitstring which contains at least one bit.

Shorthand for sized_bitstring(1, 1).

Shorthand for nonempty_list(char()).

Link to this function

nonempty_improper_list(element_type, terminator_type)

View Source

Any list with at least one element, which has to be terminated by something else than [].

To be precise, the list needs to be terminated with terminator_type.

Shorthand for nonempty_list(any()).

A nonempty_list is any list with at least one element.

Link to this function

nonempty_maybe_improper_list(element_type, terminator_type)

View Source

Any list with at least one element, which might be terminated by something else than [].

To be precise, the list needs to be terminated with either [] or terminator_type

@spec number() :: TypeCheck.Builtin.Number.t()

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.

@spec one_of(left :: TypeCheck.Type.t(), right :: TypeCheck.Type.t()) ::
  TypeCheck.Type.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).

@spec pid() :: TypeCheck.Builtin.PID.t()

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.

@spec port() :: TypeCheck.Builtin.Port.t()

Matches any port.

c.f. TypeCheck.Builtin.Port

iex> TypeCheck.conforms?(Kernel.make_ref(), reference())
true
iex> some_port = Port.open({:spawn, "cat"}, [:binary])
...> TypeCheck.conforms?(some_port, port())
true
@spec pos_integer() :: TypeCheck.Builtin.PosInteger.t()

Any integer larger than zero.

C.f. TypeCheck.Builtin.PosInteger

@spec range(range :: 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.)

C.f. TypeCheck.Builtin.Range

@spec 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

@spec reference() :: TypeCheck.Builtin.Reference.t()

Matches any reference.

c.f. TypeCheck.Builtin.Reference

iex> TypeCheck.conforms?(Kernel.make_ref(), reference())
true
iex> some_ref = IEx.Helpers.ref(0, 749884137, 111673345, 43386)
...> TypeCheck.conforms!(some_ref, reference())
#Reference<0.749884137.111673345.43386>
@spec sized_bitstring(prefix_size :: TypeCheck.Builtin.NonNegInteger.t()) ::
  TypeCheck.Builtin.SizedBitstring.t()

A bitstring of fixed size.

Desugaring of bitstring types like << _ :: size>>.

c.f. TypeCheck.Builtin.SizedBitstring.

Link to this function

sized_bitstring(prefix_size, unit_size)

View Source
@spec sized_bitstring(
  prefix_size :: TypeCheck.Builtin.NonNegInteger.t(),
  unit_size :: nil | 1..256
) :: TypeCheck.Builtin.SizedBitstring.t()

A bitstring with a fixed prefix_size (which might be 0), followed by zero or repetitions of unit_size.

Desugaring of bitstring types like << _ :: _ * unit_size>> and << _ :: prefix_size, _ :: _ * unit_size>>.

iex> TypeCheck.conforms!("hi", <<_ :: 16>>)
"hi"

iex> TypeCheck.conforms!("bye", <<_ :: 16>>)
** (TypeCheck.TypeError) `"bye"` has a different bit_size (24) than expected (16).

iex> TypeCheck.conforms!(<<1 :: size(2)>>, <<_ :: 2>>)
<<1 :: size(2)>>

iex> TypeCheck.conforms!(<<1 :: size(3)>>, <<_ :: 2>>)
** (TypeCheck.TypeError) `<<1::size(3)>>` has a different bit_size (3) than expected (2).

iex> ["ab", "abcd", "abcdef"] |> Enum.map(&TypeCheck.conforms!(&1, <<_ :: _ * 16>>))
["ab", "abcd", "abcdef"]

iex> TypeCheck.conforms!("abc",  <<_ :: _ * 16>>)
** (TypeCheck.TypeError) `"abc"` has a different bit_size (24) than expected (_ * 16).

iex> ["a", "abc", "abcde"] |> Enum.map(&TypeCheck.conforms!(&1, <<_ :: 8, _ :: _ * 16>>))
["a", "abc", "abcde"]

iex> TypeCheck.conforms!("ab",  <<_ :: 8, _ :: _ * 16>>)
** (TypeCheck.TypeError) `"ab"` has a different bit_size (16) than expected (8 + _ * 16).

c.f. TypeCheck.Builtin.SizedBitstring.

Any kind of struct.

Syntactic sugar for %{:struct => atom(), optional(atom()) => any()}

@spec term() :: TypeCheck.Builtin.Any.t()

alias for any/0

Builtin type. Syntactic sugar for :infinity | non_neg_integer()

@spec tuple() :: TypeCheck.Builtin.Tuple.t()

A tuple of any size (with any elements).

C.f. TypeCheck.Builtin.Tuple

Link to this section Extensions

Link to this function

fancy_map(fixed_kvs, required_kvs, optional_kvs)

View Source

Allows constructing map types containing a combination of fixed, required and optional keys (and their associatied type-values)

Desugaring of most ways of map syntaxes.

Note that because of reasons of efficiency and implementation difficulty, not all possibilities are supported by TypeCheck currently.

Supported are:

  • maps with only fixed keys (%{a: 1, b: 2, "foo" => number()})
  • maps with a single required keypair (%{required(key_type) => value_type})
  • maps with a single optional keypair (%{optional(key_type) => value_type})
  • maps with only fixed keys and one optional keypair (%{:a => 1, :b => 2, "foo" => number(), optional(integer()) => boolean()})

Help with extending this support is very welcome. c.f. https://github.com/Qqwy/elixir-type_check/issues/7

Link to this function

fixed_list(element_types)

View Source
@spec fixed_list(element_types :: TypeCheck.Builtin.List.t(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.

Link to this function

guarded_by(type, guard_ast, module \\ nil)

View Source
@spec guarded_by(
  type :: TypeCheck.Type.t(),
  ast :: TypeCheck.Builtin.Any.t(),
  original_module :: TypeCheck.Builtin.Atom.t() | nil
) :: 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.

Checks whether the given value implements the particular protocol.

For this type-check to work, Protocol Consolidation needs to be active.

data-generation

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 to t will be generated.
  • They don't have a t TypeCheck type, but contain a new/0 function. In this case, a single value is generated each time: the result of calling YourStructModule.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.

Link to this macro

lazy(type_call_ast)

View Source (macro)
@spec 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 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

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

named_type(name, type, type_kind \\ :type, called_as \\ nil)

View Source

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()}.