loading...

Reverted Ruby 2.7 new feature: Method reference operator

hanachin profile image Seiei Miyagi Updated on ・2 min read

2019/11/12 reverted

👋

https://github.com/ruby/ruby/commit/fb6a489af2765a3b56e301adf0019af6bbad6156
https://bugs.ruby-lang.org/issues/16275

Method reference operator

New operator .: was added.
https://bugs.ruby-lang.org/issues/12125
https://github.com/ruby/ruby/commit/67c574736912003c377218153f9d3b9c0c96a17b

It converts the method to Method object like method method.

42.:to_s
# => #<Method: Integer#to_s>

What is method method?

It converts the method to Method object.
https://docs.ruby-lang.org/en/trunk/Object.html#method-i-method

42.method(:to_s)
# => #<Method: Integer#to_s>

What is Method object?

Method object is a pair of receiver and the method, it's callable like a Proc.
https://docs.ruby-lang.org/en/trunk/Method.html

meth = 42.method(:to_s)
# => #<Method: Integer#to_s>
meth.receiver
# => 42
meth.name
# => :to_s
meth.call
# => "42"
meth.call(16)
# => "2a"

What is the differrence between Method reference operator .: and method method?

If method method is overwritten, .: still work.

class Object
  def method(*); end
end

42.method(:to_s)
# => nil
42.:to_s
# => #<Method: Integer#to_s>

Even if method method is refined, .: still work.

using Module.new {
  refine(Object) do
    def method(*); end
  end
}

42.method(:to_s)
# => nil
42.:to_s
# => #<Method: Integer#to_s>

Is it possible to convert refined method to Method?

No

using Module.new {
  refine(Object) do
    def foo(*); end
  end
}

42.:foo
# undefined method `foo' for class `Integer' (NameError)

How to convert the unary method to Method?

Add the suffix @ like method method argument.

42.method(:-@).call
# => -42
42.:-@.call
# => -42

Is it useful?

YES! It is useful for the API which takes the block, like methods in Enumerable.

[1,2,3].map { |n| 2 * n }
# => [2, 4, 6]
[1,2,3].map(&2.:*)
# => [2, 4, 6]


["NG"].any? { |word| "COPYING".include?(word) }
# => true
["NG"].any?(&"COPYING".:include?)
# => true

require "prime"

(1..10).select { |n| Prime.prime?(n) }
# => [2, 3, 5, 7]
(1..10).select(&Prime.:prime?)
# => [2, 3, 5, 7]

Conclusion

You can use new syntax Method reference operator .: now <3

$ rbenv install 2.7.0-dev
$ rbenv global 2.7.0-dev
$ ruby -e 'self.:puts.("Enjoy!")'
Enjoy!

Posted on Jan 5 '19 by:

Discussion

markdown guide
 
["NG"].any?(&"COPYING".:include?)

I really believe Ruby is moving in the wrong direction. This is not readable.

 

That's my example's problem.

Many other real world use-cases are on the issues.
bugs.ruby-lang.org/issues/12125
bugs.ruby-lang.org/issues/13581

It's handy in block argument for Enumerable methods like map, or select.

 

At first glance this seems a bit voodoo magic compared with the more explicit method, but the more I look at it, the more it does seem to be intuitive compared with other places : is used as a prefix in similar ways in Ruby.

Nice little upcoming addition.

 

: is used as a prefix in similar ways in Ruby.

Eh? : is never used as a prefix in Ruby. :: is—for namespacing in FQ names—but this is probably not as similar.

: as a prefix is used in Ruby only to declare symbols. If you think about [42].map(&:to_s) notation, there is no syntactic sugar. :to_s there is an old good plain symbol. It works because Symbol#to_proc is defined, no magic involved. & in arguments list calls to_proc on the argument and passes the result as a block to the calling method, e.g. symbol converted to a proc is passed to the receiver as a block parameter.

The naïve implementation of Symbol#to_proc in Ruby might be similar to the working implementation of the same for the string:

class String
  def to_proc
    ->(recv) { recv.send self }
  end
end

[42].map(&"to_s")
#⇒ ["42"]
 

The commit says the operator is introduced as an experimental feature. What does experimental mean in this case? Am I right in understanding that because 2.7 is only in preview, the operator is not guaranteed to make it to the final release?

 

My understanding is, experimental mean the behavior may change in future versions of Ruby. We can try experimental features and give a feedback to ruby-core team.
There is a release note about the refinements which was experimental feature in ruby 2.0.

However, please be aware that Refinements is still an experimental feature: we may change its specification in the future. Despite that, we would like you to play with it and give us your thoughts. Your feedback will help to forge this interesting feature.
ruby-lang.org/en/news/2013/02/24/r...

 

I don't like this syntax, at all.

It shaves off a few characters, but massively damages readability.

How is a new developer to make sense of this syntax? One thing ruby is really good at is "reading like English"; but this syntax appears more optimised for code golf than for human understanding.

 

In other programming languages, they have more convenient way to reference the method.

gist.github.com/americodls/20981b2...
bugs.ruby-lang.org/issues/13581

IMO, the difference between Ruby and other languages is method calling.
Other languages can reference the method by .method_name without (), but ruby can't because Ruby doesn't need () for method call.

The .: looks like method calling ., with additional : can reference the method. That makes sense to me.

 

With this syntactic sugar 'self.:puts.call("Enjoy!")' looks quite inconsistent :) Should be probably:

'self.:puts.("Enjoy!")'
 

Updated the code, Thanks for improvements!