Spec-Zone.ru › Elixir 1.4

Типы

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

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

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

  • @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()
      | port()
      | reference()
      | struct()                # any struct
      | tuple()                 # tuple of any size

                                ## Numbers
      | float()                 # float
      | integer()               # 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"

Литералы

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

type :: :atom                         ## Atoms
      | 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()]
fun() (... -> any)
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

Вы также можете именовать свои аргументы в спецификации типов с использованием синтаксиса 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.4.5/typespecs.html

Spec-Zone.ru

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