Type.Tuple (mavis v0.0.6) View Source

Represents tuple types.

The associated struct has two parameters:

  • :elements which may be a list of types, corresponding to the ordered list of tuple element types.
  • :fixed which is false if the total number of elements is not known, but must be at least as many as are in the :elements list.

Deviations from standard Erlang/Elixir:

Mavis introduces a new type (that is not expressible via dialyzer). This is a tuple with minimum arity (n), this is represented by setting the :fixed field to false. The standard erlang type any tuple has an empty list as its elements and fixed: false.

Examples:

  • the any tuple is %Type.Tuple{elements: [], fixed: false}

    iex> inspect %Type.Tuple{elements: [], fixed: false}
    "tuple()"
  • A tuple of minimum size is represented as follows:

    iex> inspect %Type.Tuple{elements: [any(), any()], fixed: false}
    "tuple({any(), any(), ...})"
  • generic tuples have their types as lists.

    iex> inspect %Type.Tuple{elements: [%Type{name: :atom}, %Type{name: :integer}]}
    "tuple({atom(), integer()})"
    iex> inspect %Type.Tuple{elements: [:ok, %Type{name: :integer}]}
    "tuple({:ok, integer()})"

Shortcut Form

The Type module lets you specify a tuple using "shortcut form" via the Type.tuple/1 macro:

iex> import Type, only: :macros
iex> tuple {...}
%Type.Tuple{elements: [], fixed: false}
iex> tuple {any(), any(), ...}
%Type.Tuple{elements: [any(), any()], fixed: false}
iex> tuple {:ok, integer()}
%Type.Tuple{elements: [:ok, integer()]}

Key functions:

comparison

Longer tuples come after shorter tuples; tuples are then ordered using Cartesian dictionary order along the elements list.

iex> import Type, only: :macros
iex> Type.compare(tuple({}), tuple({:foo}))
:lt
iex> Type.compare(tuple({:foo, 1..10}), tuple({:bar, 10..20}))
:gt

intersection

Tuples of different length do not intersect; the intersection is otherwise the Cartesian intersection of the elements.

iex> import Type, only: :macros
iex> Type.intersection(tuple({}), tuple({:ok, integer()}))
%Type{name: :none}
iex> Type.intersection(tuple({:ok, integer()}), tuple({atom(), 1..10}))
%Type.Tuple{elements: [:ok, 1..10]}

union

Only tuple types of the same length can be non-trivially unioned, and then, only if one tuple type is a subtype of the other, and they must be identical across all but one dimension.

iex> import Type, only: :macros
iex> Type.union(tuple({:ok, 11..20}), tuple({:ok, 1..10}))
%Type.Tuple{elements: [:ok, 1..20]}

subtype?

A tuple type is the subtype of another if its types are subtypes of the other across all Cartesian dimensions, but is not the subtype of a tuple that requires more minimum values.

iex> import Type, only: :macros
iex> Type.subtype?(tuple({:ok, 1..10}), tuple({atom(), integer()}))
true
iex> Type.subtype?(tuple({:ok, 1..10}), tuple({any(), any(), ...}))
true
iex> Type.subtype?(tuple({:ok, 1..10}), tuple({any(), any(), any(), ...}))
false

usable_as

A tuple type is usable as another if it each of its elements are usable as the other across all Cartesian dimensions. If any element is disjoint, then it is not usable.

iex> import Type, only: :macros
iex> Type.usable_as(tuple({:ok, 1..10}), tuple({atom(), integer()}))
:ok
iex> Type.usable_as(tuple({:ok, integer()}), tuple({atom(), 1..10}))
{:maybe, [%Type.Message{type: tuple({:ok, integer()}),
                        target: tuple({atom(), 1..10})}]}
iex> Type.usable_as(tuple({:ok, integer()}), tuple({:error, 1..10}))
{:error, %Type.Message{type: tuple({:ok, integer()}),
                       target: tuple({:error, 1..10})}}

Link to this section Summary

Functions

returns the tuple type at the (0-indexed) tuple slot.

a utility function which takes two type lists and ascertains if they can be merged as tuples.

Link to this section Types

Specs

t() :: %Type.Tuple{elements: [Type.t()], fixed: true}

Link to this section Functions

returns the tuple type at the (0-indexed) tuple slot.

iex> import Type, only: :macros
iex> Type.Tuple.elem(tuple({:ok, pos_integer()}), 1)
%Type{name: :pos_integer}
Link to this function

merge(bigger, smaller, strict \\ true)

View Source

a utility function which takes two type lists and ascertains if they can be merged as tuples.

returns the merged list if the two lists are mergeable. This follows from one of two conditions:

  • each type in the "narrower" type list is a subtype of the corresponding "broader" type list
  • all types are equal except for one

returns nil if the two lists are not mergeable; returns the merged list if the two lists are mergeable.

strict toggles whether a non-fixed tuple is allowed to be the first parameter.

completes full analysis in a single pass of the function.

Used in common between tuples and list parameters.