On Wed, Mar 07, 2007 at 09:01:17AM +0900, Daniel DeLorme wrote:
> Rick DeNatale wrote:
> >I must have missed that list of 5-6 problems, a quick re-read of the
> >thread doesn't provide an obvious list, but maybe I'm just not reading
> >it right.
> 
> - avoids ugliness like __send__
> - reduces chances of naming conflict (CgiRequest#method, Email#send)
> - allows simple pattern match to recognize present and future 
> meta-programming methods (used for forwarding of non-meta methods)
> - already half implemented.
> 
> ok, so that was 4, and not even all of them were problems. let's add:
> 
> - consistent naming makes methods easier to remember
> - mnemonic for public vs. private interface
> - avoid special keyword "class" which forces to use self.class

I don't think any of those are actually stated as problems. I hope you don't
mind me putting words into your mouth if I restate them for you:

1. __send__ is ugly

2a.Sometimes reasonable user-chosen method names override meta-programming
   methods on Object like #method, #send and #class which other programs may
   reasonably expect to be present and functional in their normal way for
   all objects

   [Aside: this even happens for built-in classes, e.g. UDPSocket#send]

or (stronger):

2b.Sometimes reasonable user-chosen method names override methods on
   Object and Kernel (e.g. CgiRequest#method vs Object#method, Email#send vs
   Object#send, Semester#class vs Object#class, MyObject#test vs Kernel#test)
   thereby hiding those methods

   [Still also true for UDPSocket#send]

3. It is currently not possible to recognise all meta-programming methods
   via a simple pattern match on the method name

4. (Not sure how to recast this as a problem. Is this a subset of problem
   3? e.g. "it is possible to recognise *some* meta-programming methods via
   a simple pattern match on the method name, but not all")

5. Meta-programming method names in Ruby are inconsistent and hard to
   remember

6. Method names do not clearly distinguish between "private" and "public"
   interfaces

7a.method name #class conflicts with keyword 'class', forcing use of
   self.class to invoke the method

or (stronger):

7b.keyword 'class' conflicts with method name #class and local variables,
   forcing use of klass = Foo and self.class

Do you agree?

> As Trans said, membership in a category like "metaprogramming" is not 
> the basis. The basis is in the 7 points above.

I disagree. Solutions to problems 2a, 3, 4, 5 hinge _crucially_ on an agreed
distinction between "normal" and "metaprogramming" methods. Problem 6 hinges
on an agreed distinction between "private and public interfaces"; perhaps
you mean the same thing here. (It's clearly not the same as Ruby's 'private'
and 'public' method modifiers)

Problems 1 and 2b are, IMO, ultimately a symptom of all method names on an
object falling into the same namespace. The only way consistent way to
separate them is by including a namespace as part of the method name. Using
the object's class name would be one solution:

   Object#object_class
   Object#object_send
   Object#object_private_methods
   Object#object_singleton_method_added
   ...

There is already a precedent here: Object#id was renamed to Object#object_id

However, a full solution to problem 2b would involve *all* methods on Object
being in a separate namespace. This quickly becomes stupid:

   Object#object_is_a?
   Object#object_nil?
   Object#object_eql?
   Object#object_to_s
   ...

Should it also apply to other built-in modules like Kernel (included in
every object) or Enumerable (included in many objects)?

   Kernel#kernel_puts
   Enumerable#enumerable_collect
   ... etc?

I think not. But if we choose some other basis for namespaces (e.g. "normal"
versus "metaprogramming") then again this crucially depends on recognising
which methods fall into which category, arguing this for each method.

Problem 7 is true of all keywords in the language, i.e. you can't have a
local variable or bare method name called 'if' or 'end'. It's a symptom of
keywords, local variables and receiverless method names falling into the
same namespace. I assume you don't want to tag these things Perl-style.

Since the set of reserved words in Ruby is very small, I don't think this is
a problem normally when choosing your own method names. But I agree that
it's unfortunate that Ruby has decided to use a name for a *built-in* method
which is also a reserved word.

From that point of view, I'd be happy to see #class changed to
#object_class, in the same way that #id was changed to #object_id

But what about send? It's already available in its own pseudo-namespace as
__send__. Should it instead be object_send? Perhaps. But whether the current
situation really constitutes "a problem" is a subjective matter, I feel.

Final point. If the requirement is that 'meta-programming' methods should be
available always on all objects, then I can offer a completely different
solution:

    # was: foo.class
    Object.class_of(foo)

    # was: foo.send(meth,*args)
    Object.send_to(foo, meth, *args)

    # was: foo.method(:bar)
    Object.method_of(foo, :bar)

That is, by making all these operations singleton methods of a module,
instead of (or as well as) instance methods which are present on all
objects, then there is never any chance of them being overriden by the
object itself. It's not very OO-like, but it *is* immune from this problem.

Making them module methods would also reduce the clutter in object instance
methods:

irb(main):001:0> class Foo; end; Foo.new.methods
=> ["dup", "hash", "private_methods", "extend", "nil?", "__send__",
"instance_eval", "tainted?", "class", "singleton_methods", "=~", "untaint",
"kind_of?", "object_id", "instance_variable_get", "respond_to?", "inspect",
"frozen?", "taint", "id", "public_methods", "equal?", "to_a", "method",
"clone", "protected_methods", "send", "freeze", "display",
"instance_variable_set", "type", "is_a?", "methods", "==", "instance_of?",
"===", "instance_variables", "__id__", "eql?", "to_s"]

(Incidentally, which of these are metaprogramming? What about "extend",
"taint", "freeze", "respond_to?")

Regards,

Brian.