Forum: Ruby-core [ruby-trunk - Feature #7486][Open] Cutting through the issues with Refinements

Posted by Thomas Sawyer (7rans)
on 2012-12-01 00:51
(Received via mailing list)
Issue #7486 has been reported by trans (Thomas Sawyer).

----------------------------------------
Feature #7486: Cutting through the issues with Refinements
https://bugs.ruby-lang.org/issues/7486

Author: trans (Thomas Sawyer)
Status: Open
Priority: Urgent
Assignee:
Category: core
Target version: 2.0.0


=begin
In issue #4085, there has been a long somewhat contentious discussion 
about Refinements. While it seems that everyone agrees they have merit, 
no one seems to have a concrete idea about how they should actually 
work. There are all sorts of complicated questions and edge case being 
flailed about. And though Matz is determined to stick to the feature 
freeze and include Refinements in Ruby 2.0, he has had to greatly peel 
back there capabilities and scope --it's quite a large change this close 
to release, and no one is still at all sure that this new limited design 
is right either. All this is rather unfortunate, not just because it 
means Ruby 2.0 is probably going to have a half-baked feature that is 
certain to change substantially by 2.1, but even more so because we were 
having pretty much the very same discussion six years ago!

In late 2003, there was a long discussion about method wrapping and 
aspect-oriented programming (AOP) on ruby-talk[1,2,3]. The conversation 
grew out of an early notion Matz (and maybe Jim Weirich?) had about 
wrapping methods for Rite. Remember Rite? That was the codename of the 
original Ruby 2.0. Back then Matz offered up the idea of using 
(({:pre})) and (({:post})) hooks to wrap methods. The notation was 
something like:

    class C
      def foo
        print "foo"
      end

      def foo:pre
        print "before"
      end

      def foo:post
        print "after"
      end
    end

    C.new.foo  #=> "beforefooafter"

Many of the same questions were asked about these method "hooks" that 
are now being asked about refinements --"do they stack"", "what happens 
if we remove the main method", "how are they applied to the object 
hierarchy?", and so on. While at first glance these yesteryear method 
hooks and today's refinements may seem quite different, they are 
actually quite related, which will become clear in a moment.

It was through these threads that Peter Vanbroekhoven and myself began 
an extensive conversation on AOP for Ruby, based originally on his idea 
of method wrapping via a module in much the same way as one uses 
include. He originally called the method that handled this simply 
"wrap". We know it today as (({prepend})). So you can thank Peter for 
that whole idea[4]. So, we were both very interested in the concept of 
AOP and with these early notions in mind we decided to take our 
conversation off-list with the hope of working out the ideal design for 
bringing AOP to Ruby. Truth is, we did even better than that.

Peter and I continued to discuss AOP over the following year trading 
hundreds of communiques exploring every nook and cranny of the concept. 
Indeed, at a certain point I think Peter was quite tired of it, as I had 
the tendency to review a concept again and again and again just to make 
sure we didn't miss anything. But as long and drawn out and as detailed 
as the whole process was, I think we were far the better for it. We 
developed a very good understanding of the whole matter. In the course 
of these conversations, I came upon the idea of the ((*transparent 
subclass*)). It was little more than a variation on Peters original wrap 
idea but it had all the hallmarks of a fundamental OOP concept. With 
further discussion we agreed that this was a solid corner stone upon 
which to lay AOP --and not just for Ruby, but for OOP in general! I gave 
it a name, the "Cut".

From there Peter and I toiled to write an RCR (Ruby Change Request) to 
introduce the concept to the Ruby community. (Yes, in those days there 
was such a thing.) We wrote and edited our RCR on the old RubyGarden.org 
wiki. After many dozens of revisions and over a year after our original 
discussion!, we finally had our proposal. To top it off Peter even put 
together a preliminary implementation patch for Ruby, as I put together 
a pure Ruby (and thus limited) demonstration library. You can find that 
code and the ((<RCR|http://rubyworks.github.com/cuts/rcr.html>)) today 
on ((<github|http://rubyworks.github.com/cuts>)).

Now all of this pre-story leads up to what I want to suggest now. I 
would like the Cuts RCR to be reconsidered[5], on the merits that it is 
precisely the well thought out, solid foundation, with a real OOP 
design, that can serve as the basis of implementation for both prepend 
and refinements, as well as all other aspect-oriented designs patterns 
developers wish it construct.

So how does this work? How can prepend and refinements be implemented 
via cuts?

By simple analogy, prepend is to include as cuts are to classes. What 
this means implementation-wise is that just as modules are included in 
the class hierarchy via proxy classes, modules would be prepended into 
the hierarchy via proxy cuts. Its a simple symmetry that provides the 
proper behavior.

For refinements we need only add a conditional proviso to cuts --a cut 
would only be applicable if the pertinent scope is `using` the 
refinement. The condition could even be customizable for other uses 
lending a great deal of flexibility, power and convenience in 
aspect-oriented designs. To make this idea clear, here is example code 
for how a cut can be used as a refinement:

    cut MyRefinement < String
      def self.apply?(binding)
        binding.using?(self)
      end
      def titlecase
        gsub(/\b\w/){ $`[-1,1] == "'" ? $& : $&.upcase }
      end
    end

This is just a normal cut as described in the RCR, but we have added the 
idea of an ((*applicable callback*)) --a condition that determines if 
the cut is used or skipped-over in the method chain. What 
`binding.using?` checks exactly is up to Matz. Most recently Matz has 
said that the scope should be per-file, but it can just as easily be at 
a lower level, say pre-module, and it all works --because, cuts 
themselves have a well defined behavior.

There would be no more confusion about how refinements are supposed to 
work. Cuts provide the well-defined answer.

[1] 
((<URL:http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...))
[2] 
((<URL:http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...))
[3] 
((<URL:http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...))
[4] Not Yahuda Katz, who Matz has erroneously credited is his recent 
keynote speeches.
[5] Excluding the idea of the aspect given at the end of the RCR, that 
can be done via a 3rd party gem.
=end
Posted by Thomas Sawyer (7rans)
on 2012-12-01 00:59
(Received via mailing list)
Issue #7486 has been updated by trans (Thomas Sawyer).


Please disregard this issue. Something went wrong with the RD syntax, so 
I just created a new issue b/c I saw no way to edit, and thus fix this 
issue's text.

----------------------------------------
Feature #7486: Cutting through the issues with Refinements
https://bugs.ruby-lang.org/issues/7486#change-34255

Author: trans (Thomas Sawyer)
Status: Open
Priority: Urgent
Assignee:
Category: core
Target version: 2.0.0


=begin
In issue #4085, there has been a long somewhat contentious discussion 
about Refinements. While it seems that everyone agrees they have merit, 
no one seems to have a concrete idea about how they should actually 
work. There are all sorts of complicated questions and edge case being 
flailed about. And though Matz is determined to stick to the feature 
freeze and include Refinements in Ruby 2.0, he has had to greatly peel 
back there capabilities and scope --it's quite a large change this close 
to release, and no one is still at all sure that this new limited design 
is right either. All this is rather unfortunate, not just because it 
means Ruby 2.0 is probably going to have a half-baked feature that is 
certain to change substantially by 2.1, but even more so because we were 
having pretty much the very same discussion six years ago!

In late 2003, there was a long discussion about method wrapping and 
aspect-oriented programming (AOP) on ruby-talk[1,2,3]. The conversation 
grew out of an early notion Matz (and maybe Jim Weirich?) had about 
wrapping methods for Rite. Remember Rite? That was the codename of the 
original Ruby 2.0. Back then Matz offered up the idea of using 
(({:pre})) and (({:post})) hooks to wrap methods. The notation was 
something like:

    class C
      def foo
        print "foo"
      end

      def foo:pre
        print "before"
      end

      def foo:post
        print "after"
      end
    end

    C.new.foo  #=> "beforefooafter"

Many of the same questions were asked about these method "hooks" that 
are now being asked about refinements --"do they stack"", "what happens 
if we remove the main method", "how are they applied to the object 
hierarchy?", and so on. While at first glance these yesteryear method 
hooks and today's refinements may seem quite different, they are 
actually quite related, which will become clear in a moment.

It was through these threads that Peter Vanbroekhoven and myself began 
an extensive conversation on AOP for Ruby, based originally on his idea 
of method wrapping via a module in much the same way as one uses 
include. He originally called the method that handled this simply 
"wrap". We know it today as (({prepend})). So you can thank Peter for 
that whole idea[4]. So, we were both very interested in the concept of 
AOP and with these early notions in mind we decided to take our 
conversation off-list with the hope of working out the ideal design for 
bringing AOP to Ruby. Truth is, we did even better than that.

Peter and I continued to discuss AOP over the following year trading 
hundreds of communiques exploring every nook and cranny of the concept. 
Indeed, at a certain point I think Peter was quite tired of it, as I had 
the tendency to review a concept again and again and again just to make 
sure we didn't miss anything. But as long and drawn out and as detailed 
as the whole process was, I think we were far the better for it. We 
developed a very good understanding of the whole matter. In the course 
of these conversations, I came upon the idea of the ((*transparent 
subclass*)). It was little more than a variation on Peters original wrap 
idea but it had all the hallmarks of a fundamental OOP concept. With 
further discussion we agreed that this was a solid corner stone upon 
which to lay AOP --and not just for Ruby, but for OOP in general! I gave 
it a name, the "Cut".

From there Peter and I toiled to write an RCR (Ruby Change Request) to 
introduce the concept to the Ruby community. (Yes, in those days there 
was such a thing.) We wrote and edited our RCR on the old RubyGarden.org 
wiki. After many dozens of revisions and over a year after our original 
discussion!, we finally had our proposal. To top it off Peter even put 
together a preliminary implementation patch for Ruby, as I put together 
a pure Ruby (and thus limited) demonstration library. You can find that 
code and the ((<RCR|http://rubyworks.github.com/cuts/rcr.html>)) today 
on ((<github|http://rubyworks.github.com/cuts>)).

Now all of this pre-story leads up to what I want to suggest now. I 
would like the Cuts RCR to be reconsidered[5], on the merits that it is 
precisely the well thought out, solid foundation, with a real OOP 
design, that can serve as the basis of implementation for both prepend 
and refinements, as well as all other aspect-oriented designs patterns 
developers wish it construct.

So how does this work? How can prepend and refinements be implemented 
via cuts?

By simple analogy, prepend is to include as cuts are to classes. What 
this means implementation-wise is that just as modules are included in 
the class hierarchy via proxy classes, modules would be prepended into 
the hierarchy via proxy cuts. Its a simple symmetry that provides the 
proper behavior.

For refinements we need only add a conditional proviso to cuts --a cut 
would only be applicable if the pertinent scope is `using` the 
refinement. The condition could even be customizable for other uses 
lending a great deal of flexibility, power and convenience in 
aspect-oriented designs. To make this idea clear, here is example code 
for how a cut can be used as a refinement:

    cut MyRefinement < String
      def self.apply?(binding)
        binding.using?(self)
      end
      def titlecase
        gsub(/\b\w/){ $`[-1,1] == "'" ? $& : $&.upcase }
      end
    end

This is just a normal cut as described in the RCR, but we have added the 
idea of an ((*applicable callback*)) --a condition that determines if 
the cut is used or skipped-over in the method chain. What 
`binding.using?` checks exactly is up to Matz. Most recently Matz has 
said that the scope should be per-file, but it can just as easily be at 
a lower level, say pre-module, and it all works --because, cuts 
themselves have a well defined behavior.

There would be no more confusion about how refinements are supposed to 
work. Cuts provide the well-defined answer.

[1] 
((<URL:http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...))
[2] 
((<URL:http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...))
[3] 
((<URL:http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...))
[4] Not Yahuda Katz, who Matz has erroneously credited is his recent 
keynote speeches.
[5] Excluding the idea of the aspect given at the end of the RCR, that 
can be done via a 3rd party gem.
=end
Posted by Vít Ondruch (vo_x)
on 2012-12-01 18:19
(Received via mailing list)
Issue #7486 has been updated by vo.x (Vit Ondruch).

Status changed from Open to Rejected


----------------------------------------
Feature #7486: Cutting through the issues with Refinements
https://bugs.ruby-lang.org/issues/7486#change-34289

Author: trans (Thomas Sawyer)
Status: Rejected
Priority: Urgent
Assignee:
Category: core
Target version: 2.0.0


=begin
In issue #4085, there has been a long somewhat contentious discussion 
about Refinements. While it seems that everyone agrees they have merit, 
no one seems to have a concrete idea about how they should actually 
work. There are all sorts of complicated questions and edge case being 
flailed about. And though Matz is determined to stick to the feature 
freeze and include Refinements in Ruby 2.0, he has had to greatly peel 
back there capabilities and scope --it's quite a large change this close 
to release, and no one is still at all sure that this new limited design 
is right either. All this is rather unfortunate, not just because it 
means Ruby 2.0 is probably going to have a half-baked feature that is 
certain to change substantially by 2.1, but even more so because we were 
having pretty much the very same discussion six years ago!

In late 2003, there was a long discussion about method wrapping and 
aspect-oriented programming (AOP) on ruby-talk[1,2,3]. The conversation 
grew out of an early notion Matz (and maybe Jim Weirich?) had about 
wrapping methods for Rite. Remember Rite? That was the codename of the 
original Ruby 2.0. Back then Matz offered up the idea of using 
(({:pre})) and (({:post})) hooks to wrap methods. The notation was 
something like:

    class C
      def foo
        print "foo"
      end

      def foo:pre
        print "before"
      end

      def foo:post
        print "after"
      end
    end

    C.new.foo  #=> "beforefooafter"

Many of the same questions were asked about these method "hooks" that 
are now being asked about refinements --"do they stack"", "what happens 
if we remove the main method", "how are they applied to the object 
hierarchy?", and so on. While at first glance these yesteryear method 
hooks and today's refinements may seem quite different, they are 
actually quite related, which will become clear in a moment.

It was through these threads that Peter Vanbroekhoven and myself began 
an extensive conversation on AOP for Ruby, based originally on his idea 
of method wrapping via a module in much the same way as one uses 
include. He originally called the method that handled this simply 
"wrap". We know it today as (({prepend})). So you can thank Peter for 
that whole idea[4]. So, we were both very interested in the concept of 
AOP and with these early notions in mind we decided to take our 
conversation off-list with the hope of working out the ideal design for 
bringing AOP to Ruby. Truth is, we did even better than that.

Peter and I continued to discuss AOP over the following year trading 
hundreds of communiques exploring every nook and cranny of the concept. 
Indeed, at a certain point I think Peter was quite tired of it, as I had 
the tendency to review a concept again and again and again just to make 
sure we didn't miss anything. But as long and drawn out and as detailed 
as the whole process was, I think we were far the better for it. We 
developed a very good understanding of the whole matter. In the course 
of these conversations, I came upon the idea of the ((*transparent 
subclass*)). It was little more than a variation on Peters original wrap 
idea but it had all the hallmarks of a fundamental OOP concept. With 
further discussion we agreed that this was a solid corner stone upon 
which to lay AOP --and not just for Ruby, but for OOP in general! I gave 
it a name, the "Cut".

From there Peter and I toiled to write an RCR (Ruby Change Request) to 
introduce the concept to the Ruby community. (Yes, in those days there 
was such a thing.) We wrote and edited our RCR on the old RubyGarden.org 
wiki. After many dozens of revisions and over a year after our original 
discussion!, we finally had our proposal. To top it off Peter even put 
together a preliminary implementation patch for Ruby, as I put together 
a pure Ruby (and thus limited) demonstration library. You can find that 
code and the ((<RCR|http://rubyworks.github.com/cuts/rcr.html>)) today 
on ((<github|http://rubyworks.github.com/cuts>)).

Now all of this pre-story leads up to what I want to suggest now. I 
would like the Cuts RCR to be reconsidered[5], on the merits that it is 
precisely the well thought out, solid foundation, with a real OOP 
design, that can serve as the basis of implementation for both prepend 
and refinements, as well as all other aspect-oriented designs patterns 
developers wish it construct.

So how does this work? How can prepend and refinements be implemented 
via cuts?

By simple analogy, prepend is to include as cuts are to classes. What 
this means implementation-wise is that just as modules are included in 
the class hierarchy via proxy classes, modules would be prepended into 
the hierarchy via proxy cuts. Its a simple symmetry that provides the 
proper behavior.

For refinements we need only add a conditional proviso to cuts --a cut 
would only be applicable if the pertinent scope is `using` the 
refinement. The condition could even be customizable for other uses 
lending a great deal of flexibility, power and convenience in 
aspect-oriented designs. To make this idea clear, here is example code 
for how a cut can be used as a refinement:

    cut MyRefinement < String
      def self.apply?(binding)
        binding.using?(self)
      end
      def titlecase
        gsub(/\b\w/){ $`[-1,1] == "'" ? $& : $&.upcase }
      end
    end

This is just a normal cut as described in the RCR, but we have added the 
idea of an ((*applicable callback*)) --a condition that determines if 
the cut is used or skipped-over in the method chain. What 
`binding.using?` checks exactly is up to Matz. Most recently Matz has 
said that the scope should be per-file, but it can just as easily be at 
a lower level, say pre-module, and it all works --because, cuts 
themselves have a well defined behavior.

There would be no more confusion about how refinements are supposed to 
work. Cuts provide the well-defined answer.

[1] 
((<URL:http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...))
[2] 
((<URL:http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...))
[3] 
((<URL:http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...))
[4] Not Yahuda Katz, who Matz has erroneously credited is his recent 
keynote speeches.
[5] Excluding the idea of the aspect given at the end of the RCR, that 
can be done via a 3rd party gem.
=end
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.