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 element
  • final the type of the final element. Typically this is [], but other types may be used as the final element and these are called improper 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}}]}

Link to this section Summary

Link to this section Types

Specs

t() :: %Type.List{final: Type.t(), nonempty: boolean(), type: Type.t()}