Spec-Zone.ru › Elixir 1.18

Исходный код 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 не будет доступна ни в какой другой функции в этом модуле (или в любом другом модуле).

Хотя псевдонимы могут быть полезны для создания абстракций в рамках фреймворков и библиотек, разработчики обычно предпочитают использовать 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. Следующая тема — атрибуты модулей.

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

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

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

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

Spec-Zone.ru

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