Spec-Zone.ru › Elixir 1.5

Типы спецификаций

В Elixir есть обозначения для объявления типов и спецификаций. Elixir — язык с динамической типизацией, и поэтому спецификации типов никогда не используются компилятором для оптимизации или изменения кода. Тем не менее, использование спецификаций типов полезно, потому что

  • они предоставляют документацию (например, такие инструменты, как ExDoc, показывают спецификации типов в документации)
  • они используются инструментами, такими как Dialyzer, которые могут анализировать код с типами, чтобы найти несоответствия типов и возможные ошибки

Спецификации типов (иногда называемые typespecs) определяются в разных контекстах с помощью следующих атрибутов:

  • @type
  • @opaque
  • @typep
  • @spec
  • @callback
  • @macrocallback

Дополнительную информацию об определении типов и типов спецификаций см. в подразделах «Определение типа» и «Определение спецификации» ниже.

Типы и их синтаксис

Синтаксис, предоставляемый Elixir для спецификаций типов, похож на синтаксис Erlang. Большинство встроенных типов, предоставляемых в Erlang (например, pid()) выражаются аналогичным образом: pid() (или просто pid). Поддерживаются также параметризованные типы (например, list(integer)) и типы удалённых типов (например, Enum.t). Целые числа и литералы атомов разрешены как типы (например, 1, :atom, или false). Все остальные типы строятся из объединений предопределённых типов. Разрешены некоторые сокращения, такие как [...], <<>>, и {...}.

Основные типы

type :: any()                   # the top type, the set of all terms
      | none()                  # the bottom type, contains no terms
      | atom()
      | map()                   # any map
      | pid()                   # process identifier
      | port()
      | reference()
      | struct()                # any struct
      | tuple()                 # tuple of any size

                                ## Numbers
      | float()
      | integer()
      | neg_integer()           # ..., -3, -2, -1
      | non_neg_integer()       # 0, 1, 2, 3, ...
      | pos_integer()           # 1, 2, 3, ...

                                                    ## Lists
      | list(type)                                  # proper list ([]-terminated)
      | nonempty_list(type)                         # non-empty proper list
      | maybe_improper_list(type1, type2)           # proper or improper list
      | nonempty_improper_list(type1, type2)        # improper list
      | nonempty_maybe_improper_list(type1, type2)  # non-empty proper or improper list

      | Literals                # Described in section "Literals"
      | Builtin                 # Described in section "Built-in types"
      | Remotes                 # Described in section "Remote types"
      | UserDefined             # Described in section "User-defined types"

Литералы

В спецификациях типов также поддерживаются следующие литералы:

type ::                               ## Atoms
        :atom                         # atoms: :foo, :bar, ...
      | true | false | nil            # special atom literals

                                      ## Bitstrings
      | <<>>                          # empty bitstring
      | <<_::size>>                   # size is 0 or a positive integer
      | <<_::_*unit>>                 # unit is an integer from 1 to 256
      | <<_::size, _::_*unit>>

                                      ## Functions
      | (... -> type)                 # any arity, returns type
      | (() -> type)                  # 0-arity, returns type
      | (type1, type2 -> type)        # 2-arity, returns type

                                      ## Integers
      | 1                             # integer
      | 1..10                         # integer from 1 to 10

                                      ## Lists
      | [type]                        # list with any number of type elements
      | []                            # empty list
      | [...]                         # shorthand for nonempty_list(any())
      | [type, ...]                   # shorthand for nonempty_list(type)
      | [key: value_type]             # keyword list with key :key of value_type

                                              ## Maps
      | %{}                                   # empty map
      | %{key: value_type}                    # map with required key :key of value_type
      | %{required(key_type) => value_type}   # map with required pairs of key_type and value_type
      | %{optional(key_type) => value_type}   # map with optional pairs of key_type and value_type
      | %SomeStruct{}                         # struct with all fields of any type
      | %SomeStruct{key: value_type}          # struct with required key :key of value_type

                                      ## Tuples
      | {}                            # empty tuple
      | {:ok, type}                   # two-element tuple with an atom and any type

Встроенные типы

Следующие типы также предоставляются Elixir в качестве сокращений на основе основных и литеральных типов, описанных выше.

