【发布时间】:2016-02-24 09:47:21
【问题描述】:
在 Ruby 中,如果要在类中定义常量,他们会使用全部大写来定义它们。例如:
class MyClass
MY_FAVORITE_NUMBER = 13
end
你如何在 Elixir 中做到这一点?如果不存在这样的等价物,你如何解决 Elixir 中的幻数问题?
【问题讨论】:
标签: elixir
在 Ruby 中,如果要在类中定义常量,他们会使用全部大写来定义它们。例如:
class MyClass
MY_FAVORITE_NUMBER = 13
end
你如何在 Elixir 中做到这一点?如果不存在这样的等价物,你如何解决 Elixir 中的幻数问题?
【问题讨论】:
标签: elixir
【讨论】:
def my_favorite_number, do: @my_favorite_number
另一种定义常量的方法是我使用 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 有很大帮助。
顺便说一句,如果你好奇的话,你可以看看 repo here。
正如我所说,我添加这个答案主要是为了完整性。
【讨论】:
wxALL 有什么区别?
wxALL 是常量的 OR,而我想的是一个方法调用,比如Time.now。
环顾 github,我发现这对我很有吸引力。允许从其他模块访问常量。
ModuleA
@my_constant 23
defmacro my_constant, do: @my_constant
ModuleB
Require ModuleA
@my_constant ModuleA.my_constant
又一次简单而迷人的灵丹妙药解决方案
【讨论】:
也许你定义了一个常量模块文件,你可以在其中定义宏
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
【讨论】:
qoute do 而不是do?
Elixir 模块可以有关联的元数据。元数据中的每个项目都称为一个属性,并通过其名称访问。您可以使用@name value 在模块内定义它。并且作为@name访问
defmodule Example
@site 'StackOverflow' #defining attribute
def get_site do
@site #access attribute
end
end
请记住,这仅适用于模块的顶层,您不能在函数定义中设置模块属性。
【讨论】:
我想补充一下我是如何开始做常量的,这类似于@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
我了解 Phoenix 如何使用 MyApp.Web 导入/使用子句。我离 Elixir 专家还差得很远,但是通过这种方法,您可以只导入您想要的那些常量,而无需在它们前面加上任何命名空间/范围。这样,您就可以轻松挑选各个常量组。
使用直接函数,(我认为)您必须将它们分解为多个模块,然后导入模块。
我不知道这个与直接模块函数的优化后果,但我认为它非常简洁——尤其是在 Elixir 中“导入”事物的各种方式之间的练习(import、use、@ 987654325@, require 对于来自其他语言的初学者来说非常混乱,这是一个单一的 using 或 import 语句)。
编辑:我已将常量 def 声明更改为 defp。这是因为当多个模块import常量文件时,存在歧义冲突。将它们更改为私有范围的函数可以避免这种冲突。所以每个模块都有自己的相同常量的“私有副本”。
【讨论】: