Type.List (mavis v0.0.6) View Source
Represents lists, except for the empty list, which is represented by
the empty list literal [].
There are three fields for the struct defined by this module.
nonemptyif true, the list must have at least one element; if false, then it may be the empty list[].typethe type for all elements of the list, except the final elementfinalthe type of the final element. Typically this is[], but other types may be used as the final element and these are calledimproperlists.
Shortcut Form
The Type module lets you specify a list using "shortcut form" via the
Type.list/1 and Type.list/2 macros:
iex> import Type, only: :macros
iex> list(pos_integer())
%Type.List{type: %Type{name: :pos_integer}}
iex> list(...)
%Type.List{type: %Type{name: :any}, nonempty: true}
iex> list(pos_integer(), ...)
%Type.List{type: %Type{name: :pos_integer}, nonempty: true}Examples:
- The "any proper list" type is
%Type.List{}. Note this is distinct from[]iex> inspect %Type.List{} "list()" - a list of a given type
iex> inspect %Type.List{type: %Type{name: :integer}} "list(integer())" - a nonempty list of a given type
iex> inspect %Type.List{type: %Type{name: :integer}, nonempty: true} "list(integer(), ...)" - an improper list must be nonempty, and looks like the following:
iex> inspect %Type.List{type: %Type{module: String, name: :t}, ...> nonempty: true, ...> final: %Type{module: String, name: :t}} "nonempty_improper_list(String.t(), String.t())" - a maybe improper list should have empty list as a subtype of the final field.
iex> inspect %Type.List{type: %Type{module: String, name: :t}, ...> final: %Type.Union{of: [[], %Type{module: String, name: :t}]}} "maybe_improper_list(String.t(), String.t())"
Key functions:
comparison
nonempty: false list types come after nonempty: true list types; and list
types are ordered by the type order of their content types, followed by the type
order of their finals. The empty list type comes between the two nonempty
categories.
iex> import Type, only: :macros
iex> Type.compare(list(...), [])
:lt
iex> Type.compare(list(), [])
:gt
iex> Type.compare(list(integer()), list(atom()))
:lt
iex> Type.compare(%Type.List{final: integer()}, %Type.List{final: atom()})
:ltintersection
The intersection of two list types is the intersection of their contents; a
nonempty: true list type intersected with a nonempty: false list type is nonempty: true
iex> import Type, only: :macros
iex> Type.intersection(list(...), list())
%Type.List{nonempty: true}
iex> Type.intersection(list(1..20), list(10..30))
%Type.List{type: 10..20}union
The intersection of two list types is the union of their contents; a
nonempty: true list type intersected with a nonempty: false list type is nonempty: false
iex> import Type, only: :macros
iex> Type.union(list(...), list())
%Type.List{}
iex> Type.union(list(1..10), list(10..20))
%Type.List{type: 1..20}subtype?
A list type is a subtype of another if its contents are subtypes of each other;
a nonempty: true list type is subtype of its nonempty: false counterpart.
iex> import Type, only: :macros
iex> Type.subtype?(list(...), list())
true
iex> Type.subtype?(list(1..10), list(2..30))
falseusable_as
A list type is usable_as another if its contents are usable_as the other's;
nonempty: false list types might be usable as nonempty: true types.
iex> import Type, only: :macros
iex> Type.usable_as(list(1..10), list(integer()))
:ok
iex> Type.usable_as(list(1..10), list(atom())) # note it might be the empty list
{:maybe, [%Type.Message{type: %Type.List{type: 1..10}, target: %Type.List{type: %Type{name: :atom}}}]}
iex> Type.usable_as(list(), list(...))
{:maybe, [%Type.Message{type: %Type.List{}, target: %Type.List{nonempty: true}}]}