匿名模块可以用于什么目的?

时间:2022-03-19 12:38:32

What purpose could anonymous modules in a Ruby app serve? The concept itself is easy to grasp, but I can't imagine any reason that you'd ever use such a thing. What problem do they solve?

Ruby应用程序中的匿名模块可以用于什么目的?概念本身很容易掌握,但我无法想象你曾经使用过这样的东西的任何理由。他们解决了什么问题?

2 个解决方案

#1


5  

There is a more general principle at work here.

这里有一个更普遍的原则。

Phil Karlton famously said: "There are only two hard problems computer science: cache invalidation and naming things." So, naming things is hard. Which means that if we can get away with not naming a thing, we should do it!

Phil Karlton着名地说:“计算机科学只有两个难题:缓存失效和命名事物。”因此,命名事情很难。这意味着,如果我们能够在不命名的情况下逃脱,我们应该这样做!

Or, if you look at it from a different perspective: if naming things is hard, then giving something a name means that thing is important. But sometimes, there are things in our programs which aren't important and thus aren't worthy of a name.

或者,如果从不同的角度来看待它:如果命名很难,那么给某个名称意味着事情很重要。但有时候,我们的程序中有些东西并不重要,因而不值得一个名字。

This is not unique to Ruby modules. You could ask the question about any anonymous concept, and in fact, the question does get asked all the time. When C# 2.0 introduced anonymous methods, people asked why one would ever want to use a method without a name, when C# 3.0 introduced anonymous lambdas (and anonymous types), people asked why one would ever want to use them. Python's anonymous functions are severely restricted compared to Python's named functions, and the Python community asks why one would ever need full-blown anonymous functions. Of course, we, as Ruby programmers are so used to lightweight (blocks) and fully reified (Procs) anonymous functions that we can't understand why one would ever not want to use one.

这不是Ruby模块独有的。您可以询问有关任何匿名概念的问题,事实上,问题会一直被问到。当C#2.0引入匿名方法时,人们会问为什么人们会想要使用没有名称的方法,当C#3.0引入匿名lambdas(和匿名类型)时,人们会问为什么人们会想要使用它们。与Python的命名函数相比,Python的匿名函数受到严格限制,而Python社区则询问为什么人们需要完整的匿名函数。当然,我们,因为Ruby程序员习惯于轻量级(块)和完全具体化(Procs)匿名函数,我们无法理解为什么人们不想使用它。

Java has anonymous classes since 1.1 and anonymous lambdas since 8. Basically, anonymous "things" are everywhere and they are useful, especially for quick one-off usage.

Java从1.1开始有匿名类,自8开始有匿名lambda。基本上,匿名“东西”无处不在,它们很有用,尤其适用于快速一次性使用。

