最近发现 Ruby 在调用方法(lamdb,proc 等等可调用的对象)的书写上很不一致。 比如对于方法来说,
def met
end
直接写met 就是调用,你不想调用都不行,
而对于lamdba 则不能直接使用()调用很不统一, 使用l=[],或.call, .()
lm = lamdb{ ...}
lm() #error
lm # not call
lm.call #ok
lm[] #ok
lm.() #ok
这样无意对代码的一致性造成了很不好的印象。
比如我写一个高阶函数, 那我就不能统一写成 fn(),
而是写成fn.call 感觉有点非主流。
刚刚看了一些帖子,自问自答一波 在 ruby 中方法与其他 lamdab ,block, proc 很不同,方法可以接收 block,但是其他的都不可以。也就是说,在函数式语言中所说的 function-first-class 在 ruby 中不成立。所以写高阶函数时,可考虑用 lamdba 替代。
tt.call(aa, 'ook')
这样子就可以是函数first的了。 如果定义成方法则很不方便。
再说说设计的问题,ruby 的方法设计为自动调用,原因可以是收到 Lisp 的影响,设计者不希望以()显示的限制 ruby 的书写方法。因为很多时候强制使用()来表示调用语义会限制 DSL 的设计。但是这样以来,也会产生很多问题,这样的方式和大部分语言都不一致。lisp 特殊的 S 表达式,则没有这种问题。所以设计的一致性是很重要的。
方法调用,还有 lambda, proc 等,在 Ruby 里是不一样的东西。至于为什么这样设定,这是作者是这样想的。对我个人来说,没有什么好与不好。如果与 JS 等语言比,方法、函数、lamba 其实就是一个东西,确实是更一致,但是 JS 也有 JS 不一致的地方。
实际上,Ruby 因为有 block 的存在,很多时候并不像是 JS 一样,是函数传来传去的,它是传 block。以你的例子(我帮你格式化了,不知道是不是你本身的意图)
tt = ->(fn, arg) {
fn.call(fn.call(arg))
}
aa = ->(arg) {
arg << '!'
}
tt.call(aa, 'ok')
首先在 Ruby 里我们可能不太会去定义高阶函数,而会去使用一个私有方法。在我接受高阶函数的概念后,我也尝试在 JS, Scala 里用高阶函数来封装局部逻辑,但是事实证明是这带来了代码可维护性的降低。因为本身这种需要局部高阶函数的函数,逻辑就很复杂,这时候使用高阶函数,相对于另一个私有方法,本身就在增加复杂函数的长度。
如果我把你的 tt 当成是一个方法,而不是一个高阶函数,那可以这样写:
def tt(arg)
yield(yield(arg)) # 这里我不清楚你的意图,一般应该不会这样做,我只是复制你的逻辑
end
tt('ok', &block) # block是这个调用tt的方法的一个block参数
是不是相对你的版本好看一些?
JS 里的
var hide = function() {
// ...
}
本来在 Ruby 里,我们就会用一个方法来代替的,所以就基本不存在你说的连环a_lambda.call()
的调用。
可以这样认为,一开始设计成可以省略括号,结果后来使用的人多了发现带来的问题更多。 还不如抄袭 Smalltalk 彻底一点,连逗号都没有,比如: https://www.gnu.org/software/smalltalk/manual/html_node/Files.html#Files