Как работает оператор Ruby.()?
Недавно я встретил некоторый код, используя вызов метода, состоящий из формата object.(arg1, arg2)
, не видя хорошего объяснения того, как он работает. См. Этот пример кода:
class TestServiceObject
def call
'method'
end
end
TestServiceObject.new.()
# => 'method'
Какой термин для такого рода сокращений?
Ответы
Ответ 1
Обозначение в виде точечных скобок является сокращенным способом передачи аргументов в неявный метод call
объекта Ruby:
foo = lambda {|bar| puts bar}
foo.call('baz')
#=> baz
foo.('baz')
foo.call('baz') === foo.('baz')
#=> true
Также обратите внимание, что следующие обозначения также являются действительными (и эквивалентными) вызовами метода call
:
foo['baz']
#=> baz
foo::('baz')
#=> baz
В вашем примере вы явно переопределяете метод call
в классе TestServiceObject
, чтобы он возвращал строку 'method'
при вызове. Соответственно, вы можете явно переопределить метод call
для принятия аргументов:
class TestServiceObject
def call(foo=nil)
foo || 'method'
end
end
TestServiceObject.new.()
#=> method
TestServicesObject.new.('bar')
#=> bar
UPDATE
Как отмечает комментатор @LoganSerman, сокращенный оператор работает на все, что отвечает на call
, что частично подтверждается следующим примером:
m = 12.method("+")
m.call(3)
#=> 15
m.(3)
#=> 15
ОБНОВЛЕНИЕ 2:
Как комментатор @Stefan также указывает на документацию на Proc#call
prc.() вызывает prc.call() с указанными параметрами. Его синтаксический сахар, чтобы скрыть "вызов".
Ответ 2
obj.(args)
- это просто функция, предоставляемая через синтаксический анализатор. Технически не псевдоним, но он имеет тот же эффект, что и вызов obj.call(args)
объекта, который определяет метод call
.
Ответ 3
foo.(bar, baz)
интерпретируется как
foo.call(bar, baz)
как
foo + bar
интерпретируется как
foo.+(bar)
или
foo[bar, baz] = quux
интерпретируется как
foo.[]=(bar, baz, quux)
Цель состоит в том, чтобы объекты, похожие на функции, похожи на вызывающие методы:
foo.(bar, baz) # function
foo(bar, baz) # method
Несмотря на утверждения в других ответах на этот вопрос, это не имеет никакого отношения к "неявному методу call
" (Ruby даже не имеет неявных методов, только Scala) или оператора индексирования.
Оператор индексирования преобразуется в другой вызов метода ([]
), а не в вызов call
:
o = Object.new
def o.call(*args); "`call` called with #{args.join(', ')}" end
o.(42)
# => "`call` called with 42"
o[42]
# NoMethodError: undefined method `[]' for #<Object:0xdeadbeefc0ffee>
def o.[](*args); "`[]` called with #{args.join(', ')}" end
o[42]
# => "`[]` called with 42"