On Wed, Mar 07, 2007 at 09:01:17AM +0900, Daniel DeLorme wrote:
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:
-
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]
-
It is currently not possible to recognise all meta-programming
methods
via a simple pattern match on the method name
-
(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”)
-
Meta-programming method names in Ruby are inconsistent and hard to
remember
-
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.