2016-06-08 4 views
6

у меня есть этот модуль в эликсира с атрибутом:модуль доступа атрибуты вне модуля

defmodule MyAwesomeModule do 
    @awesome_number 7 

    # other stuff... 
end 

Я не могу получить доступ к @awesome_number вне модуля. Я попытался с помощью метода Module.get_attribute/2, но он выдает эту ошибку:

iex(79)> Module.get_attribute(MyAwesomeModule, :awesome_number) 
** (ArgumentError) could not call get_attribute on module MyAwesomeModule because it was already compiled 
    (elixir) lib/module.ex:1101: Module.assert_not_compiled!/2 
    (elixir) lib/module.ex:1016: Module.get_attribute/3 

Так прямо сейчас, я оберточной атрибут модуля в способе доступа к нему, но это не реально сделать смысл для меня. Я мог бы просто использовать метод и прекратить использование атрибута все вместе:

defmodule MyAwesomeModule do 
    @awesome_number 7 

    def awesome_number, do: @awesome_number 

    # other stuff... 
end 

Так что мой вопрос, есть лучший/правильный способ сделать это?

ответ

12

AFAIK нет доступа к атрибутам модуля вне данного модуля. Определение функции для раскрытия атрибута модуля - это путь, который вы уже делаете.

Все еще может быть веской причиной сохранить атрибут модуля, а не просто использовать функцию без атрибута модуля. Это зависит от контекста. Имейте в виду, что значение, хранящееся в атрибутах модуля, вычисляется во время компиляции. При этом у вас могут быть разные причины для использования или не использовать атрибут модуля. Давайте рассмотрим следующие примеры:

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

Если необходимо вычислить awesome_number (долгое время), и ему не нужно изменять его значение, то переход с атрибутом модуля + для его экспонирования - это путь.

Edit:

Существует еще модуль атрибутов из того, что я уже говорил ранее. Они работают лучше, чем просто функции. Вот пример, и цитата из эликсира документы:

defmodule MyServer do 
    @my_data 14 
    def first_data, do: @my_data 
    @my_data 13 
    def second_data, do: @my_data 
end 

MyServer.first_data #=> 14 
MyServer.second_data #=> 13 

Notice that reading an attribute inside a function takes a snapshot of its current value. In other words, the value is read at compilation time and not at runtime. As we are going to see, this makes attributes useful to be used as storage during module compilation.

Используя их Module.register_attribute/3 (http://elixir-lang.org/docs/stable/elixir/Module.html#register_attribute/3) и особенно с опцией accumulate: true, делает их полезными во многих отношениях.

Я хочу сказать, что они могут быть более полезными, чем просто использоваться как постоянная .

+0

Хм. ИМО, которая кажется очень слабой причиной. Я не вижу никакого использования другого использования атрибутов модуля (кроме документов, тегов и т. Д.). – Sheharyar

+1

. Библиотека Elixir Plug Plug - хороший пример использования атрибутов модуля с 'accumulate: true'. Вот ссылка, если вы хотите больше копать в источнике https://github.com/elixir-lang/plug/blob/19f53a67e672152a7393611681431c1e0ec1be04/lib/plug/builder.ex#L121 – ventsislaf

4

Существует способ «обмана» с использованием use и макросов. Посмотрите на this example.

Например предположим, можно определить модуль как:

defmodule AwesomeLibrary do 
    defmacro __using__(_) do 
    quote do 
     def print(s), do: IO.puts(s) 
    end 
    end 
end 

Затем, в некотором модуле вы можете использовать ключевое слово use таким образом.

defmodule TestLibrary do 
    use AwesomeLibrary 
end 

Эффект в том, что все определено в блоке __using__ копируется в новый модуль во время компиляции. Таким образом, в этом случае вы можете использовать TestLibrary.print, даже если print определен в другом модуле.

Используется также для копирования констант. В качестве примера вы можете посмотреть библиотеку TimeX. Он использует a dedicated module for constants, который импортируется всякий раз, когда это необходимо.

Мне кажется, что это лучший способ поделиться определениями констант вокруг большой кодовой базы.

+1

Это отличная ссылка в TimeX. Это также то, как я их использую. Я создал концептуальное приложение, чтобы посмотреть на различные способы обмена «константами» по всем проектам: https://github.com/bill-mybiz/elixir-constants-concept. Я также создал SO «Documentation» на константах («в обзоре» прямо сейчас), потому что это одна из немногих областей, в которых документация эликсира не так сильна. (Почти все остальное задокументировано блестяще). Другим преимуществом этого метода является то, что вы можете использовать их внутри инструкций guard (так как это время компиляции). – ibgib

+0

Если вы просто хотите использовать 'print/1' в других модулях, вы можете просто« импортировать »' AwesomeLibrary', не нужно использовать макросы 'use/2' и' __using __/1'. –

Смежные вопросы