Spec-Zone.ru › Elixir 1.17

Исходный код alias, require, import и use

Для удобства повторного использования программного обеспечения Elixir предоставляет три директивы (alias, require, и import) плюс макрос, называемый use, краткое описание которых приведено ниже:

# Alias the module so it can be called as Bar instead of Foo.Bar
alias Foo.Bar, as: Bar

# Require the module in order to use its macros
require Foo

# Import functions from Foo so they can be called without the `Foo.` prefix
import Foo

# Invokes the custom code defined in Foo as an extension point
use Foo

Сейчас мы подробно рассмотрим их. Имейте в виду, что первые три называются директивами, потому что они имеют лексическую область видимости, в то время как use является общей точкой расширения, которая позволяет используемому модулю вставлять код.

alias

alias позволяет задавать псевдонимы для любых имен модулей.

Представьте себе модуль, использующий специализированный список, реализованный в Math.List. Директива alias позволяет ссылаться на Math.List так же, как List внутри определения модуля:

defmodule Stats do
  alias Math.List, as: List
  # In the remaining module definition List expands to Math.List.
end

Оригинальный List по-прежнему доступен внутри Stats по полному имени Elixir.List.

Все модули, определенные в Elixir, определены внутри основного Elixir пространства имен, например, Elixir.String. Однако для удобства вы можете опустить "Elixir" при их упоминании.

Псевдонимы часто используются для определения сокращений. Фактически, вызов alias без опции :as автоматически устанавливает псевдоним на последнюю часть имени модуля, например:

alias Math.List

Это то же самое, что:

alias Math.List, as: List

Обратите внимание, что alias имеет лексическую область видимости, что позволяет задавать псевдонимы внутри конкретных функций:

defmodule Math do
  def plus(a, b) do
    alias Math.List
    # ...
  end

  def minus(a, b) do
    # ...
  end
end

В приведенном выше примере, поскольку мы вызываем alias внутри функции plus/2, псевдоним будет действителен только внутри функции plus/2. minus/2 никак не повлияет.

require

Elixir предоставляет макросы как механизм метапрограммирования (написание кода, генерирующего код). Макросы расширяются на этапе компиляции.

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

iex> Integer.is_odd(3)
** (UndefinedFunctionError) function Integer.is_odd/1 is undefined or private. However, there is a macro with the same name and arity. Be sure to require Integer if you intend to invoke this macro
    (elixir) Integer.is_odd(3)
iex> require Integer
Integer
iex> Integer.is_odd(3)
true

В Elixir, Integer.is_odd/1 определен как макрос, чтобы он мог использоваться в качестве условия. Это означает, что для вызова Integer.is_odd/1 нам необходимо сначала загрузить модуль Integer.

Обратите внимание, что, как и директива alias, директива require также имеет лексическую область видимости. Мы более подробно поговорим о макросах в последующих главах.

import

Мы используем import всякий раз, когда хотим получить доступ к функциям или макросам из других модулей без использования полного имени. Обратите внимание, что мы можем импортировать только общедоступные функции, так как частные функции никогда не доступны извне.

Например, если нам нужно несколько раз использовать функцию duplicate/2 из модуля List, мы можем импортировать её:

iex> import List, only: [duplicate: 2]
List
iex> duplicate(:ok, 3)
[:ok, :ok, :ok]

Мы импортировали только функцию duplicate (с арностью 2) из List. Хотя :only необязательно, рекомендуется использовать её, чтобы избежать импорта всех функций данного модуля в текущую область видимости. :except также можно указать в качестве опции, чтобы импортировать всё из модуля, за исключением списка функций.

Обратите внимание, что import также имеет лексическую область видимости. Это означает, что мы можем импортировать конкретные макросы или функции внутри определений функций:

defmodule Math do
  def some_function do
    import List, only: [duplicate: 2]
    duplicate(:ok, 10)
  end
end

В примере выше импортированная List.duplicate/2 видна только внутри этой конкретной функции. duplicate/2 не будет доступна в любой другой функции этого модуля (или любого другого модуля).

Хотя import могут быть полезны для фреймворков и библиотек, чтобы создавать абстракции, разработчики обычно предпочитают использовать alias вместо import в собственных кодовых базах, так как псевдонимы делают происхождение вызываемой функции более ясным.

use

