Bri*_*ler 5 constraints constants immutability elixir reusability
我觉得有些误导和困惑,尽管Elixir具有不可变性,但不可变性却埋在可变抽象层之间。
例如,
如果我有代码:
foo = {:cat, "Puff"}
bar = foo
foo = {:cat, "Pepper"}
Run Code Online (Sandbox Code Playgroud)
我可以期望bar会保留下来,{:cat, "Puff"}
但是foo的不断变化的值可能使我们难以推理。
当我第一次阅读模块属性时,我以为它们会提供一些解决方案,但有人向我指出,这是完全有效的代码:
defmodule Cats do
@cat "Puff"
def foo, do: @cat
@cat "Pepper"
def bar, do: @cat
end
Run Code Online (Sandbox Code Playgroud)
可以依靠保持不变的功能定义吗?例如。
defmodule Cats do
def foo, do: "Puff"
def bar, do: "Pepper"
end
Run Code Online (Sandbox Code Playgroud)
这样的代码可以被认为是惯用的吗?有什么原因我不应该这样做吗?
我是否可以将其他任何种类的实体声明为具有某种价值,而没有其他任何代码赋予该实体新价值的可能性?
这样的代码可以被认为是惯用的吗?
没有。
有什么原因我不应该这样做吗?
一吨
首先,您不需要BEAM中的常量。只需使用"Puff"和"Piffles"无处不在。不会分配新的内存。任何条款也是如此。元组的最后一个错误已在1.6中修复。
因此根本不需要创建常量。
如果要缩短名称以调用值,可以定义一个宏,编译器将在任何地方内联该宏。这比调用一个函数更有效(稍微,但是仍然)。
阅读完所有评论后,我觉得我们可能会被这里的术语所困扰。人们在CS中称常量为很多东西。例如 在Java中,有final int i = 42;和public enum Math { PI }。像Ruby这样的常量CONST Pi = 3.14159265。还有像Javascript 这样的常量const i = 42;。
他们都不同。Java枚举一旦设置,就不会更改。Ruby允许重置。Java final和Javascript const基本相同,在不失一般性的情况下,可以忽略细微差别。
到目前为止,一切都很好。您似乎在谈论Javascript const。长生不老药具有很大的形状范围。一个人不能不小心修改任何东西。但是有一个重新绑定。但是,不能从另一个作用域内部重新绑定变量。
iex|1 ? foo = 42
iex|2 ? if true, do: foo = :bar
#? warning: variable "foo" is unused
iex|3 ? foo
#? 42
Run Code Online (Sandbox Code Playgroud)
foo在块,宏/函数调用,闭包内分配任何不同的作用域都不会修改原始变量。
唯一可能的重新绑定可能在同一范围内发生。如果我们在谈论函数中的局部变量,那么偶然地无意地重新绑定变量的机会几乎为零(这在Jose 的这篇出色文章中已经提到过。)如果我们在谈论模块属性,它们对于由编译器内联的模块作用域,无法从外部访问。一个人可能会同时使用累积的和未累积的模块属性,并且有一种方法可以通过javascript声明正累积的属性并引用列表的开头来声明一个实常数,但是没有人以这种方式使用它,因为模块属性是通常在模块顶部声明,很容易掌握是否存在重复的名称。
长话短说,const用Javascript可以避免开发人员在由于不可变性和作用域而在Elixir中根本不可能的情况下开枪。
在您认为需要的地方显示任何代码示例,const然后我将展示如何在不使用任何类似东西const的情况下将其重构为惯用的Elixir 。
我想你回答了你自己的问题。我的理解是,使用模块属性作为常量是标准做法,甚至在入门指南中推荐,但正如您所指出的,模块属性可以在编译时重新绑定。如果您确实想要保证“如果我使用标记实体,结果将始终相同”,那么也正如您所指出的,定义函数可以保证这一点。
尽管即使使用函数,也可以声明另一个返回不同值的子句:
defmodule Cats do
def foo, do: "Puff"
def foo("not Puff"), do: "Piggles"
end
Run Code Online (Sandbox Code Playgroud)
如果您不想声明很多函数,则可以声明一个返回已定义值的映射的函数。
defmodule Cats do
def names, do: %{foo: "Puff", bar: "Piffles"}
end
Run Code Online (Sandbox Code Playgroud)
然后像这样使用它:
iex(1)> Cats.names.foo
"Puff"
iex(2)> Cats.names.bar
"Piffles"
Run Code Online (Sandbox Code Playgroud)
或者同一功能的多个子句:
defmodule Cats do
def names(:foo), do: "Puff"
def names(:bar), do: "Piffles"
end
iex(1)> Cats.names(:foo)
"Puff"
iex(2)> Cats.names(:bar)
"Piffles"
Run Code Online (Sandbox Code Playgroud)