Dot versus double-colon

OK, I’ve been thinking (always dangerous after 11 pm).

Class-level entities can be accessed via a double-colon.
No big mystery:

File::open(…)
File::SEPARATOR

Happily, a class method can also be accessed via a dot:

File.open(…)

But a constant can’t:

File.SEPARATOR # syntax error

First, why is it this way?

Personally, I don’t like the :: anyway, as it reminds me
of C++.

By the way, for those who are wondering, this is possible:

class File
def self.SEPARATOR
File::SEPARATOR
end
end

File.SEPARATOR # “/”

Even though it looks almost like recursion. Hmm, that raises
the question of why/how this works.

And another issue is: One could write a little module that,
when included in a class, exposed all the class’s constants in
this way. Within five minutes I had it “almost” working. I’ll
bet someone in another timezone or with more caffeine could
have it working in three. Anyone?

Hal

Hal Fulton wrote:

Even though it looks almost like recursion. Hmm, that raises
the question of why/how this works.

And another issue is: One could write a little module that,
when included in a class, exposed all the class’s constants in
this way. Within five minutes I had it “almost” working. I’ll
bet someone in another timezone or with more caffeine could
have it working in three. Anyone?

Hal

module Dot
def Dot.append_features( mod )
mod.constants.each{|c| mod.class_eval( “def self.#{c}() #{mod}::#{c}
end” ) }
end
end

Hmm…you’re right, it is late and this isn’t well tested.

Michael

OK, I’ve been thinking (always dangerous after 11 pm).

Class-level entities can be accessed via a double-colon.
No big mystery:

File::open(…)
File::SEPARATOR

Happily, a class method can also be accessed via a dot:

File.open(…)

But a constant can’t:

File.SEPARATOR # syntax error

First, why is it this way?

File.open() is sending the message :open to object File. The same
cannot be said for the constant SEPARATOR.

Personally, I don’t like the :: anyway, as it reminds me
of C++.

I don’t like :: for methods, because . makes sense for method calls on
objects and classes, because … well, you know why (*).

:: makes sense for namespaces - as in constant resolution. It would
not bother me one bit should :: be deprecated for method calls.

By the way, for those who are wondering, this is possible:

class File
def self.SEPARATOR
File::SEPARATOR
end
end

File.SEPARATOR # “/”

Even though it looks almost like recursion. Hmm, that raises
the question of why/how this works.

I’m sure you realise now. It’s because you’ve defined a method :slight_smile:

And another issue is: One could write a little module that,
when included in a class, exposed all the class’s constants in
this way. Within five minutes I had it “almost” working. I’ll
bet someone in another timezone or with more caffeine could
have it working in three. Anyone?

I’m happy for methods and constants to be accessed via different
mechanisms (message passing vs namespace resolution) and for those
mechanisms to be syntactically distinct.

Hal

Cheers,
Gavin

(*) Classes are objects.

···

On Sunday, September 14, 2003, 3:04:40 PM, Hal wrote:

OK, I’ve been thinking (always dangerous after 11 pm).

Class-level entities can be accessed via a double-colon.
No big mystery:

File::open(…)
File::SEPARATOR

Happily, a class method can also be accessed via a dot:

File.open(…)

But a constant can’t:

File.SEPARATOR # syntax error

First, why is it this way?

I think because scoping must necessarily have its own notation - consider
the following:

class T; end

class C
class T; end

def T; end

def m
  C.new
  ::T
end

end

If you were to use a ‘.’ in place of the ‘::’ here, you’d end up with an
ambiguous expression, as the following is perfectly legal:

C.new
.T

There’s also the fact that if you eliminated double-colon in favor of dot
you’d basically have to force all methods to start with a lowercase letter:

class C
class T; end
def T; end
end

Should the method T in this case completely replace the class T? What about
all of the constants nested within T? Of course, since methods starting with
uppercase letters are generally frowned upon, that might not affect that
much code, but it would still be a pretty drastic change.

Now, as for using ‘::’ for calling singleton methods… well, as far as I
can tell, you can use it for calling any method, singleton or not:

