DEV Community

loading...
Okinawa Ruby User Group

Ruby 2.6 new feature: refine `#to_proc`

Seiei Miyagi
こんにちは
・1 min read

Refinements take place at block passing. [Feature #14223]
https://github.com/ruby/ruby/blob/v2_6_0/NEWS

It's very useful when you want to use the literals as DSL for the block.
Following example refines Integer#to_proc and Array#to_proc to slice the values.

using Module.new {
  refine(Integer) do
    def to_proc
      -> o { o[self] }
    end
  end

  refine(Array) do
    def to_proc
      # Same as -> o { map(&o.method(:[]) }
      # https://dev.to/hanachin/ruby-27-new-feature-method-reference-operator-38l2
      -> o { map(&o.:[]) }
    end
  end
}

p "foo".match(/(a)(b)(c)/)&.then(&1) #=> nil
p "abc".match(/(a)(b)(c)/)&.then(&1) #=> "a"
p "abc".match(/(a)(b)(c)/)&.then(&[1]) #=> ["a"]
p "abc".match(/(a)(b)(c)/)&.then(&[1,2,3]) #=> ["a","b","c"]

Discussion (5)

Collapse
defman profile image
Sergey Kislyakov 🇷🇺 🇺🇸

&o.:[] the amount of sugar in these 6 symbols gave me diabetes.

Collapse
hanachin profile image
Seiei Miyagi Author

140% symbols😋🍭

  • map(&o.:[])
    • (
    • &
    • .
    • :
    • [
    • ]
    • )
  • map{|k|o[k]}
    • {
    • |
    • [
    • ]
    • }
Collapse
dhnaranjo profile image
Desmond Naranjo

I don't have a damn idea what I am looking at.

Collapse
defman profile image
Sergey Kislyakov 🇷🇺 🇺🇸

My guess is &1 is 1.to_proc() and o is the receiver object (e.g. a.map(&0) would do a.map( -> a[0])) and the sugar diabetes thingy is a.[].to_proc(). If I'm wrong please correct me @hanachin .

There's something similar in Kotlin though I like their implementation more.

Collapse
hanachin profile image
Seiei Miyagi Author

That's correct!

& would implicitly call #to_proc to convert the object to Proc object.

Any object that implements the to_proc method can be converted into a proc by the & operator, and therefore con be consumed by iterators.
docs.ruby-lang.org/en/2.6.0/Proc.h...

But before 2.6, #to_proc that defined in Refinements are ignored at & operator.

# foo.rb
using Module.new {
  refine(Integer) do
    def to_proc
      -> o { o[self] }
    end
  end
}

p 1.then(&0)
Enter fullscreen mode Exit fullscreen mode
% ruby -v foo.rb
ruby 2.5.3p105 (2018-10-18 revision 65156) [x86_64-darwin17]
Traceback (most recent call last):
foo.rb:9:in `<main>': wrong argument type Integer (expected Proc) (TypeError)
Enter fullscreen mode Exit fullscreen mode

In 2.6, we can use #to_proc for & operator.

% ruby -v foo.rb
ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17]
1
Enter fullscreen mode Exit fullscreen mode