> So, in one sense, refinements are to localize monkey-patching. But they don't actually localize it much better since they can apply at a distance to blocks (module_eval feature), and classes down-hierarchy.
> Previously, all code determined what methods to call based solely on the target object's class hierarchy. Even with monkeypatches in place, we still have to look solely at the target class to determine what's being called.
> With refinements, every piece of code everywhere in the system could potentially see refinements active whether there's a "using" clause near them or not. Blocks could be forced to call different methods at any time, normal code could see a superclass add a refinement and change all future calls. Refinements may prevent monkeypatches from affecting the entire runtime, but don't make it any easier to determine what methods will actually be called.

I think I might have a solution to this:

1. refinements should only go through the local module hierarchy, not 
the class hierarchy because all contributors to a module namespace 
should be familiar with the conventions and refinements used within that 
module.

If for example ActiveRecord wants to add .constantize their Strings and 
use that feature throughout their project it should be fine, as they 
should be putting everything under the ::ActiveRecord module anyway.

If someone has his own rails application he can use the refinement again 
in his own application module or - if he isn't using any module - apply 
it to all of his classes.

2. instance_eval/module_eval/class_eval should NOT apply the refinements 
of the target. Instead they should use the same refinements as the 
context they were defined in.

To still allow for fancy DSLs there should be a way to explicitly rebind 
the context of the Proc to a different refinement context. Basically 
bind its lookup to a different module.

A Proc passed to the DSL would first have to be re-bound to the DSL's 
own module and then eval'd. As long as nobody else rebinds the Proc 
again this shouldn't invalidate any cache as the Proc was never called 
in its old context before. What is even better is that someone else 
could extend the DSL under a different module but the DSL's extensions 
would still get applied to the block, as expected by the caller.



3. there should also be an way to remove refinements from a module and 
all its submodules. This makes it possible to apply a refinement to 
::Object and then prune it out of some foreign namespaces where it turns 
out to cause trouble.

Consider it "optimistic refining".



1 and 2 mean that every Callsite and Proc can only have one refinement 
context at any given time.
Rule 3 makes monkeypatching safer as we can still apply it globally as 
we already do and simply undo it in places where the monkey bites us.

While this may seem less flexible you also have to consider that these 
single-scope refinements can be used together with Module.prepend

This allows you to move an aspect of code to a different module inside 
your application, apply refinements only to that module (and its 
submodules), write your refined code inside that module and then prepend 
that module with the refined behavior to the target class which you do 
not want to "pollute" with the refinements.

>
> They also don't solve the monkeypatching problem in any way. Monkeypatches have been used for a few reasons:
>
> * Adding methods to existing types, for DSL or fluent API purposes.
>
> Refinements would limit the visibility of those methods, somewhat, but you can't tell without digging around both the target class hierarchy and the calling class hierarchy what methods will really be called.
>
> * Replacing existing methods with "better" versions
>
> Refinements would again limit the visibility of those changes, but ultimately result in some code calling one method and some code calling another, with no easy way to determine the code that will be called ahead of time.
>
> It may be possible to address the technical issues of optimizing call sites with and without refinements, but I really don't feel like refinements are solving as many problems as they're going to create. I lament a future where I can't look at a piece of code and determine the methods it's calling solely based on the types it is calling against. It's going to be harder -- not easier -- to reason about code with refinements in play.