Встроенный типОпределён как
term()any()
arity()0..255
as_boolean(t)t
binary()<<_::_*8>>
bitstring()<<_::_*1>>
boolean()false | true
byte()0..255
char()0..0x10FFFF
charlist()[char()]
nonempty_charlist()[char(), ...]
fun()(... -> any)
function()fun()
identifier()pid() | port() | reference()
iodata()iolist() | binary()
iolist()maybe_improper_list(byte() | binary() | iolist(), binary() | [])
keyword()[{atom(), any()}]
keyword(t)[{atom(), t}]
list()[any()]
nonempty_list()nonempty_list(any())
maybe_improper_list()maybe_improper_list(any(), any())
nonempty_maybe_improper_list()nonempty_maybe_improper_list(any(), any())
mfa(){module(), atom(), arity()}
module()atom()
no_return()none()
node()atom()
number()integer() | float()
struct()%{:__struct__ => atom(), optional(atom()) => any()}
timeout():infinity | non_neg_integer()

Типы удаленных данных

Любой модуль также может определить свои собственные типы, и модули в Elixir не являются исключением. Например, модуль Range определяет тип t/0, который представляет диапазон: к этому типу можно обратиться как к Range.t/0. Аналогично, строка является String.t/0, любой перечисляемый объект может быть Enum.t/0 и так далее.

Карты

Ключевые типы в картах могут перекрываться, и если это происходит, приоритет отдаётся левому ключу. Значение карты не относится к этому типу, если оно содержит ключ, который не входит в разрешённые ключи карты.

Если вы хотите указать, что разрешены ключи, которые ранее не были определены в карте, принято завершать тип карты optional(any) => any.

Обратите внимание, что синтаксическое представление map() равно %{optional(any) => any}, а не %{}. Обозначение %{} задаёт одиночный тип для пустой карты.

Пользовательские типы

Атрибуты модуля @type, @typep, и @opaque могут использоваться для определения новых типов:

@type type_name :: type
@typep type_name :: type
@opaque type_name :: type

Тип, определённый с помощью @typep, является закрытым. Непрозрачный тип, определённый с помощью @opaque, — это тип, где внутренняя структура типа не будет видна, но тип по-прежнему является публичным.

Типы могут быть параметризованы путём определения переменных в качестве параметров; эти переменные затем могут использоваться для определения типа.

@type dict(key, value) :: [{key, value}]

Определение спецификации

@spec function_name(type1, type2) :: return_type
@callback function_name(type1, type2) :: return_type
@macrocallback macro_name(type1, type2) :: Macro.t

Обработчики вызовов используются для определения функций обработчиков поведений (см. страницу «Поведения» в документации для получения дополнительной информации о поведении).

Гварды могут использоваться для ограничения переменных типа, предоставляемых в качестве аргументов функции.

@spec function(arg) :: [arg] when arg: atom

Если вы хотите указать более одной переменной, разделите их запятой.

@spec function(arg1, arg2) :: [arg1, arg2] when arg1: atom, arg2: integer

Переменные типов без ограничений также могут быть определены.

@spec function(arg) :: [arg] when arg: var

Вы также можете назвать свои аргументы в typespec, используя синтаксис arg_name :: arg_type. Это особенно полезно в документации как способ различения нескольких аргументов одного типа (или нескольких элементов одного типа в определении типа):

@spec days_since_epoch(year :: integer, month :: integer, day :: integer) :: integer
@type color :: {red :: integer, green :: integer, blue :: integer}

Спецификации могут быть перегружены, как и обычные функции.

@spec function(integer) :: atom
@spec function(atom) :: integer

Примечания

Elixir не рекомендует использовать тип t:string/0, так как он может быть перепутано с бинарными данными, которые в Elixir называются «строками» (в отличие от списков символов). Чтобы использовать тип, который называется t:string/0 в Erlang, необходимо использовать тип charlist/0, который является синонимом для string. Если вы используете string, компилятор выдаст предупреждение.

Если вы хотите обратиться к типу «строка» (тот, с которым работают функции в модуле String), используйте тип String.t/0 вместо него.

© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.5.3/typespecs.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API