【问题标题】:Elixir: Modifying value of module attributeElixir:修改模块属性的值
【发布时间】:2015-08-06 18:10:09
【问题描述】:

是否有可能实现以下行为,即尝试更改模块属性的值以改变模块方法的行为?

defmodule Adder do
  @num_to_add 10
  def addTo(input), do: input + @num_to_add
end

IO.inspect Adder.addTo(5)  # Prints 15

Adder.num_to_add = 20

IO.inspect Adder.addTo(5)  # Expect it to print 25

它抛出以下错误

** (CompileError) hello.exs:8: cannot invoke remote function Adder.num_to_add/0 inside match
    (elixir) src/elixir_clauses.erl:26: :elixir_clauses.match/3

如果这是不可能的(因为 Elixir 中的所有内容都应该是不可变的),是否有任何 Elixir 方法可以实现类似的行为。

【问题讨论】:

  • 我不确定这里的用例,但模块属性更类似于常量而不是类成员。将模块属性视为模块级常量是明智的。
  • 谢谢@OnorioCatenacci - 我没有任何具体的用例,只是尝试实验

标签: elixir


【解决方案1】:

这是不可能的,因为属性只存在于该特定模块的编译之前。编译模块时,所有属性都被内联并被遗忘,因此此时您可以从该模块调用函数,不再可能修改属性。

这段代码应该更清楚地显示这一点:

defmodule Test do
  @attr 1
  @attr 2

  def attr do
    @attr
  end
end

IO.inspect Test.attr # => 2
Module.put_attribute(Test, :attr, 3)
IO.inspect Test.attr # => ** (ArgumentError) could not call put_attribute on module Test because it was already compiled

请注意,您可以在模块尚未编译时更改属性的值(例如在模块的主体中​​),只需再次设置它,就像我在这里将 @attr 设置为 2 时所做的那样。

顺便说一句,您似乎想要实现的目标可以通过Agent 轻松完成:

defmodule Storage do
  def start_link do
    Agent.start_link(fn -> 10 end, name: __MODULE__)
  end

  def add_to(input) do
    Agent.get_and_update(__MODULE__, fn (x) -> {x + input, x + input} end)
  end
end

Storage.start_link
IO.inspect Storage.add_to(5) # => 15
IO.inspect Storage.add_to(5) # => 20

Elixir 中的一个很好的经验法则是,每当您需要跟踪某些可变状态时,您都需要有一个封装该状态的进程。

【讨论】:

    【解决方案2】:

    在 Elixir 中,模块并不像面向对象编程语言中的对象/类那样存储状态。模块属性更像是一个常量,一旦模块被编译,它的值就不能改变,除非通过函数公开,否则它不能被外部访问。

    Pawel 提供了一些关于如何实现类似行为的不错选择。这是另一个更简单的方法,因为您不需要在另一个进程中存储状态。

    defmodule Adder do
      @num_to_add 10
    
      def addTo(input, num_to_add \\ @num_to_add), 
        do: input + num_to_add
    end
    

    在上述方法中,我们只是将模块属性设置为默认值。如果我们想覆盖它,只需提供第二个参数。

    【讨论】:

      猜你喜欢
      • 2017-10-31
      • 1970-01-01
      • 1970-01-01
      • 2016-06-24
      • 2016-09-24
      • 1970-01-01
      • 1970-01-01
      • 2016-07-01
      • 1970-01-01
      相关资源
      最近更新 更多