For example, if you just want to wrap some existing method, without going through the hassle of alias_method (which you really shouldn't use any more for that problem, Module#prepend now exists and is a much better solution), you could do:

例如,如果你只想包装一些现有的方法,而不是经历alias_method的麻烦(你真的不应该再使用它来解决那个问题,现在模块#prepend已经存在并且是一个更好的解决方案),你可以做:

class Array
  prepend(Module.new do
    def [](*)
      puts 'Before-hook'
      super.tap { puts 'After-hook' }
    end
  end)
end

p [42][0]
# Before-hook
# After-hook
# => 42

#2


5  

This is a Rails specific answer, it's not about anonymous modules in general.

这是一个Rails特定的答案,它不是一般的匿名模块。

Short answer

Being able to call super when overriding generated methods.

在覆盖生成的方法时能够调用super。

Long answer

Given a module that creates methods:

给定一个创建方法的模块:

module Generator
  def generate_method(name)
    define_method(name) do
      "I am #{name}"
    end
  end
end

Calling generate_method from within a class creates a new instance method:

从类中调用generate_method会创建一个新的实例方法:

class MyClass
  extend Generator
  generate_method :foo
end

MyClass.new.method(:foo) #=> #<Method: MyClass#foo>

Invoking the method works as expected:

调用该方法按预期工作:

MyClass.new.foo #=> "I am foo"

But you can't easily alter foo:

但你不能轻易改变foo:

class MyClass
  def foo
    super.upcase
  end
end

MyClass.new.foo #=> no superclass method `foo'

If our generator uses an anonymous module to define the methods within:

如果我们的生成器使用匿名模块来定义以下方法:

module Generator
  def generate_method(name)
    generated_methods.module_eval do
      define_method(name) do
        "I am #{name}"
      end
    end
  end

  def generated_methods
    @generated_methods ||= begin
      mod = Module.new
      include(mod)
      mod
    end
  end
end

We get:

我们得到:

class MyClass
  extend Generator
  generate_method :foo
end

MyClass.new.method(:foo) #=> #<Method: MyClass(#<Module:0x007fbd29833658>)#foo>

And altering foo now works as expected:

并且改变foo现在按预期工作:

class MyClass
  def foo
    super.upcase
  end
end

MyClass.new.foo #=> "I AM FOO"

#1


5  

There is a more general principle at work here.

这里有一个更普遍的原则。

Phil Karlton famously said: "There are only two hard problems computer science: cache invalidation and naming things." So, naming things is hard. Which means that if we can get away with not naming a thing, we should do it!

Phil Karlton着名地说:“计算机科学只有两个难题:缓存失效和命名事物。”因此,命名事情很难。这意味着,如果我们能够在不命名的情况下逃脱,我们应该这样做!

Or, if you look at it from a different perspective: if naming things is hard, then giving something a name means that thing is important. But sometimes, there are things in our programs which aren't important and thus aren't worthy of a name.

或者,如果从不同的角度来看待它:如果命名很难,那么给某个名称意味着事情很重要。但有时候,我们的程序中有些东西并不重要,因而不值得一个名字。

This is not unique to Ruby modules. You could ask the question about any anonymous concept, and in fact, the question does get asked all the time. When C# 2.0 introduced anonymous methods, people asked why one would ever want to use a method without a name, when C# 3.0 introduced anonymous lambdas (and anonymous types), people asked why one would ever want to use them. Python's anonymous functions are severely restricted compared to Python's named functions, and the Python community asks why one would ever need full-blown anonymous functions. Of course, we, as Ruby programmers are so used to lightweight (blocks) and fully reified (Procs) anonymous functions that we can't understand why one would ever not want to use one.

这不是Ruby模块独有的。您可以询问有关任何匿名概念的问题,事实上,问题会一直被问到。当C#2.0引入匿名方法时,人们会问为什么人们会想要使用没有名称的方法,当C#3.0引入匿名lambdas(和匿名类型)时,人们会问为什么人们会想要使用它们。与Python的命名函数相比,Python的匿名函数受到严格限制,而Python社区则询问为什么人们需要完整的匿名函数。当然,我们,因为Ruby程序员习惯于轻量级(块)和完全具体化(Procs)匿名函数,我们无法理解为什么人们不想使用它。

Java has anonymous classes since 1.1 and anonymous lambdas since 8. Basically, anonymous "things" are everywhere and they are useful, especially for quick one-off usage.

Java从1.1开始有匿名类,自8开始有匿名lambda。基本上,匿名“东西”无处不在,它们很有用,尤其适用于快速一次性使用。

For example, if you just want to wrap some existing method, without going through the hassle of alias_method (which you really shouldn't use any more for that problem, Module#prepend now exists and is a much better solution), you could do:

例如,如果你只想包装一些现有的方法,而不是经历alias_method的麻烦(你真的不应该再使用它来解决那个问题,现在模块#prepend已经存在并且是一个更好的解决方案),你可以做:

class Array
  prepend(Module.new do
    def [](*)
      puts 'Before-hook'
      super.tap { puts 'After-hook' }
    end
  end)
end

p [42][0]
# Before-hook
# After-hook
# => 42

#2


5  

This is a Rails specific answer, it's not about anonymous modules in general.

这是一个Rails特定的答案,它不是一般的匿名模块。

Short answer

Being able to call super when overriding generated methods.

在覆盖生成的方法时能够调用super。

Long answer

Given a module that creates methods:

给定一个创建方法的模块:

module Generator
  def generate_method(name)
    define_method(name) do
      "I am #{name}"
    end
  end
end

Calling generate_method from within a class creates a new instance method:

从类中调用generate_method会创建一个新的实例方法:

class MyClass
  extend Generator
  generate_method :foo
end

MyClass.new.method(:foo) #=> #<Method: MyClass#foo>

Invoking the method works as expected:

调用该方法按预期工作:

MyClass.new.foo #=> "I am foo"

But you can't easily alter foo:

但你不能轻易改变foo:

class MyClass
  def foo
    super.upcase
  end
end

MyClass.new.foo #=> no superclass method `foo'

If our generator uses an anonymous module to define the methods within:

如果我们的生成器使用匿名模块来定义以下方法:

module Generator
  def generate_method(name)
    generated_methods.module_eval do
      define_method(name) do
        "I am #{name}"
      end
    end
  end

  def generated_methods
    @generated_methods ||= begin
      mod = Module.new
      include(mod)
      mod
    end
  end
end

We get:

我们得到:

class MyClass
  extend Generator
  generate_method :foo
end

MyClass.new.method(:foo) #=> #<Method: MyClass(#<Module:0x007fbd29833658>)#foo>

And altering foo now works as expected:

并且改变foo现在按预期工作:

class MyClass
  def foo
    super.upcase
  end
end

MyClass.new.foo #=> "I AM FOO"