irb(main):001:0> Object::new::to_s
=> “#Object:0x2ba4098
irb(main):002:0> Object::new::inspect
=> “#Object:0x2ba1770
irb(main):003:0> String::new::strip
=> “”
irb(main):004:0>

So, if one uses both double-colon and dot in one’s code for method access,
it is necessarily in an arbitrary fashion. For some reason that I do not
fully understand, I find using ‘::’ for class method access aesthetically
pleasing:

String::new

I tell myself that I like it because it makes the same code tell me more
about itself at a glance, but I’m not sure about that. I think it might have
more to do with the fact that it’s more consistent looking (to me) when I’m
chaining constants together to access a class method:

Test::Unit::UI::Console::TestRunner::run(suite)

Anyhow, that’s my current take on the matter, subject to vacillation at any
point in the future. I actually just switched over to doing things this way
a few months back after seeing it in someone else’s code.

Personally, I don’t like the :: anyway, as it reminds me
of C++.

Well, I didn’t use to like underscores in method names and local variables,
but now I find them quite appealing. After that experience I’ve determined
to not judge the notation of one language just because it’s like (or not
like) the notation of another language, unless the notation in question has
serious practical downsides. It wouldn’t do to have a language like C++ or
Java spoiling my Ruby bliss :slight_smile:

By the way, for those who are wondering, this is possible:

class File
def self.SEPARATOR
File::SEPARATOR
end
end

File.SEPARATOR # “/”

Even though it looks almost like recursion. Hmm, that raises the question
of
why/how this works.

From a bit of irb exploration it would appear that this works because ‘::’
will always bind to a constant in preference of a method, while ‘.’ is
guaranteed to bind to a method.

And another issue is: One could write a little module that, when
included in a
class, exposed all the class’s constants in this way. Within five minutes
I had it
“almost” working. I’ll bet someone in another timezone or with more
caffeine could
have it working in three. Anyone?

I guess it reminds me of writing a module to allow one to access all
under_score methods as camelCase: you can do it (love that Ruby!), but would
it just confuse folks who read your code? I think I would be confused.

It’s been fun thinking about this… thanks for the brain food, Hal!

Nathaniel

