在Elixir中,有什么方法可以使_real_常量?

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)

这样的代码可以被认为是惯用的吗?有什么原因我不应该这样做吗?

我是否可以将其他任何种类的实体声明为具有某种价值,而没有其他任何代码赋予该实体新价值的可能性?

Ale*_*kin 5

这样的代码可以被认为是惯用的吗?

没有。

有什么原因我不应该这样做吗?

一吨

首先,您不需要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 。


Ada*_*hip 2

我想你回答了你自己的问题。我的理解是,使用模块属性作为常量是标准做法,甚至在入门指南中推荐,但正如您所指出的,模块属性可以在编译时重新绑定。如果您确实想要保证“如果我使用标记实体,结果将始终相同”,那么也正如您所指出的,定义函数可以保证这一点。

尽管即使使用函数,也可以声明另一个返回不同值的子句:

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)