ruby在保持原始类型签名的同时动态更改方法

时间:2016-02-22 22:20:48

标签: ruby dynamic metaprogramming

我的目标是使用现有函数foo,并创建一个名为bar的精确副本,这对alias_method来说非常简单。然后,我想动态重新定义foo,使其具有完全相同的类型签名,以便我可以从中调用bar,以及其他原因。

这个要求意味着我不能只做像

这样的事情
define_method(:foo) do |*args, &block|
    send(:bar, *args, &block)
end

因为它更改了foo的类型签名。

我也没有看到我如何使用类似method(:foo).parameters的内容,因为它会告诉我类型签名是什么,但不会指定默认参数的值。

任何帮助都非常有用!

2 个答案:

答案 0 :(得分:2)

Ruby没有清单类型和清单类型签名的概念。由于它们不存在,你显然无法得到它们。

在进行Ruby编程时, 当然是程序员头脑中潜在类型和类型签名的概念。但这恰好存在于这个概念的位置:在程序员的头脑中,而在程序员的头脑中。

可能也存在于文档中,但这并不保证。此外,没有标准格式将其放入文档中。在Ruby文档中有各种不同的格式来表达类型,有时类型不是使用任何形式的(半)正式表示法表达,而只是在散文中表达,有时,它们隐含在参数名称中。在某些情况下,类型只是Ruby文化的一部分,每个人都知道它们,但它们实际上从未在任何地方写下来(最明显的例子是each mixin所依赖的Enumerable协议,每个人都只知道"没有明确指定)。

您还询问可选参数的默认参数:这些参数是动态评估的,因为停止问题,莱斯定理以及编程中所有其他有趣的不可判定性结果,因此无法获取有关它们的静态信息。< / p>

答案 1 :(得分:0)

TL; DR

Ruby不关心类型。它只关心一个对象是否可以#respond_to?一条消息。因此,任何有关类型的硬连接期望都应该以方法/变量名称和文档编码。

使用Duck-Typing来包装方法

“Ruby方式”是使用duck-typing而不是严格的“类型签名”。虽然包装方法没有任何问题,但您的方法应该:

  1. 如果参数的类型很重要,请使用有意义的参数名称。
  2. 在您需要一个对象#respond_to?当前不支持的方法的情况下,执行隐式或显式强制或定义单例方法。
  3. 必要时实施单例方法以允许进行鸭子输入。
  4. 例如:

    def foo array
      array.is_a? Array
    end
    
    def flexible_foo string_or_array
      if string_or_array.respond_to? :split
        array = string_or_array.split /,?\s+/
      else
        array = string_or_array
      end
      foo array
    end
    
    flexible_foo 'a, b, c'
    #=> true
    
    flexible_foo %w[a b c]
    #=> true
    

    在这个例子中,#foo需要一个数组。通过包装#foo,我们创建了一个类似于工作的方法,如果它响应:split消息,则会将值强制转换为数组:String和Array不响应。

    文档

    RDoc和YARD都可以“开箱即用”地记录方法签名,但YARD也有support for using tags来记录“类型签名”和返回类型等内容。

    如果您的代码是针对什么类型的对象可以作为参数传递的固定期望而编写的,那么您可以在RDoc或YARD将尽职尽责地报告的注释中记录这些期望。但是,这被认为是程序员的责任而不是Ruby解释器,并且当Ruby在运行时引发NoMethodError exception时你就会知道你是否违反了隐式契约。

    这是Ruby社区采用测试驱动开发的一个原因:由于Ruby可以动态地重新定义方法和类,因此解释器直到运行时才会知道调用方法是否发送了无效消息。这通常被视为GoodThing®,但您的里程和意见可能会有所不同。