<:((><

···

Hal Fulton [mailto:hal9000@hypermetrics.com] wrote:

Michael Garriss wrote:

Hal Fulton wrote:

Even though it looks almost like recursion. Hmm, that raises
the question of why/how this works.

And another issue is: One could write a little module that,
when included in a class, exposed all the class’s constants in
this way. Within five minutes I had it “almost” working. I’ll
bet someone in another timezone or with more caffeine could
have it working in three. Anyone?

Hal

module Dot
def Dot.append_features( mod )
mod.constants.each{|c| mod.class_eval( “def self.#{c}()
#{mod}::#{c} end” ) }
end
end

Has to be included after all consts are defined…working on that…

Gavin Sinclair wrote:

First, why is it this way?

File.open() is sending the message :open to object File. The same
cannot be said for the constant SEPARATOR.

Certainly it could be said. In fact, there is no way to tell
by looking at it whether it is in fact a method or a constant.

And a constant could be viewed as a method that always returns
the same value. Uniform access, you know.

:: makes sense for namespaces - as in constant resolution. It would
not bother me one bit should :: be deprecated for method calls.

I’m accessing a value within an object. Sure, the value
happens to be constant. So what?

By the way, for those who are wondering, this is possible:

class File
def self.SEPARATOR
File::SEPARATOR
end
end

File.SEPARATOR # “/”

Even though it looks almost like recursion. Hmm, that raises
the question of why/how this works.

I’m sure you realise now. It’s because you’ve defined a method :slight_smile:

You missed my point. Since the method File.SEPARATOR can be referred
to by the syntax File::SEPARATOR, then how does the method know that
it is not calling itself recursively?

And another issue is: One could write a little module that,
when included in a class, exposed all the class’s constants in
this way. Within five minutes I had it “almost” working. I’ll
bet someone in another timezone or with more caffeine could
have it working in three. Anyone?

I’m happy for methods and constants to be accessed via different
mechanisms (message passing vs namespace resolution) and for those
mechanisms to be syntactically distinct.

I see what you mean. But I say: Why should a constant be anything
but a reader which happens always to return the same value?

Hal

Nathaniel Talbott wrote:

I think because scoping must necessarily have its own notation - consider
the following:

[snip]

These are the most compelling reasons I’ve seen yet. Thanks, Nathaniel.

It just goes to show that Matz was smarter 11 years ago than I am
today.

It’s been fun thinking about this… thanks for the brain food, Hal!

Not very nourishing, I’m afraid. :slight_smile:

Hal

Michael Garriss wrote:

Michael Garriss wrote:

Hal Fulton wrote:

Even though it looks almost like recursion. Hmm, that raises
the question of why/how this works.

And another issue is: One could write a little module that,
when included in a class, exposed all the class’s constants in
this way. Within five minutes I had it “almost” working. I’ll
bet someone in another timezone or with more caffeine could
have it working in three. Anyone?

Hal

module Dot
def Dot.append_features( mod )
mod.constants.each{|c| mod.class_eval( “def self.#{c}()
#{mod}::#{c} end” ) }
end
end

Has to be included after all consts are defined…working on that…

constant_added might help. and fyi, I was trying to use
define_method rather than eval’ing a string. wonder if
the techniques are interchangeable?

I’ll worry about it in the morning.

Hal

Gavin Sinclair wrote:

First, why is it this way?

File.open() is sending the message :open to object File. The same
cannot be said for the constant SEPARATOR.

Certainly it could be said. In fact, there is no way to tell
by looking at it whether it is in fact a method or a constant.

It “could” be said, but it can’t. The interpreter knows whether it’s
a constant, and the capital letter is more than just a convention as
well - even though SEPERATOR is a valid method name.

And a constant could be viewed as a method that always returns
the same value. Uniform access, you know.

Yes, it’s appealing. Sometimes I implement “constants” as methods,
because I want the return value to be regenerated each time. If a
constant is a hash, its innards can be modified, and it ain’t so
constant anymore. I suppose there’s :freeze…

So your statement above should be qualified: the constant would be a
method that always returns the same object, not neccesarily the same
value.

:: makes sense for namespaces - as in constant resolution. It would
not bother me one bit should :: be deprecated for method calls.

I’m accessing a value within an object. Sure, the value
happens to be constant. So what?

Sending a message is very different from accessing a value. I
acknowledge your intentions, though I’m happy the way things are, but
that fundamental needs to be kept in mind, IMO.

Just like attr_* are convenient sugar for literal accessor methods,
are you proposing that “CONSTANT = 1” could/should be sugar for
creating a method?

By the way, for those who are wondering, this is possible:

class File
def self.SEPARATOR
File::SEPARATOR
end
end

File.SEPARATOR # “/”

Even though it looks almost like recursion. Hmm, that raises
the question of why/how this works.

I’m sure you realise now. It’s because you’ve defined a method :slight_smile:

You missed my point. Since the method File.SEPARATOR can be referred
to by the syntax File::SEPARATOR, then how does the method know that
it is not calling itself recursively?

Presumably the interpreter looks for a constant before a method. This
may be good luck rather than good management, although the :: may have
something to do with it.

[…]

Cheers,
Gavin

BTW, you could make constants available through dot-notation using
:method_missing, couldn’t you?

···

On Sunday, September 14, 2003, 5:13:04 PM, Hal wrote:

And a constant could be viewed as a method that always returns
the same value. Uniform access, you know.

The problem you identified (infinite recursion) justifies the existence
of ::. Moreover, :: should be somewhat faster than a normal method
dispatch.

:: makes sense for namespaces - as in constant resolution. It would
not bother me one bit should :: be deprecated for method calls.

I’m accessing a value within an object. Sure, the value
happens to be constant. So what?

I agree w/ Gavin here, I’d be happy to drop the
SomeClass::singleton_method notation.

By the way, for those who are wondering, this is possible:

class File
def self.SEPARATOR
File::SEPARATOR
end
end

File.SEPARATOR # “/”

Even though it looks almost like recursion. Hmm, that raises
the question of why/how this works.

I’m sure you realise now. It’s because you’ve defined a method :slight_smile:

You missed my point. Since the method File.SEPARATOR can be referred
to by the syntax File::SEPARATOR, then how does the method know that
it is not calling itself recursively?

The constant takes precedence over the method when using ::, so no
recursion.

And another issue is: One could write a little module that,
when included in a class, exposed all the class’s constants in
this way. Within five minutes I had it “almost” working. I’ll
bet someone in another timezone or with more caffeine could
have it working in three. Anyone?

There’s a really obvious possible solution:

batsman@tux-chan:/tmp$ expand -t2 i.rb

if nil

class Module
alias_method :_old_method_missing, :method_missing
def method_missing(meth, *args, &block)
return const_get(meth) if const_defined? meth
_old_method_missing
end
end

end

or

module ExposeConstants
def self.extend_object(mod)
class << mod;
alias_method :_old_method_missing, :method_missing
end
super
end

def method_missing(meth, *args, &block)
return const_get(meth) if const_defined? meth
_old_method_missing
end
end

File.extend ExposeConstants

p File.SEPARATOR

batsman@tux-chan:/tmp$ ruby i.rb
“/”

Using method_missing has the following properties:

  • works for constants defined after .extend
  • it’s much slower than defining methods for the existent constants
    (that’s easy to write too).

I’m happy for methods and constants to be accessed via different
mechanisms (message passing vs namespace resolution) and for those
mechanisms to be syntactically distinct.

I see what you mean. But I say: Why should a constant be anything
but a reader which happens always to return the same value?

Speed? It isn’t probably the primary reason, however.

Another benefit is that Ruby can give you a better warning if you redefine
a constant (you’ll get warning: method redefined; discarding old Constant
in the other case).

···

On Sun, Sep 14, 2003 at 04:13:04PM +0900, Hal Fulton wrote:


_ _

__ __ | | ___ _ __ ___ __ _ _ __
'_ \ / | __/ __| '_ _ \ / ` | ’ \
) | (| | |
__ \ | | | | | (| | | | |
.__/ _,
|_|/| || ||_,|| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

We come to bury DOS, not to praise it.
– Paul Vojta, vojta@math.berkeley.edu

Hal Fulton wrote:

Michael Garriss wrote:

Michael Garriss wrote:

Hal Fulton wrote:

Even though it looks almost like recursion. Hmm, that raises
the question of why/how this works.

And another issue is: One could write a little module that,
when included in a class, exposed all the class’s constants in
this way. Within five minutes I had it “almost” working. I’ll
bet someone in another timezone or with more caffeine could
have it working in three. Anyone?

Hal

module Dot
def Dot.append_features( mod )
mod.constants.each{|c| mod.class_eval( “def self.#{c}()
#{mod}::#{c} end” ) }
end
end

Has to be included after all consts are defined…working on
that…

constant_added might help. and fyi, I was trying to use
define_method rather than eval’ing a string. wonder if
the techniques are interchangeable?

Not sure. For some reason I never use #define_method. I tend to get a
little #eval happy sometimes. It was just so exicting when I learned
about #eval that I have trouble putting it down. I’ll have to add
#define_method (and #constant_added for that matter) to my bag 'o tricks.

Michael

Mauricio Fernández wrote:

And a constant could be viewed as a method that always returns
the same value. Uniform access, you know.

The problem you identified (infinite recursion) justifies the existence
of ::. Moreover, :: should be somewhat faster than a normal method
dispatch.

Well, I guess I’m just being a little irrational here.

I just happen to dislike :: in every case.

I think what I’m saying is not “make constants into methods”
but “make the notations the same (and disallow methods named
the same as constants).”

But it’s unimportant and there are probably reasons for not
doing so.

The constant takes precedence over the method when using ::, so no
recursion.

Hmm. So if you wanted to recurse, you’d have to use the
dot notation.

Again it’s only an issue because Ruby allows a class to have
a method and a constant by the same name.

Here’s another thought question. Are all constants class-level?
Would a singleton constant attached to an object make sense?

There’s a really obvious possible solution:

batsman@tux-chan:/tmp$ expand -t2 i.rb

if nil

class Module
alias_method :_old_method_missing, :method_missing
def method_missing(meth, *args, &block)
return const_get(meth) if const_defined? meth
_old_method_missing
end
end

end

or

module ExposeConstants
def self.extend_object(mod)
class << mod;
alias_method :_old_method_missing, :method_missing
end
super
end

def method_missing(meth, *args, &block)
return const_get(meth) if const_defined? meth
_old_method_missing
end
end

File.extend ExposeConstants

p File.SEPARATOR

Very good. I’d have seen that if I hadn’t failed to see it.
How’s that for a tautology?

Hal

···

On Sun, Sep 14, 2003 at 04:13:04PM +0900, Hal Fulton wrote:

Michael Garriss wrote:

Hal Fulton wrote:

Michael Garriss wrote:

Michael Garriss wrote:

Hal Fulton wrote:

Even though it looks almost like recursion. Hmm, that raises
the question of why/how this works.

And another issue is: One could write a little module that,
when included in a class, exposed all the class’s constants in
this way. Within five minutes I had it “almost” working. I’ll
bet someone in another timezone or with more caffeine could
have it working in three. Anyone?

Hal

module Dot
def Dot.append_features( mod )
mod.constants.each{|c| mod.class_eval( “def self.#{c}()
#{mod}::#{c} end” ) }
end
end

Has to be included after all consts are defined…working on
that…

constant_added might help. and fyi, I was trying to use
define_method rather than eval’ing a string. wonder if
the techniques are interchangeable?

Not sure. For some reason I never use #define_method. I tend to get a
little #eval happy sometimes. It was just so exicting when I learned
about #eval that I have trouble putting it down. I’ll have to add
#define_method (and #constant_added for that matter) to my bag 'o tricks.

I think I meant constant_missing. Hmm, is there a constant_added??? If
not, maybe there should be.

But constant_missing might not be useful here. My brain is really
getting cloudy now.

I’m the same way. I’ve overused eval in the past (by some people’s
standards). But now we’ve got const_[gs]et, instance_variable_[gs]et,
define_method, and all that coolness. These often obviate the need
for eval.

Hal

Hal Fulton wrote:

Here’s another thought question. Are all constants class-level?
Would a singleton constant attached to an object make sense?

To reply to myself:

3.instance_eval { FOO = 555 }
puts 3.instance_eval { FOO } # 555

So it’s possible… even for a Fixnum, which can’t have
singletons. :0

Useful or recommended? Not that I can imagine.

Hal

Hal Fulton wrote:

I think I meant constant_missing. Hmm, is there a constant_added??? If
not, maybe there should be.

But constant_missing might not be useful here. My brain is really
getting cloudy now.

I’m the same way. I’ve overused eval in the past (by some people’s
standards). But now we’ve got const_[gs]et, instance_variable_[gs]et,
define_method, and all that coolness. These often obviate the need
for eval.

Just had to share this super ugly example from some of my #eval happy code:

def add_cols( *mods )
eval(<<CODE
class << self
[#{mods.join(‘,’)}].each do |mod|
name = mod.to_s.sub(‘Rextra::DB::Col::’,‘’)
eval(eval(“Rextra::DB::Col.new_col(name,eval(\”#{mod}::NEW_COL\"
))"))
end
def new( *args )
obj = super( *args )
methods.each do |m|
method(m).call(obj) if m =~ /^_new_col/
end
obj
end
end
CODE
)
end

Check out the four layers deep eval. Don’t ask…

Michael

   3.instance_eval { FOO = 555 }
   puts 3.instance_eval { FOO } # 555

svg% ruby -e '3.instance_eval { FOO = 555 }; puts 12.instance_eval { FOO }'
555
svg%

Guy Decoux

ts wrote:

“H” == Hal Fulton hal9000@hypermetrics.com writes:

3.instance_eval { FOO = 555 }
puts 3.instance_eval { FOO } # 555

svg% ruby -e ‘3.instance_eval { FOO = 555 }; puts 12.instance_eval { FOO }’
555
svg%

Thanks, Guy…

you.say(so_much) { without_using(any_English) }

Hal

“Hal Fulton” hal9000@hypermetrics.com wrote in message

you.say(so_much) { without_using(any_English) }

Guy.speaks(ruby)