Макрос use часто используется в качестве точки расширения. Это означает, что когда вы use модуль FooBar, вы разрешаете этому модулю вставлять любой код в текущий модуль, такой как импорт самого себя или других модулей, определение новых функций, установка состояния модуля и т. д.

Например, для написания тестов с использованием фреймворка ExUnit разработчик должен использовать модуль ExUnit.Case:

defmodule AssertionTest do
  use ExUnit.Case, async: true

  test "always pass" do
    assert true
  end
end

За кулисами use загружает указанный модуль и затем вызывает обратный вызов __using__/1 в нём, что позволяет модулю вставлять некоторый код в текущий контекст. Некоторые модули (например, упомянутый выше ExUnit.Case, а также Supervisor и GenServer) используют этот механизм, чтобы заполнить ваш модуль некоторым базовым поведением, которое ваш модуль должен переопределить или дополнить.

В общем случае следующий модуль:

defmodule Example do
  use Feature, option: :value
end

компилируется в

defmodule Example do
  require Feature
  Feature.__using__(option: :value)
end

Поскольку use позволяет выполнять любой код, мы не можем точно знать побочные эффекты использования модуля без чтения его документации. Поэтому используйте эту функцию с осторожностью и только в строго необходимых случаях. Не используйте use там, где import или alias подойдут.

Понимание псевдонимов

На этом этапе вы можете задаться вопросом: что именно представляет собой псевдоним в Elixir и как он отображается?

Псевдоним в Elixir — это идентификатор с большой буквы (например, String, Keyword и т. д.), который преобразуется в атом во время компиляции. Например, псевдоним String по умолчанию преобразуется в атом :"Elixir.String":

iex> is_atom(String)
true
iex> to_string(String)
"Elixir.String"
iex> :"Elixir.String" == String
true

Используя директиву alias/2, мы изменяем атом, к которому расширяется псевдоним.

Псевдонимы расширяются до атомов, потому что в виртуальной машине Erlang (и, следовательно, в Elixir) модули всегда представлены атомами:

iex> List.flatten([1, [2], 3])
[1, 2, 3]
iex> :"Elixir.List".flatten([1, [2], 3])
[1, 2, 3]

Вот как мы вызываем модули Erlang:

iex> :lists.flatten([1, [2], 3])
[1, 2, 3]

Вложенные модули

Теперь, когда мы обсудили псевдонимы, давайте поговорим о вложенности и о том, как она работает в Elixir. Рассмотрим следующий пример:

defmodule Foo do
  defmodule Bar do
  end
end

В примере выше будут определены два модуля: Foo и Foo.Bar. Ко второму можно получить доступ как к Bar внутри Foo при условии, что они находятся в одной лексической области видимости.

Если позже модуль Bar перемещен за пределы определения модуля Foo, он должен быть упомянут по полному имени (Foo.Bar) или для него должен быть задан псевдоним с использованием директивы alias.

Примечание: в Elixir, вам не нужно определять модуль Foo перед определением модуля Foo.Bar, так как они фактически независимы. Вышеприведенное также можно записать как:

defmodule Foo.Bar do
end

defmodule Foo do
  alias Foo.Bar
  # Can still access it as `Bar`
end

Установка псевдонима для вложенного модуля не включает в область видимости родительские модули. Рассмотрим следующий пример:

defmodule Foo do
  defmodule Bar do
    defmodule Baz do
    end
  end
end

alias Foo.Bar.Baz
# The module `Foo.Bar.Baz` is now available as `Baz`
# However, the module `Foo.Bar` is *not* available as `Bar`

Как мы увидим в последующих главах, псевдонимы также играют важную роль в макросах, чтобы обеспечить их чистоту.

Многократный alias/import/require/use

Можно alias, import, require, или use несколько модулей сразу. Это особенно полезно, когда мы начинаем вкладывать модули, что очень часто используется при создании приложений Elixir. Например, представьте себе приложение, где все модули вложены под MyApp, вы можете задать псевдонимы модулям MyApp.Foo, MyApp.Bar и MyApp.Baz одновременно следующим образом:

alias MyApp.{Foo, Bar, Baz}

На этом мы завершаем обзор модулей Elixir. Следующая тема — атрибуты модулей.

← Предыдущая страница IO и файловая система
Следующая страница → Атрибуты модулей

Скачать версию ePub

Создано с помощью ExDoc (v0.34.1) для языка программирования Elixir

© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.17.2/alias-require-and-import.html

Spec-Zone.ru

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