Just out of curiosity: Wouldn't the problem with your examples be that
the String class is hard-wired in the ruby parser -- see eg
http://groups.google.com/group/comp.lang.ruby/browse_frm/thread/2a0ad2d4a78f499d/
I'm not sure though if this is still true for ruby 1.9.
Here's a hacked-together implementation for JRuby:
The idea is that at parse time it saves off the node associated with
whatever you're "use"ing and attaches it to future parsed calls. Then
there's a modification to calls to use that namespace first if it's present.
Caveats:
* HACKHACKHACK
* Only works for single-arg "call" in this case, but easy to generalize
to all other calls
* The namespace never gets turned off in this example. It wouldn't be
hard to add with parser help, but there's no trigger I can use in this
simple version.
* Interpreter-only. Compilation would be a bit trickier, but not much.
If I were going to implement this for real I'd probably have a special
"namespaced call site" that does the magic for all calls.
A fortunate side effect of this impl is that the "to_s" inside the
namespaced Merb::String actually goes to the real String, since "self"
is the 'crazy time' object below. This may or may not be desirable, but
it was easier than trying to make super work.
- Charlie
Another important one:
* This version searches the namespace for the method *unconditionally*.
It would probably be desirable to identify whether it applies to the
receiver or not:
use String => Merb::String
And then the namespace association only applies when the receiver is_a?
String.
- Charlie
That sounds a lot like the Objective-C method poseAs, discarding some
of it's limitations.
But what is the scope of use? AFAIK, there is no construct in Ruby
that has a file-only scope (except the ones that need to be closed
because of syntactical reasons). Granted, those are details.
But what about this example in Yehudas gist:
===
# require "my_app3", :use => Merb::String
"hello_goodbye".camel_case #=> "HelloGoodbye"
"hello_goodbye".camel_case(true) #=> ArgumentError
===
Making it externally controllable which class a file uses is at least
discussable. I don't like it, especially because it is not really
fitting in Rubys way of not seeing files as program units. Also, only
one parametrization can be in place at once, which smells like a
source of conflicts.
Regards,
Florian Gilcher
--
Florian Gilcher
smtp: f...@andersground.net
jabber: Sk...@jabber.ccc.de
gpg: 533148E2
I see the namespacing more like adding an extra layer of indirection to
calls so you can hook them without globally overriding them. So within
the context of a file you're saying "I want to have a go at all methods
called against String" and you can optionally then provide your own impls.
If it's the file scoping that seems odd, I don't see any reason why it
couldn't be explicitly scoped. Something like:
use String => Merb::String do
... and in here all calls to String methods can be replaced by Merb
end
It could be argued that the file scope logic is a little too unbounded,
certainly.
- Charlie
That sounds a lot like the Objective-C method poseAs, discarding some of it's limitations.
On Feb 19, 2009, at 8:15 AM, Charles Oliver Nutter wrote:
Charles Oliver Nutter wrote:
Caveats:
* HACKHACKHACK
* Only works for single-arg "call" in this case, but easy to generalize to all other calls
* The namespace never gets turned off in this example. It wouldn't be hard to add with parser help, but there's no trigger I can use in this simple version.
* Interpreter-only. Compilation would be a bit trickier, but not much.
Another important one:
* This version searches the namespace for the method *unconditionally*. It would probably be desirable to identify whether it applies to the receiver or not:
use String => Merb::String
And then the namespace association only applies when the receiver is_a? String.
- Charlie
But what is the scope of use? AFAIK, there is no construct in Ruby that has a file-only scope (except the ones that need to be closed because of syntactical reasons). Granted, those are details.
But what about this example in Yehudas gist:
===
# require "my_app3", :use => Merb::String"hello_goodbye".camel_case(true) #=> ArgumentError
"hello_goodbye".camel_case #=> "HelloGoodbye"
===
Making it externally controllable which class a file uses is at least discussable. I don't like it, especially because it is not really fitting in Rubys way of not seeing files as program units.
Also, only one parametrization can be in place at once, which smells like a source of conflicts.
Regards,
Florian Gilcher
--
Florian Gilcher
smtp: f...@andersground.net
jabber: Sk...@jabber.ccc.de
gpg: 533148E2
I suppose there's another argument in favor of an explicit bounding of
the "use" area: where do you *start*? If this is parse time, having it
start at the first "use" call seems a little odd; you'd almost want it
to be a whole-file pragma. But starting it at the beginning of the file
seems just as odd, since a use anywhere in the body changes all method
dispatches.
- Charlie
Also, only one parametrization can be in place at once, which smells like a source of conflicts.
"only one parametrization can be in place at once" -- I don't understand what you mean by this.
I'm trying to work out the details in my head. The main complication I
have is what gets done at parse time and what gets done at runtime.
Parse Time:
It seems like at least the "use" would have to set a global or
per-thread flag during parsing so that calls made under that scope would
be generated with extra logic for namespace checking.
Runtime:
The runtime bit is trickier. I think the node passed to use would have
to be saved off in the parsed calls so they could do that lookup.
But then there's the question of whether they should look it up each
time. That's a constant lookup hit for ever namespaced call. Looking it
up every time could also lead to some oddities if the method itself had
conflicting constants in its lookup hierarchy.
If instead the use itself were captured and used as the reference point
for the the lookup, it would be executed once in the script, immediately
cached, and all calls would then use the same value. Yes, I think that's
probably better.
So a NamespacedCallNode would aggregate a UseNode, with the UseNode
aggregating some other node structure for the argument and a body of
code; basically all calls would have a backreference to the 'use', and
the 'use' itself would represent the cache key and lookup reference point.
Threading:
I lean more toward having the parse of a "use" set a flag in the
executing parser instance on a single thread; there are possible
complications here though.
If one thread requires a file while in a "use", and at the same time
another thread requires the same file without a "use", we have a race.
If one thread requires a file and another file loads it, we have issues
as well (though not substantially different from issues in normal Ruby.
Of course the threading concerns are entirely parse-time; once parsed
every thread would see the namespacing, eh?
Mock-up:
As this settles we could probably mock it up pretty easily in JRuby. My
early attempt was without parser support; I could continue hacking it
that way, but having real parser help would make it easier. We shall
see...I'm curious what others think of the proposal.
- Charlie
+1
I like this much better than giving the use command its own block.
--
-- Jim Weirich
-- jim.w...@gmail.com
+1 as well, but I like the block. For me, it seems to be more clear as
to what is going on.
use Merb::String => String do
"bla blah".some_special_merb_only_method
end
"bla blah".some_special_merb_only_method # => doh! doesn't exist
--
Aaron Patterson
http://tenderlovemaking.com/
I'd hate for people to think it had closure semantics... Without a
block, it's analogous to the class, module or def keyword, which are
perfectly clear. I'm fine either way though.
Sent from my iPhone
On Feb 21, 2009, at 9:35 PM, Aaron Patterson
I am not sure I like this plan at all, really: it would be better
to implement through (or at least concurrently with) methodwise
selectors, but since this version is not dealing with an actual
block, it should follow the while/until pattern instead:
using Merb::String => String
whatever
end
--
Magic is insufficiently advanced technology.
> -----Original Message-----
> From: Eero Saynatkari [mailto:rub...@kittensoft.org]
> Sent: Sunday, February 22, 2009 7:56 AM
> To: ruby...@ruby-lang.org
> Subject: [ruby-core:22330] Re: YASNP (Yet Another Selector
> Namespace Proposal)
>
> Excerpts from Aaron Patterson's message of Sun Feb 22
> 04:35:41 +0200 2009:
> > On Sun, Feb 22, 2009 at 04:34:18AM +0900, Jim Weirich wrote:
> > > On Feb 20, 2009, at 8:39 AM, Yehuda Katz wrote:
> > >> - no special namespace keyword; any module can be a namespace
> > >
> > > +1
> > >
> > > I like this much better than giving the use command its own block.
> >
> > +1 as well, but I like the block. For me, it seems to be
> more clear
> > +as
> > to what is going on.
> >
> > use Merb::String => String do
> > "bla blah".some_special_merb_only_method
> > end
>
>
> I am not sure I like this plan at all, really: it would be
> better to implement through (or at least concurrently with)
> methodwise selectors, but since this version is not dealing
> with an actual block, it should follow the while/until
> pattern instead:
>
> using Merb::String => String
> whatever
> end
Put me in the "don't like" category as well.
Regards,
Dan
Can you explain *why* you don't like it?
Keep in mind that most people would never use this feature... it's mostly for frameworks to be able to make core extensions for their own use without polluting the global space.
-- Yehuda
It is difficult to see the extent of changes. I prefer the method-wise
namespace:
"foo".Merb:to_url
My quick take...
The good:
- Clearly demarcated syntax to express that you are changing
behavior. Much more visible than monkey patching.
- It is lexically defined. Your semantic change only affects the code
it is surrounding. The scope of the changes will not seep everywhere.
Though I suspect you may end up with a boilerplate at the top of every
file in your library.
The bad:
- This mostly just seems like monkey-patching defense to me. Maybe
you are fighting the symptom and not the problem by adding this? Not
sure I have much more to add to this...Redefining methods in Ruby is a
reality and a powerful tool. I think you are just trying to
counter-balance it so library authors can prevent users from changing
your libraries behavior.
Though you cannot really prevent users from doing anything right? They
can still modify Merb::String to do the things you were trying to
prevent in the first place. Maybe that is ok, but it still feels like
if someone wrecks the API you are consuming (String for example), then
they may feel the need to wreck your version of string also. Perhaps
then they deserve it....then again they deserved it the second they
ruined String.
I feel pretty ambivalent about this...
-Tom
Yehuda Katz wrote:Famous last words...One poignant blog entry can undo your statement in a heartbeat.
Can you explain *why* you don't like it?
Keep in mind that most people would never use this feature... it's mostly for frameworks to be able to make core extensions for their own use without polluting the global space.
My quick take...
The good:
- Clearly demarcated syntax to express that you are changing behavior. Much more visible than monkey patching.
- It is lexically defined. Your semantic change only affects the code it is surrounding. The scope of the changes will not seep everywhere. Though I suspect you may end up with a boilerplate at the top of every file in your library.
The bad:
- This mostly just seems like monkey-patching defense to me. Maybe you are fighting the symptom and not the problem by adding this? Not sure I have much more to add to this...Redefining methods in Ruby is a reality and a powerful tool. I think you are just trying to counter-balance it so library authors can prevent users from changing your libraries behavior.
Though you cannot really prevent users from doing anything right? They can still modify Merb::String to do the things you were trying to prevent in the first place. Maybe that is ok, but it still feels like if someone wrecks the API you are consuming (String for example), then they may feel the need to wreck your version of string also. Perhaps then they deserve it....then again they deserved it the second they ruined String.
I feel pretty ambivalent about this...
-Tom
I'm not saying it might not be useful, I just want a more fine grained
approach available to me:
http://www.sapphire-lang.org/wiki/sapphire/Selector_namespaces
Regards,
Dan
In message "Re: [ruby-core:22322] Re: YASNP (Yet Another Selector Namespace Proposal)"
on Sun, 22 Feb 2009 11:35:41 +0900, Aaron Patterson <aa...@tenderlovemaking.com> writes:
|+1 as well, but I like the block. For me, it seems to be more clear as
|to what is going on.
|
| use Merb::String => String do
| "bla blah".some_special_merb_only_method
| end
|
| "bla blah".some_special_merb_only_method # => doh! doesn't exist
+1 to have some kind of selector namespace (it's planned for 2.0
anyway), but -1 for using block (or block-like syntax) to denote
namespace scope, because
* it's not really a block
* too small granularity for namespace switching might bring confusion
I'd prefer per file namespace switching.
matz.
I think it's impossible to have per-file without making "use" a special
keyword, ideally at the top of the file. The block syntax made more
sense to me because I knew exactly when it started and ended, and a
'use' later in the file did not affect code that came before it. Also,
the example where I have both a namespace and a 'use' in the same file
would be impossible if the use applied to the whole file.
I'm willing to mock up anything else we want to try, including a
whole-file version.
- Charlie
--
those damn raccoons!
In general, I think selector namespaces are a bad idea, even if they
are planned for 2.0. :( Rather than trying to argue persuasively for
my opinion, I'll throw out a couple things to think about.
In implementing Rubinius, we have only encountered one major issue
with redefining a core library method: mathn changing Fixnum#/.
At least as important (if not more) as seeing what a language feature
makes easy is seeing what a language feature makes hard. What will it
look like in code when folks inevitable try to work around the fences
you erect with your selector namespaces? They will try to work around
them.
Engineering software by trying to protect against the invisible,
unknowable, purely speculative demons that lurk in some future world
leads to horrible languages and even worse software. One of the things
Ruby has going for it is some very good software that judiciously
changes assumptions made at the time other software/code was written.
I would urge everyone to attempt to truly understand what sort of Ruby
software will be created with selector namespaces.
Cheers,
Brian
This could certainly be simplified. I'm also leaning toward "using" and
making the body more like a class body, so it doesn't appear to be a
real block:
using Foo::A, Bar::B, Baz::C
some
code
end
- Charlie
In addition to overriding, selectors can be used for adding (and,
if implemented file-/block-wise, deleting.)
> At least as important (if not more) as seeing what a language feature
> makes easy is seeing what a language feature makes hard. What will it
> look like in code when folks inevitable try to work around the fences
> you erect with your selector namespaces? They will try to work around
> them.
>
> Engineering software by trying to protect against the invisible,
> unknowable, purely speculative demons that lurk in some future world
> leads to horrible languages and even worse software. One of the things
> Ruby has going for it is some very good software that judiciously
> changes assumptions made at the time other software/code was written.
I think the two paragraphs contrast a bit :)
However, to offer an alternative consideration, how about a real
mechanism for "unrequiring" and/or "unextending"? Particularly the
non-methodwise variants of selector namespaces could very well be
replaced by being able to (really) activate and deactivate files
or modules at runtime.
Excerpts from brixen's message of Wed Feb 25 00:04:34 +0200 2009:
> In implementing Rubinius, we have only encountered one major issueIn addition to overriding, selectors can be used for adding (and,
> with redefining a core library method: mathn changing Fixnum#/.
if implemented file-/block-wise, deleting.)
> At least as important (if not more) as seeing what a language feature> makes easy is seeing what a language feature makes hard. What will it
> look like in code when folks inevitable try to work around the fences
> you erect with your selector namespaces? They will try to work around
> them.
> Engineering software by trying to protect against the invisible,
> unknowable, purely speculative demons that lurk in some future world
> leads to horrible languages and even worse software. One of the things
> Ruby has going for it is some very good software that judiciously
> changes assumptions made at the time other software/code was written.
I think the two paragraphs contrast a bit :)
However, to offer an alternative consideration, how about a real
mechanism for "unrequiring" and/or "unextending"? Particularly the
non-methodwise variants of selector namespaces could very well be
replaced by being able to (really) activate and deactivate files
or modules at runtime.
--Magic is insufficiently advanced technology.
Unrequiring and unextending make global changes. I think the selector
namespace ideas being bandied about are a nice way to hook calls within
a given context without invading core classes or making global changes
that could break other people's code. In truth, I think good selector
namespace could actually reduce the need for and danger of
monkeypatching, since you can make safe localized changes.
If I were to mock up a proposal, which one should I go with first?
Someone propose me a spec.
- Charlie
Here's a new mock-up of this behavior in JRuby (without making parser
modifications that would certainly clean it up more):
{{
module Foo
module Bar
def to_s
puts 'inside namespace: ' + to_s
'blah'
end
end
end
using Foo::Bar => String
puts 'hello'.to_s
puts 1.to_s
#output:
inside namespace: hello
blah
1
}}
The improvements here are the use of a hash to associate a namespace
with a specific class, only applying the namespaces that map to the
target object's class, and (behind the scenes) resetting namespaces at
the end of the file. So the "using" flips them on, and the end of the
file flips them off. Multiple are also possible:
{{
module Foo
module Bar
def to_s
puts 'string namespace'
end
end
end
module Blah
def to_s
puts 'fixnum namespace'
end
end
using Foo::Bar => String
using Blah => Fixnum
'hello'.to_s
1.to_s
#output:
string namespace
fixnum namespace
}}
Again, this is only hacked for these specific call types, namely an
fcall with no block (for "using" to capture namespaces) and a call with
no args and no block (for the to_s calls).
Patch against JRuby trunk is attached as an example.
- Charlie
Yehuda Katz wrote:
diff --git a/src/org/jruby/ast/CallNoArgNode.java b/src/org/jruby/ast/CallNoArgNode.java
index 1b2cd40..081e1f0 100644
--- a/src/org/jruby/ast/CallNoArgNode.java
+++ b/src/org/jruby/ast/CallNoArgNode.java
@@ -32,8 +32,12 @@
***** END LICENSE BLOCK *****/
package org.jruby.ast;
+import java.util.List;
import org.jruby.Ruby;
+import org.jruby.RubyArray;
import org.jruby.RubyClass;
+import org.jruby.RubyHash;
+import org.jruby.RubyModule;
import org.jruby.exceptions.JumpException;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.lexer.yacc.ISourcePosition;
@@ -46,19 +50,48 @@ import org.jruby.runtime.builtin.IRubyObject;
* A method or operator call.
*/
public final class CallNoArgNode extends CallNode {
+ List<Node> namespaces;
// For 'b.foo'
public CallNoArgNode(ISourcePosition position, Node receiverNode, String name) {
super(position, receiverNode, name, null, null);
}
+
+ // For 'b.foo()'. Args are only significant in maintaining backwards compatible AST structure
+ public CallNoArgNode(ISourcePosition position, Node receiverNode, String name, List<Node> namespaces) {
+ super(position, receiverNode, name, null, null);
+ this.namespaces = namespaces;
+ }
// For 'b.foo()'. Args are only significant in maintaining backwards compatible AST structure
public CallNoArgNode(ISourcePosition position, Node receiverNode, Node args, String name) {
super(position, receiverNode, name, args, null);
}
+
+ // For 'b.foo()'. Args are only significant in maintaining backwards compatible AST structure
+ public CallNoArgNode(ISourcePosition position, Node receiverNode, Node args, String name, List<Node> namespaces) {
+ super(position, receiverNode, name, args, null);
+ this.namespaces = namespaces;
+ }
@Override
public IRubyObject interpret(Ruby runtime, ThreadContext context, IRubyObject self, Block aBlock) {
- return callAdapter.call(context, self, getReceiverNode().interpret(runtime, context, self, aBlock));
+ IRubyObject receiver = getReceiverNode().interpret(runtime, context, self, aBlock);
+ if (namespaces != null) {
+ DynamicMethod method = null;
+ for (Node node : namespaces) {
+ IRubyObject namespace = ((RubyArray)node.interpret(runtime, context, self, aBlock)).eltInternal(0);
+ RubyModule nsModule = ((RubyModule)((RubyHash)namespace).keys().eltInternal(0));
+ RubyClass targetClass = (RubyClass)((RubyHash)namespace).rb_values().eltInternal(0);
+ if (targetClass == receiver.getMetaClass()) {
+ method = nsModule.searchMethod(callAdapter.methodName);
+ if (method != null && !method.isUndefined()) break;
+ }
+ }
+ if (method != null && !method.isUndefined()) {
+ return method.call(context, receiver, self.getMetaClass(), callAdapter.methodName);
+ }
+ }
+ return callAdapter.call(context, self, receiver);
}
@Override
diff --git a/src/org/jruby/parser/ParserSupport.java b/src/org/jruby/parser/ParserSupport.java
index f3d9b97..267b54b 100644
--- a/src/org/jruby/parser/ParserSupport.java
+++ b/src/org/jruby/parser/ParserSupport.java
@@ -35,6 +35,8 @@
***** END LICENSE BLOCK *****/
package org.jruby.parser;
+import java.util.ArrayList;
+import java.util.List;
import org.jruby.ast.AndNode;
import org.jruby.ast.ArgsPreOneArgNode;
import org.jruby.ast.ArgsPreTwoArgNode;
@@ -182,6 +184,8 @@ public class ParserSupport {
private ParserConfiguration configuration;
private RubyParserResult result;
+ private List namespaces = new ArrayList();
+
public void reset() {
inSingleton = 0;
inDefinition = false;
@@ -192,6 +196,10 @@ public class ParserSupport {
throw new SyntaxException(PID.DUBY_EXTENSIONS_OFF, position, "Duby extensions not configured");
}
}
+
+ public List getNamespaces() {
+ return namespaces;
+ }
public StaticScope getCurrentScope() {
return currentScope;
@@ -922,8 +930,12 @@ public class ParserSupport {
if (receiver == null) receiver = NilImplicitNode.NIL;
if (iter != null) return new CallNoArgBlockNode(position, receiver, (String) name.getValue(), iter);
-
- return new CallNoArgNode(position, receiver, (String) name.getValue());
+
+ if (namespaces.size() > 0) {
+ return new CallNoArgNode(position, receiver, (String) name.getValue(), namespaces);
+ } else {
+ return new CallNoArgNode(position, receiver, (String) name.getValue());
+ }
}
private Node new_call_complexargs(Node receiver, Token name, Node args, Node iter) {
@@ -972,8 +984,12 @@ public class ParserSupport {
switch (args.size()) {
case 0:
if (iter != null) return new CallNoArgBlockNode(union(receiver, args), receiver, (String) name.getValue(), args, (IterNode) iter);
-
- return new CallNoArgNode(union(receiver, args), receiver, args, (String) name.getValue());
+
+ if (namespaces.size() > 0) {
+ return new CallNoArgNode(union(receiver, args), receiver, args, (String) name.getValue(), namespaces);
+ } else {
+ return new CallNoArgNode(union(receiver, args), receiver, args, (String) name.getValue());
+ }
case 1:
if (iter != null) return new CallOneArgBlockNode(union(receiver, args), receiver, (String) name.getValue(), args, (IterNode) iter);
@@ -1014,6 +1030,14 @@ public class ParserSupport {
private Node new_fcall_simpleargs(Token operation, ArrayNode args, Node iter) {
String name = (String) operation.getValue();
+
+ // it if's a "using" call add a namespace and return a nil node
+ if (name.equals("using")) {
+ namespaces.add(args);
+ return new NilNode(union(operation, args));
+ }
+
+ // otherwise, if we have a namespace, pass it into the FCall
switch (args.size()) {
case 0: // foo()
You have misunderstood my point. I'm not saying you don't have a
problem. I'm saying selector namespaces will be used in the way I
indicated. And it will lead to ugly, more confusing software. You can
solve your problem without selector namespaces. Instead of focusing on
why you think they are a good idea, I suggest considering how they can
be misused.
All you are saying is you want a way to define methods on the same
class with the same name but different behaviors, where the particular
method called is selected by context. I get it. I think it's a bad
idea.
Brian
I agree that this will be used in ways other than just framework creators. Rails and Merb may not use this to erect fences, but someone will. I think that before we go gung-ho on this, there should be a Devil’s advocate discussion of how one could misuse this feature, and how it could cause problems. The problems may not warrant dropping the idea, they may just help see issues with it. I think that is a really good exercise that we should do.
As an example, what if I am using Merb, and two plugins, which all define a namespaced method, which I want to change. That’s three namespaces that I have to __know about__ and modify. As opposed to one now.
JD
Going out and changing (monkeypatching) libraries (or namespaces)
willy-nilly is what called for something more localized to begin with.
I've yet to see a case that monkeypatching makes simpler or easier, and
this is no different.
I think it's a question of what sort of pain you bring on yourself. If
you monkeypatch libraries, and someone else's patch conflicts, you both
deserve to get your knuckles rapped. If you monkeypatch someone else's
module and it leads to more complicated code or eventual bugs, you
deserve to get your knuckles rapped.
If anything, the namespace proposal is vastly safer than monkeypatching
to begin with...so much so that it could probably take over most of
monkeypatching's use cases, leading to *better* code.
I understand the concern, but anyone arguing that "feature X combined
with monkeypatching is too dangerous/confusing/complicated" should
probably consider whether it's monkeypatching's fault in the first place.
- Charlie
> As an example, what if I am using Merb, and two plugins, which all
> define a namespaced method, which I want to change. That's three
> namespaces that I have to __know about__ and modify. As opposed to
> one now.
I must admit, I'm a little confused by the above. Maybe we have
different understandings about what selector namespaces are and what
they do.
If three plugins each define a method named "xyz" in different
modules, you have three different methods in three modules. Adding
namespaces doesn't change that. All the namespace does is allow
programmers to say "Over this section of code, calling a method named
'xyz' will come from this module rather than that module."
Could some elaborate about their fears of name spaces creating
fences. I don't quite understand what they are getting at.
Thanks.
--
-- Jim Weirich
-- jim.w...@gmail.com
> -----Original Message-----
> From: Jim Weirich [mailto:jim.w...@gmail.com]
> Sent: Tuesday, February 24, 2009 8:53 PM
> To: ruby...@ruby-lang.org
> Subject: [ruby-core:22448] Re: YASNP (Yet Another Selector Namespace
> Proposal)
>
>
Clarification of my example: If three namespaces define xyz, and you want all of the xyz's to act differently, then you need to modify three namespaces with monkeypatching. This way you change their effects wherever you use them.
Rebuttal of my example: As pointed out by Charlie here and offlist, the proper way to handle this conflict of namespaces is to create your own namespace that does what you want.
Overall, the example wasn't my major point, my point was in asking the same community that is lively about this discussion, and very creative (just see the Ruby projects in existence), to come up with ways that this can be misused. Throw them out there so we can see if there is a large downside. I don't know if there is. This example wasn't meant to prove it wrong, just to give an idea.
I guess this kind of falls under bikeshedding, but I think that it is valid to partake in some of this kind of brainstorming before this, or any, proposal is accepted.
JD
> I guess this kind of falls under bikeshedding, but I think that it
> is valid to partake in some of this kind of brainstorming before
> this, or any, proposal is accepted.
That's why Charlie's prototyping is so important. Once we have real
code to play with, we can have concrete examples of problems rather
than vague fears about fences.
If folks are interested in playing with it more, I could expand it to
cover all call, fcall, and vcall forms. It would be easier to experiment
in a general way then.
- Charlie
Jim Weirich wrote:
--
boss forgot system password
Here's a patch that extends it at least to all forms of CallNode, which
is probably the primary case. fcalls and vcalls are against self, and
will probably be less common to namespace. attr/op/element assignments
will probably be rare for similar reasons. But they'd probably all need
similar treatment.
This patch is a bit bigger and messier, but still only about a half
hour's work. A better design would be to enlist the parser in this
process. It would also produce better-performing code; I don't expect
this version to perform particularly well.
I've also attached a really primitive rspec that shows "using" inside an
eval. In the current prototype, using is scoped to the eval body as if
it were a file. I kinda like it.
- Charlie
The namespace applies from that point forward; the original Foo::Bar
module is unaffected.
This could be modified to be for a full file, but it would require more
parser help and ideally would have to be at the top of the file.
- Charlie
In message "Re: [ruby-core:22427] Re: YASNP (Yet Another Selector Namespace Proposal)"
on Wed, 25 Feb 2009 07:04:34 +0900, Brian Ford <bri...@gmail.com> writes:
|In implementing Rubinius, we have only encountered one major issue
|with redefining a core library method: mathn changing Fixnum#/.
Interesting. I thought people behind Rubinius would love (or even
demand) some kind of selector namespace, to protect their core
libraries from users' monkey patching.
matz.
I'm also in favor of discussing this, but all I hear so far in opposition is vague FUD... no specific examples of problems that could be caused. It would be a lot easier to have a lively discussion about specific concerns, and I'd love to have it!-- Yehuda
Selector namespaces was and probably still is a planned feature; if
Ruby has it, the better. It is not really for strict protection,
there are other mechanisms for that, but it would be cleaner from
the user's point of view.
> BTW: how will IRB handle file-based switches?
This is a clear use case for fine-grained control of namespaces. You
may want to run a single call or block inside of particular
combination of namespaces for debugging or testing.
Also exceptions should carry the namespace combination in which the
failure occured and you should be able to tell what namespaces you are
using at any point in your code.
Thanks
Michal
On Feb 25, 2009, at 6:17 AM, Yehuda Katz wrote:I'm also in favor of discussing this, but all I hear so far in opposition is vague FUD... no specific examples of problems that could be caused. It would be a lot easier to have a lively discussion about specific concerns, and I'd love to have it!-- YehudaIs FUD the new "the opposition doesn't convince me"? I hear it a lot. I think it's overused. Also, I find it insulting or at least a "verbal" fauxpas as well as misplaced[1].
The problem is rather obvious: suddently, I can have multiple definitions of a method (or none!) depending on context. Nowadays, i can just inspect/test that behaviour in IRB/rdb and be sure that it is about the same everywhere (safe for the unusual catch that it could be un/redefined along the way). With selector namespaces, that catch would get commonplace.
This could be fixed by additional inspection facilities, but it _is_ an additional layer of complexity (and not a small one). I also don't buy the "no one will use it" argument. Back when I started learning Ruby, I heard the same about redefining/extending core methods/objects. Nowadays, you cannot find a lib that doesn't have it's small extension to Object.
BTW: how will IRB handle file-based switches?
In favor of the proposal, I also want to construct explain a case where I always missed it. Take ActiveRecord or DataMapper. The objects usually get passed into a template (a completely different context) where most of the methods are not intended to be used (#find being the common case, basically everything that does Database operations explicitly). The uninitiated (TM) still use them, causing all kinds of problems to the initiated (TM).Some frameworks I know (mostly PHP) solve this problem by hydrating the database objects to an array before handing them over to the view. With selector namespaces, there would be an easy fix for this: let the view be a evaluted in a different Namespace. This would also allow for "convenience methods" to be added in the view exclusively.
Regards,Florian[1]: FUD stands for a marketing strategy after all. There is no market at ruby-core. And we are not corporate goons trying to keep you from something.
In message "Re: [ruby-core:22460] Re: YASNP (Yet Another Selector Namespace Proposal)"
on Wed, 25 Feb 2009 17:47:27 +0900, Florian Gilcher <f...@andersground.net> writes:
|BTW: how will IRB handle file-based switches?
Good point.
Seems Google ate my reply to this.
Apparently my thought experiment on possible misuses is too difficult.
Here is a concrete alternative exercise. What are the principles for
using selector namespaces?
When do you put one method in? If one method, why not two? Where are
the boundaries in a library, framework or application?
The essence of my objection is that you will so cleverly put into a SN
a method that someone else will want to change.
Again, there has been no justification for why SNs are *needed* to
solve the problem presented here. You want to have multiple methods
with the same name on the same public, common class but with different
behaviors. There is no justification for why #chars or #camel_case
*must* have different behaviors.
You think, "I'm so smart about programming my app, no one will want to
monkey patch this method." But they will! Just as they have done in
Ruby for a long time. And when they do, what will that look like? And
when you try to understand the behavior of some software, you now need
to look into N different definitions of one method name and determine
how those interact.
Complexity for what benefit? How is this more natural? And it is not
just in one file. There is no restriction of a class to one file in
Ruby. Open class gives the ability to build software in layers.
The following applies just as well to engineering software and
designing languages as to building furniture: http://www.johndilworth.com/95.
Brian
> > > -- jim.weir...@gmail.com
Hi Matz,
On the contrary, I have strongly argued for Rubinius to be accessible
and modifiable to the deepest levels since hearing Evan's comment
about Rubinius kinda having evil built in. Of course it can be
misused, just as I can load a segfaulting C extension into MRI.
The case of mathn and Fixnum#/ is an anomaly IMO. Had there not been
the distinction between methods in C and methods in Ruby, mathn would
not have been written that way. Perhaps redesigning the Numeric tower
so that real number math was the default.
I understand how selector namespaces can be used. My concern is that
they can be misused in a way that makes software harder to understand
or that requires an even stronger antidote than monkey patching to
work around someone's well-intentioned assumptions that may be invalid
in a different context.
Considering the possible unintended consequences isn't FUD, it's a
sincere effort to maintain the quality of Ruby software and the
enjoyment of writing it.
Cheers,
Brian
just do this:
MyFramework.foo("bar")
?
The second is even cleaner and says clearly what method you call.
Using NS you must remember which method you imported to file and which
not.
--
Pozdrawiam
Radosław Bułat
http://radarek.jogger.pl - mój blog
There may be a middle way
If the parser were modified such that upon encountering the
EOF token, it terminated the current do/end block, then
:use could be a normal Kernel method that simply takes block,
without introducing spurious indentation.
use MyWorld do
....
<EOF>
This also allows quite handy expressions such as:
ruby -e "3.times do print 'Howdy, '"
or
eval "3.times do print 'Howdy, '"
There is still the question of whether EOF should terminate nested
do style blocks in order to handle:
use MyWorld do
use OtherWorld do
.....
<EOF>
This parser change certainly is easy to implement.
And, it cannot break any working code.
- brent
Charles Oliver Nutter-2 wrote:
>
> Yukihiro Matsumoto wrote:
>> +1 to have some kind of selector namespace (it's planned for 2.0
>> anyway), but -1 for using block (or block-like syntax) to denote
>> namespace scope, because
>>
>> * it's not really a block
>> * too small granularity for namespace switching might bring confusion
>>
>> I'd prefer per file namespace switching.
>
> I think it's impossible to have per-file without making "use" a special
> keyword, ideally at the top of the file. The block syntax made more
> sense to me because I knew exactly when it started and ended, and a
> 'use' later in the file did not affect code that came before it. Also,
> the example where I have both a namespace and a 'use' in the same file
> would be impossible if the use applied to the whole file.
>
> I'm willing to mock up anything else we want to try, including a
> whole-file version.
>
> - Charlie
>
>
>
--
View this message in context: http://www.nabble.com/-ruby-core%3A22246--YASNP-%28Yet-Another-Selector-Namespace-Proposal%29-tp22094654p22208463.html
Sent from the ruby-core mailing list archive at Nabble.com.
On Feb 24, 9:17 pm, Yehuda Katz <wyc...@gmail.com> wrote:
> I'm also in favor of discussing this, but all I hear so far in opposition isSeems Google ate my reply to this.
> vague FUD... no specific examples of problems that could be caused. It would
> be a lot easier to have a lively discussion about specific concerns, and I'd
> love to have it!
> -- Yehuda
>
Apparently my thought experiment on possible misuses is too difficult.
Here is a concrete alternative exercise. What are the principles for
using selector namespaces?
When do you put one method in? If one method, why not two? Where are
the boundaries in a library, framework or application?
The essence of my objection is that you will so cleverly put into a SN
a method that someone else will want to change.
Again, there has been no justification for why SNs are *needed* to
solve the problem presented here. You want to have multiple methods
with the same name on the same public, common class but with different
behaviors. There is no justification for why #chars or #camel_case
*must* have different behaviors.
You think, "I'm so smart about programming my app, no one will want to
monkey patch this method."
But they will! Just as they have done in
Ruby for a long time. And when they do, what will that look like? And
when you try to understand the behavior of some software, you now need
to look into N different definitions of one method name and determine
how those interact.
Complexity for what benefit? How is this more natural? And it is not
just in one file. There is no restriction of a class to one file in
Ruby. Open class gives the ability to build software in layers.
The following applies just as well to engineering software and
designing languages as to building furniture: http://www.johndilworth.com/95.
And if i wanted code like that, i'd use a functional language ;).
Regards,
Florian
I think the FUD here is "oh no, what if this happens or that happens and
code gets more complicated!". You can what if any feature to death, but
without considering the probability of real issues it really is FUD.
And of course the "what iffers" may not be weighing those scenarios
against the positive uses of namespaces.
> The problem is rather obvious: suddently, I can have multiple
> definitions of a method (or none!) depending on context. Nowadays, i can
> just inspect/test that behaviour in IRB/rdb and be sure that it is about
> the same everywhere (safe for the unusual catch that it could be
> un/redefined along the way). With selector namespaces, that catch would
> get commonplace.
I wanted to point out that having the following namespace:
module Foo; def to_s; 'blah'; end; end
using Foo
'hello'.to_s
Is really no different from the following code, other than being cleaner
and shorter:
module Foo; def Foo.to_s(str); 'blah'; end; end
Foo.to_s('hello')
You can't really control what any library decides to do today, so I
don't think you lose any more control with namespaces. And
monkeypatching code because you want *others* to be affected has always
been a losing proposition...since they may have monkeypatched code to
affect *you* already.
> BTW: how will IRB handle file-based switches?
It would need to have modifications to its parser logic to support
namespacing, just as it needed modifications to allow stabby lambdas
through.
- Charlie
--
Ola Bini (http://olabini.com)
Ioke creator (http://ioke.org)
JRuby Core Developer (http://jruby.org)
Developer, ThoughtWorks Studios (http://studios.thoughtworks.com)
Practical JRuby on Rails (http://apress.com/book/view/9781590598818)
"Yields falsehood when quined" yields falsehood when quined.
I'm starting to think that if we don't have explicitly bounded
namespacing, there should be a way to turn it off. Like "using nil" but
not as ugly..
- Charlie
Is FUD the new "the opposition doesn't convince me"? I hear it a lot. I think it's overused. Also, I find it insulting or at least a "verbal" fauxpas as well as misplaced[1].
Apologies.
The problem is rather obvious: suddently, I can have multiple definitions of a method (or none!) depending on context. Nowadays, i can just inspect/test that behaviour in IRB/rdb and be sure that it is about the same everywhere (safe for the unusual catch that it could be un/redefined along the way). With selector namespaces, that catch would get commonplace.
Not really... because the namespaces would need to be specified on a per-file basis (as per Matz), it would be trivial to go into a file and determine which namespaces were in use. Remember, this is not a global facility, it's per-file facility that must be explicitly invoked. It's also not the case that when *I* invoke a selector namespace in my library code, it effects your library code or your app.
This could be fixed by additional inspection facilities, but it _is_ an additional layer of complexity (and not a small one). I also don't buy the "no one will use it" argument. Back when I started learning Ruby, I heard the same about redefining/extending core methods/objects. Nowadays, you cannot find a lib that doesn't have it's small extension to Object.
It's not really relevant if people will use it, because it must be explicitly invoked. In other words, someone else can't invoke a selector namespace on your code. I think the most common use-case will likely be frameworks and libraries, but if you want to extend string for the duration of your application, and explicitly include the namespace into your files, that doesn't bother me. In short, it doesn't seem as complex as people are making it out to seem, or even more complex than facilities we have in Ruby today.
BTW: how will IRB handle file-based switches?
I'd assume that namespaces would apply to the rest of the IRB session. Perhaps a "using nil" could be specified to deactivate namespaces.
In favor of the proposal, I also want to construct explain a case where I always missed it. Take ActiveRecord or DataMapper. The objects usually get passed into a template (a completely different context) where most of the methods are not intended to be used (#find being the common case, basically everything that does Database operations explicitly). The uninitiated (TM) still use them, causing all kinds of problems to the initiated (TM).Some frameworks I know (mostly PHP) solve this problem by hydrating the database objects to an array before handing them over to the view. With selector namespaces, there would be an easy fix for this: let the view be a evaluted in a different Namespace. This would also allow for "convenience methods" to be added in the view exclusively.
It wouldn't really work that way, unless you explicitly removed methods from the global ActiveRecord and only made them available in a specific namespace. Even then, it would be trivial to include the namespace in question into the templates.
[1]: FUD stands for a marketing strategy after all. There is no market at ruby-core. And we are not corporate goons trying to keep you from something.
Hehe... Perhaps a strong term. I was trying to get across that the objections were vague, and in the interest of a vibrant discussion, I was hoping for some clear examples that would be so complex as to justify the derision. After all, Ruby is not a particularly simple language; instead, it aims to be NATURAL.
...Why oh why? :)
(Mind you, the "block" is not actually a block, so the `do .. end`
notation would be possibly misleading; hence my `using ... end`
suggestion. The point is of course moot if we go with matz' file-
wide modifier.)
I'm not seeing your point - selector namespaces aren't a fence to keep
client code out, they're a fence to keep framework code in. Let's say
merb defines String#chars, wrapped neatly within a namespace so that
all the code in merb can make use of it and no code outside merb sees
it. Now as a merb user/extender, if you want to change the way
String#chars behaves within merb, simply reopen the namespace and
change it. All that the SN is preventing is merb's String#chars from
leaking out into the global namespace - in fact, I'd go so far as to
say that it changed monkeypatching from being dynamically scoped to
being lexically scoped, which cannot be other than a good thing.
> Again, there has been no justification for why SNs are *needed* to
> solve the problem presented here. You want to have multiple methods
> with the same name on the same public, common class but with different
> behaviors. There is no justification for why #chars or #camel_case
> *must* have different behaviors.
In fact SNs could fix something that has always bothered me about ruby
- that 'require' introduces dynamically scoped changes to your
environment.
martin:~ $ cat a.rb
class String
def length
1
end
end
martin:~ $ cat b.rb
class B
def foo
"hello".length
end
def bar
require 'a.rb'
end
end
b = B.new
p b.foo
b.bar
p b.foo
martin:~ $ ruby b.rb
5
1
With your code, you know what you are doing; with a framework, you
shouldn't need to worry that 'require' is doing something other than
giving you access to that framework's defined API simply because
*within the context of the framework* it was found convenient to
redefine some core classes.
> You think, "I'm so smart about programming my app, no one will want to
> monkey patch this method." But they will! Just as they have done in
> Ruby for a long time. And when they do, what will that look like? And
> when you try to understand the behavior of some software, you now need
> to look into N different definitions of one method name and determine
> how those interact.
Let them monkeypatch it and be happy. They'll just need to do it
within the namespace.
> Complexity for what benefit? How is this more natural? And it is not
> just in one file. There is no restriction of a class to one file in
> Ruby. Open class gives the ability to build software in layers.
And selector namespaces give the ability to have your code lexically
rather than dynamically scoped.
martin
I think Matz is referring to a desire to ensure that within Rubinius's
"string.rb" that implements String, they are able to guarantee for
certain methods that the "internal" versions are called. Then
monkey-patching a core class would not (in theory) break Rubinius internals.
- Charlie
Accepted, no hard feelings.Is FUD the new "the opposition doesn't convince me"? I hear it a lot. I think it's overused. Also, I find it insulting or at least a "verbal" fauxpas as well as misplaced[1].
Apologies.The problem is rather obvious: suddently, I can have multiple definitions of a method (or none!) depending on context. Nowadays, i can just inspect/test that behaviour in IRB/rdb and be sure that it is about the same everywhere (safe for the unusual catch that it could be un/redefined along the way). With selector namespaces, that catch would get commonplace.
Not really... because the namespaces would need to be specified on a per-file basis (as per Matz), it would be trivial to go into a file and determine which namespaces were in use. Remember, this is not a global facility, it's per-file facility that must be explicitly invoked. It's also not the case that when *I* invoke a selector namespace in my library code, it effects your library code or your app.
Yes. I'm more about reconstruction what happens later on. Say... you have an Object A in Context C, because you have a breakpoint there. You have an assertion about a property of the object that you will use later on. You want to check that assertion _now_. But as "later on" happens to be in Context D, you assertion is true now but wrong later on.
I don't want to present this as a showstopper. But it _is_ something that can happen which couldn't happen before. It will certainly not happen to us good programmers.Sure, but one of your arguments was that it won't be widly used anyways ;).This could be fixed by additional inspection facilities, but it _is_ an additional layer of complexity (and not a small one). I also don't buy the "no one will use it" argument. Back when I started learning Ruby, I heard the same about redefining/extending core methods/objects. Nowadays, you cannot find a lib that doesn't have it's small extension to Object.
It's not really relevant if people will use it, because it must be explicitly invoked. In other words, someone else can't invoke a selector namespace on your code. I think the most common use-case will likely be frameworks and libraries, but if you want to extend string for the duration of your application, and explicitly include the namespace into your files, that doesn't bother me. In short, it doesn't seem as complex as people are making it out to seem, or even more complex than facilities we have in Ruby today.
I don't see it as something overly complex. But then again I also see this with the mind of my collegues that really struggle even with the concept of :method_missing. So I try to illustrate both sides. I like to adopt both viewpoints for the sake of an internal discussion.
BTW: how will IRB handle file-based switches?
I'd assume that namespaces would apply to the rest of the IRB session. Perhaps a "using nil" could be specified to deactivate namespaces.
Hm, i would prefer to actually being able to switch. Especially, because i use irb heavily for development of new code, not just to check.In favor of the proposal, I also want to construct explain a case where I always missed it. Take ActiveRecord or DataMapper. The objects usually get passed into a template (a completely different context) where most of the methods are not intended to be used (#find being the common case, basically everything that does Database operations explicitly). The uninitiated (TM) still use them, causing all kinds of problems to the initiated (TM).Some frameworks I know (mostly PHP) solve this problem by hydrating the database objects to an array before handing them over to the view. With selector namespaces, there would be an easy fix for this: let the view be a evaluted in a different Namespace. This would also allow for "convenience methods" to be added in the view exclusively.
It wouldn't really work that way, unless you explicitly removed methods from the global ActiveRecord and only made them available in a specific namespace. Even then, it would be trivial to include the namespace in question into the templates.
Oh, sure, nothing is foolproof. But it shows an intention.[1] I also just wanted to construct a case i see coming for DataMapper and ActiveRecord, but some future library might make use of semantics like that. And it is a case most people handle every day.
[1]: FUD stands for a marketing strategy after all. There is no market at ruby-core. And we are not corporate goons trying to keep you from something.
Hehe... Perhaps a strong term. I was trying to get across that the objections were vague, and in the interest of a vibrant discussion, I was hoping for some clear examples that would be so complex as to justify the derision. After all, Ruby is not a particularly simple language; instead, it aims to be NATURAL.
I never got that NATURAL. ATM, I work at a company where everything outside of Java, RFC calls and XML is unnatural. For those people, Java is natural. Because thats the nature of the context.I like Ruby for keeping a complex thing complex. Perhaps thats natural, perhaps not.
Regards,Florian[1]: I like code hinting at the intention of a programmer. It gives you the possibility not to support people that work against your intentions. "But I was able to make it available" is far less of an argument then "I was able to make it available".The same argument goes for private methods. "But I was able to use __send__ to call it in the last version" is different from "But i was able to call it". I don't give out support for people using my private methods while I do invite them to do whatever they want with it if they know what they are doing.
I have read the full thread now, and have some concrete questions about
the proposal. I'm not opposed to the proposal, but these are things I'd
really like to understand before agreeing to it.
If any of this has been answered, or if I misunderstood something, I
apologize in advance:
- How would this interact with send?
- How would it interact with other introspection facilities? Kernel#method ?
Basically, what is the runtime behavior of all metaprogramming and
introspection features here...
- Do I understand correctly that this is supposed to be a lexical
effect, and not a dynamic one?
... in that case I have no idea how it's supposed to work at parse
time. Basically, if you use modules as possible namespaces, there is no
way the parser can know which methods are supposed to be possibly
selector-based. And even so, you don't actually know in the above
example that "str" is a String, so how do you know that Merb::String
should be applied to that variable at parse time? The thread has said
it's a lexical effect, but all the examples I see have confused lexical
and dynamic effects, and parse time with runtime.
Once again, the third point could relate to me misunderstanding things
here. But if not, I'm not sure how this is supposed to work.
Cheers
> Florian Gilcher wrote:
>> On Feb 25, 2009, at 6:17 AM, Yehuda Katz wrote:
>>> I'm also in favor of discussing this, but all I hear so far in
>>> opposition is vague FUD... no specific examples of problems that
>>> could be caused. It would be a lot easier to have a lively
>>> discussion about specific concerns, and I'd love to have it!
>>>
>>> -- Yehuda
>> Is FUD the new "the opposition doesn't convince me"? I hear it a
>> lot. I think it's overused. Also, I find it insulting or at least a
>> "verbal" fauxpas as well as misplaced[1].
>
> I think the FUD here is "oh no, what if this happens or that happens
> and code gets more complicated!". You can what if any feature to
> death, but without considering the probability of real issues it
> really is FUD.
>
> And of course the "what iffers" may not be weighing those scenarios
> against the positive uses of namespaces.
That may be true or not, but it is still against the rules of a good
discussion to generally calling it FUD. Disprove the other or ignore
him. Accept if he doesn't have your point of view. Tell him that he
doesn't convince you. For me, that comment was ad hominem, because it
also has generic implications about the others state of mind and
behaviour.
>
>> The problem is rather obvious: suddently, I can have multiple
>> definitions of a method (or none!) depending on context. Nowadays,
>> i can just inspect/test that behaviour in IRB/rdb and be sure that
>> it is about the same everywhere (safe for the unusual catch that it
>> could be un/redefined along the way). With selector namespaces,
>> that catch would get commonplace.
>
> You can't really control what any library decides to do today, so I
> don't think you lose any more control with namespaces. And
> monkeypatching code because you want *others* to be affected has
> always been a losing proposition...since they may have monkeypatched
> code to affect *you* already.
As i pointed out more then once: I am on the side of having
namespaces, i just wanted to illustrate that it is not impossible to
find things get more complex. But i was also not speaking about
control but about making sense of a current and future state of a
system at a given moment.
>
>> BTW: how will IRB handle file-based switches?
>
> It would need to have modifications to its parser logic to support
> namespacing, just as it needed modifications to allow stabby lambdas
> through.
>
> - Charlie
Well, thats for sure. Will it be able to switch namespaces, will it
not? I wanted to raise the question, not imply that it is hard to
answer.
Regards,
Florian
Yes, +1 for using ... end. If we go with explicit scoping (or maybe make
it optional somehow?) this would be the right way. using needs to be
something the parser is aware of for any of this to work, so we might as
well do it right.
- Charlie
So on the one hand you support Rubinius being totally open, even if that
openness can be misused, but on the other you don't want selector
namespaces because they can be misused. Aren't these opposite positions?
And if Rubinius is as open as you want it to be, wouldn't it be simple
for someone to implement selector namespaces in Rubinius? And would you
support that or not?
So clarify for me: are you saying you support Rubinius's total openness
and its vastly more likely potential for misuse, but you don't support
namespaces and their relatively limited potential for misuse?
- Charlie
In message "Re: [ruby-core:22490] Re: YASNP (Yet Another Selector Namespace Proposal)"
on Thu, 26 Feb 2009 04:08:19 +0900, Ola Bini <ola....@gmail.com> writes:
|If any of this has been answered, or if I misunderstood something, I
|apologize in advance:
Here's MY opinion.
|- How would this interact with send?
send should honor namespaces.
|- How would it interact with other introspection facilities? Kernel#method ?
| Basically, what is the runtime behavior of all metaprogramming and
|introspection features here...
and other introspection features as well.
|- Do I understand correctly that this is supposed to be a lexical
|effect, and not a dynamic one?
I think so.
| ... in that case I have no idea how it's supposed to work at parse
|time.
One possible idea is making symbols to belong to each namespace, like
Common Lisp does, in that case,
obj.foo(bar)
should be work as
obj.<namespace::foo>(bar)
where <namespace::foo> means "symbol foo in that namespace", and its
correspondence can be modified by (dynamic) method definitions.
matz.
Yeah, I don't see it as being too difficult, though IRB does force the
issue of "turning namespaces off" at some point, since you may want to
do that in a long IRB session. And if you can turn namespaces
off...maybe they should be explicitly scoped :)
- Charlie
David Flanagan
> You end up needing to do:
>
> Extlib
> ::String
> .constantize(Extlib::String.to_path(Extlib::String.underscore(str)))
>
> instead of:
>
> str.underscore.to_path.constantize
>
> Honestly, we tried this in DataMapper for a while, but it got too
> bulky to
> be realistic (outside of Java, I suppose).
Plus this technique wouldn't allow polymorphic methods based on the
target's type. That's a serious disadvantage.
--
-- Jim Weirich
-- jim.w...@gmail.com
GREAT point...I hadn't even considered that. That's a much stronger
argument than the verbosity.
- Charlie
> - How would this interact with send?
> - How would it interact with other introspection facilities?
> Kernel#method ?
> Basically, what is the runtime behavior of all metaprogramming
> and introspection features here...
> - Do I understand correctly that this is supposed to be a lexical
> effect, and not a dynamic one?
Ola asks some great questions. I have a few as well:
(1) If two selector namespaces are in scope, and they both define a
method of the same name, which one takes precedence? (i.e. Assume
that namespaces X and Y both defined a method "to_xml" on Object, and
that both are in scope. Which version will be invoked?)
(2) With a particular namespace in scope, will there be a way to
explicitly invoke a conflicting method from a different namespace
(i.e. Assume that namespaces X and Y both define a method "to_xml" on
Object. Will there be a way to explicitly invoke the Y version of
"to_xml" from code that is currently in the scope of the X namespace.)
In my prototype they're first-come, first-served. You can have many
namespaces affect the same class and provide the same methods, and
they're searched in "using" order:
module Foo; def to_s; 'here'; end; end
module Bar; def to_s; 'there'; end; end
using Foo => String
using Bar => String
puts 'hello'.to_s # => outputs "here"
It seems like the most straight-forward behavior to me.
> (2) With a particular namespace in scope, will there be a way to
> explicitly invoke a conflicting method from a different namespace (i.e.
> Assume that namespaces X and Y both define a method "to_xml" on Object.
> Will there be a way to explicitly invoke the Y version of "to_xml" from
> code that is currently in the scope of the X namespace.)
Probably not. This would probably be a rare case, but it would be a
strong argument for making namespaces more fine-grained. Of course, Dan
Berger's proposal of a per-call syntax would resolve this, but it's more
verbose.
- Charlie
We have all been using the words "namespace" and "selector" pretty much
interchangeably in this thread.
I'm becoming a bit confused about talking about several selectors being
in scope at the same time here. One of my points in the other mail I
sent was that there is no real way to know what a namespace contains
until runtime - and the parser needs to know at parse time - otherwise
it can't choose the right namespace. Which is why I thought there could
only be _one_ namespace active at the same time.
For more reference, I urge a look at Common Lisp packages.
> When do you put one method in? If one method, why not two? Where are
> the boundaries in a library, framework or application?
You should use a namespace whenever you add a method to a module or
class that is not part of your library. For example, Rake adds an
"ext" method to String. If selector namespaces were available, Rake
would scope that "ext" method in a Rake namespace. Allowing other
libraries/frameworks to also define their own independent version of
"ext" and allow users to explicit control which version they call.
You should probably not use namespaces on modules or classes that you
own and control.
> The essence of my objection is that you will so cleverly put into a SN
> a method that someone else will want to change.
Putting something in a namespace does nothing to prevent someone from
intentionally changing the method.
> Again, there has been no justification for why SNs are *needed* to
> solve the problem presented here. You want to have multiple methods
> with the same name on the same public, common class but with different
> behaviors.
A better way to say this is that we want to be able to enhance shared
classes with framework specific methods without:
(1) needing to coordinate non-conflicting names across all libraries
for all time,
(2) needing to name our methods with ugly, conflict resolving prefixes
(e.g. calling using "rake_ext" instead of "ext").
> You think, "I'm so smart about programming my app, no one will want to
> monkey patch this method." ...
Selector namespaces are not about preventing intentional monkey
patching. If Rake put the "ext" method into a namespace, you could
still monkey patch it.
It is to prevent accidental name collision. If anything, it makes
monkey patching safer and easier. Knowing that no one will accidently
overwrite my 'ext' method on String, I am more likely to go ahead and
add the method to String (where it belongs).
> | ... in that case I have no idea how it's supposed to work at parse
> |time.
>
> One possible idea is making symbols to belong to each namespace, like
> Common Lisp does, in that case,
>
> obj.foo(bar)
>
> should be work as
>
> obj.<namespace::foo>(bar)
>
> where <namespace::foo> means "symbol foo in that namespace", and its
> correspondence can be modified by (dynamic) method definitions.
>
>
I definitely like this thinking. As I see a selector proposal, it
basically works like this:
every method call has an associated namespace. that namespace defaults
to the Parsing Default for that file.
if a namespace is set for a file, then THAT namespace will be the
Parsing Default for that file.
of course, this means that namespaces/selectors should just be names -
not modules.
When you start having modules as namespaces, then it gets really
complicated to see what is actually scoped inside of another namespace
or not, since the parser can't know this.
And these are my opinions...
> - How would this interact with send?
I think it would be tricky to make it work with send unless send were
officially elevated in status a bit. Namespaces would not effect
"secondary calls" made by methods I directly call, and I consider send's
eventual invocation of a real target method to be a "secondary call".
In order for send to work, we'd have to special-case "send" behavior to
also know about namespaces active in the caller's scope. And I think
I've made it clear how I feel about caller-scoped data being implicitly
accessible for *any* purpose. Not only is it a real pain to implement
and optimize, it leads to greater complexity of the system overall. And
if that's not enough...it grants yet another special behavior to core
methods that's impossible to emulate with normal Ruby code. That's bad.
> - How would it interact with other introspection facilities?
> Kernel#method ?
> Basically, what is the runtime behavior of all metaprogramming and
> introspection features here...
I would imagine you'd want introspection APIs for "in effect"
namespaces, but I don't see that normal introspection of a target class
would produce different results. At least in my understanding of this,
the namespace is really a transparent layer on top of the call
protocol...not on top of the target type. You might liken it to Groovy's
categories, but without the messy downstream threading effects.
Of course, we could be describing different proposals here. As much as I
support Matz taking an implementation-agnostic approach to new features,
we do need to be able to implement those features *somehow* and I don't
see a clean way to do so with support for send and introspection at the
moment.
> - Do I understand correctly that this is supposed to be a lexical
> effect, and not a dynamic one?
Yes. Lexical and largely bound up at parse time. But how does the parser
know about runtime structures? .....
> ... in that case I have no idea how it's supposed to work at parse
> time. Basically, if you use modules as possible namespaces, there is no
> way the parser can know which methods are supposed to be possibly
> selector-based. And even so, you don't actually know in the above
> example that "str" is a String, so how do you know that Merb::String
> should be applied to that variable at parse time? The thread has said
> it's a lexical effect, but all the examples I see have confused lexical
> and dynamic effects, and parse time with runtime.
My prototype doesn't do it yet, but I would see a real implementation
using whatever a namespace resolves to at the point which "using" lives
in the code, so usually at top-level.
So at parse time:
1. The "using" is encountered, and the "locator" for the namespace is
saved at that point in the list of active namespaces
2. The list of active namespaces is from then on attached to parsed
calls, possibly by adding new node types to avoid encumbering existing
call logic with namespace behavior.
At runtime:
1. The using is encountered during execution, and the locator is fired.
From then on that locator points to a single namespace and is not
reevaluated.
2. The locators attached to methods later in that file are then
pre-populated with an appropriate namespace, and do not re-execute the
locator.
3. The namespaced methods then would all be guaranteed to use the same
namespace for calls.
There's no way to have it all be parse time since Ruby does not support
static declarations of modules or classes. And there's no way to make it
all at runtime without a large penalty, since all calls would have to
always be checking for namespaces, even in the majority of code that
doesn't use them.
Have a look at the patch; since you're familiar with JRuby it might
explain a bit about it. Note that I'm reevaluating the "locator" for the
namespace every time, but that's easily fixed.
- Charlie
They certainly could be names, or the namespaces could be declared in a
new way that the parser is more aware of. Using modules right now is
mostly for convenience.
> When you start having modules as namespaces, then it gets really
> complicated to see what is actually scoped inside of another namespace
> or not, since the parser can't know this.
My prototype is basically just overloading modules to use as a method
bag. In this sense it's similar to the "borrow" feature you added to
JRuby to steal methods from a target module or class.
As for the resolution of the namespace, I think doing it once when the
"using" is encountered at runtime would work fine.
- Charlie
Yeah, that shouldn't be hard; send_with_namespace would just explicitly
enlist in the exact same protocol that "namespaced calls" implicitly
enlist in.
- Charlie
Given that "Common List packages" is probably a fairly large topic, can
you provide a "Lisp eye for the Ruby guy" description of them or how
they differ from the current proposals? In my quick reading of them they
sound pretty similar to what I prototyped.
- Charlie
> We have all been using the words "namespace" and "selector" pretty
> much interchangeably in this thread.
Good call. I understand "selector" to mean "message selector", i.e.
the symbol used to identify a method during a message send.
"Namespace" is anything the divides a set of names into distinct
spaces (modules and classes in Ruby are namespaces). Selector
namespaces are a particular kind of namespace that allows the same
selector to select different methods, usually based on lexical
information (as opposed to dynamic runtime information).
In this thread, namespace probably refers specifically to selector
namespaces.
> I'm becoming a bit confused about talking about several selectors
> being in scope at the same time here. One of my points in the other
> mail I sent was that there is no real way to know what a namespace
> contains until runtime - and the parser needs to know at parse time
> - otherwise it can't choose the right namespace.
I think it only needs to know what the namespaces are at lex time.
You would still need to search the namespaces to find the method
definition (just as today we search the class hierarchy for the actual
method).
This could of course handle send and those other introspection things,
if we provide a way for a method to get access to the selectors that
were activated for invoking method node... Or make those methods
extra-special in the way Charles really dislikes. =)
Cheers
>
> -- Yehuda
>
> 2009/2/25 Ola Bini <ola....@gmail.com <mailto:ola....@gmail.com>>
Thanks Charles,
I think I've gotten most of my questions answered to some degree now.
There is definitely some varying opinions on how to do things, but now I
see how this could be done. I will crawl back under my rock now.
I regret not just doing this in the prototype, but I would see the
lex/parse time handling of namespaces as holding a "slot" that's
attached to parsed calls, and then at runtime that slot is filled with
the runtime structures necessary.
- Charlie
Very difficult. Perhaps we're thinking about namespaces differently? For
me, namespaces are simply hooking the call process to provide a
different method lookup channel. The fact that they're attached to a
given type is largely unrelated. And namespaces as described would not
affect downstream "secondary calls", so you couldn't namespace "each"
and have "map" honor that change. I consider "send"'s eventual call of
the target method to be a similar "secondary call".
Of course the namespace proposal could be modified to be bound to a
specific type or to affect downstream calls, but those are much larger
changes.
> |- How would it interact with other introspection facilities? Kernel#method ?
> | Basically, what is the runtime behavior of all metaprogramming and
> |introspection features here...
>
> and other introspection features as well.
Same reasons as above; if namespaces are just a call-protocol hook,
their effect on a given type is irrelevant for introspection purposes.
Yes, I may be namespacing calls to String#to_s to do something
different. But String doesn't suddenly define different methods as a result.
> One possible idea is making symbols to belong to each namespace, like
> Common Lisp does, in that case,
>
> obj.foo(bar)
>
> should be work as
>
> obj.<namespace::foo>(bar)
>
> where <namespace::foo> means "symbol foo in that namespace", and its
> correspondence can be modified by (dynamic) method definitions.
This is probably fine, so long as we don't go down the path of having to
reevaluate the namespace every time we want to make a namespaced call.
That would make it prohibitively expensive.
- Charlie