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.
nonempty
if true, the list must have at least one element; if false, then it may be the empty list[]
.type
the type for all elements of the list, except the final elementfinal
the type of the final element. Typically this is[]
, but other types may be used as the final element and these are calledimproper
lists.
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()})
:lt
intersection
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))
false
usable_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}}]}