2015-11-22 3 views
23

В Ruby, если бы кто-то определял константы в классах, они определяли бы их, используя все кепки. Например:Как вы определяете константы в модулях Elixir?

class MyClass 
    MY_FAVORITE_NUMBER = 13 
end 

Как вы это делаете в Эликсире? И если такой эквивалент не существует, как вы обходите проблему магических чисел в Эликсире?

ответ

25

Вы можете предварять ваше имя переменной @:

defmodule MyModule do 
    @my_favorite_number 13 
end 

Вот модулей docs

+1

, но @my_favorite_num ber нельзя получить извне модуля –

+0

@lfx_cool правильно. если вы хотите получить доступ к нему извне модуля, вы можете определить функцию, которая возвращает свое значение: 'def my_favorite_number, do: @ my_favorite_number' – AbM

+2

Это метод getter, если у нас есть ярлыки для этого, например. attr_reader: my_favorite_number –

4

Elixir могут быть связаны метаданные. Каждый элемент в метаданных называется атрибутом и имеет доступ к его имени. Вы определяете его внутри модуля, используя @name value. И доступна как @name

defmodule Example 
    @site 'StackOverflow' #defining attribute 

    def get_site do 
    @site #access attribute 
    end 
end 

Remeber это работает только на верхнем уровне модуля, и вы не можете установить атрибут модуля внутри определения функции.

7

Другой подход к определению констант - это тот, который я использовал с файлами заголовков wxErlang. То есть вы можете просто определить одну функцию линии, которая возвращает постоянное значение для вас. Как так:

def wxHORIZONTAL, do: 4 
    def wxVERTICAL, do: 8 
    def wxBOTH, do: (wxHORIZONTAL ||| wxVERTICAL) 

и вот еще один пример из того же исходного кода:

# From "defs.h": wxDirection 
    def wxLEFT, do: 16 
    def wxRIGHT, do: 32 
    def wxUP, do: 64 
    def wxDOWN, do: 128 
    def wxTOP, do: wxUP 
    def wxBOTTOM, do: wxDOWN 
    def wxNORTH, do: wxUP 
    def wxSOUTH, do: wxDOWN 
    def wxWEST, do: wxLEFT 
    def wxEAST, do: wxRIGHT 
    def wxALL, do: (wxUP ||| wxDOWN ||| wxRIGHT ||| wxLEFT) 

Как вы можете видеть, что делает его немного легче определить константу в терминах другой константы. И когда мне нужны эти константы в другом модуле, все, что мне нужно сделать, это require WxConstants в верхней части модуля. Это упрощает определение константы в одном месте и использование ее в нескольких других - помогает много с DRY.

Кстати, вы можете увидеть репо here, если вам интересно.

Как я уже сказал, я добавляю этот ответ в основном ради полноты.

+0

Это классно, но иногда мне нужна константа, которая вычисляется значение, которое, по-видимому, ведет вниз по кроличьей дыре функции memoization http://stackoverflow.com/questions/35465306/how-to-create-a-global-variable-in-elixir-module/35468186#35468186. Полезно знать, если возникнет такая необходимость. –

+0

Я не уверен, что вы подразумеваете под _computed_value _ - какая разница между тем, что вы подразумеваете под вычисленным значением и, например, 'wxALL' выше? –

+0

'wxALL' является OR констант, а то, о чем я думал, было вызовом метода, например' Time.now'. –

1

Я хотел бы добавить, как я начал делать константы, которая похожа на ответ @Onorio Catenacci, но использует процитировать:

defmodule IbGib.Constants do 

    @doc """ 
    Use this with `use IbGib.Constants, :ib_gib` 
    """ 
    def ib_gib do 
    quote do 
     defp delim, do: "^" 
     defp min_id_length, do: 1 
     # etc... 
    end 
    end 

    @doc """ 
    Use this with `use IbGib.Constants, :error_msgs` 
    """ 
    def error_msgs do 
    quote do 
     defp emsg_invalid_relations do 
     "Something about the rel8ns is invalid. :/" 
     end 
     # etc... 
    end 
    end 

    @doc """ 
    When used, dispatch to the appropriate controller/view/etc. 
    """ 
    defmacro __using__(which) when is_atom(which) do 
    apply(__MODULE__, which, []) 
    end 
end 

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

use IbGib.Constants, :ib_gib # < specifies only the ib_gib constants 
use IbGib.Constants, :error_msgs 

# ... then in some function 
Logger.error emsg_invalid_relations 

Я получил это с тем, как Феникс делает импорт/использование предложений с MyApp.Web. Я нигде не знаком с экспертом Elixir, но с помощью этого метода вы можете импортировать только те константы, которые вы хотите, и вам не нужно префикс их с помощью какого-либо пространства имен/scoping. Таким образом, вы можете легко выбирать и выбирать отдельные группы констант.

С прямыми функциями (я думаю) вам придется разбить их на несколько модулей, а затем импортировать модуль.

Я не знаю оптимизации последствия этого против прямых функций модуля, но я думал, что это было очень аккуратно - особенно для практики между различными способами «импорт» вещи в эликсира (import, use, alias, require является очень запутанный, как новичок, поступающий с других языков, где это один оператор using или import).

EDIT: Я изменил константу def объявлений на defp. Это связано с тем, что когда несколько модулей import файла констант, существует конфликт двусмысленности. Изменение их для функций, ограниченных в частном порядке, позволяет избежать этого конфликта. Поэтому каждый модуль имеет свою собственную «частную копию» той же константы.

2

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

defmodule MyApp.Constants do 
    defmacro const_a do 
    quote do: "A" 
    end 
end 

Вы можете использовать его, импортировать его в любой другой модуль

defmodule MyApp.ModuleA do 
    import MyApp.Constants 

    def get_const_a do 
    const_a() 
    end 
end 

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

case something do 
    const_a() -> do_something 
    _ -> do_something_else 
end