ruby picture

RCR 264: Unifying dot (.) and colon (::) operators

Submitted by Malte (Sat Jul 10 07:07:15 UTC 2004)

Abstract

The . and :: operators could have the same meaning: getting a constant _or_ calling a method.

Problem

Having two similar operators is a source of confusion, too: "." is only for method calls, whilst "::" is for both.

Proposal

Both "." and "::" should, in my opinon, work as "::" does at the moment.

Analysis

You could do then:


    
  Math.PI
  MyModule.MyClass.new

  

Implementation

It's obvious from the first letter of an id whether it is a constant/module/class or a method.
ruby picture
Comments Current voting

Method names can have the name that begins with capital letter. How should they be handled?

-- matz.


I would actually rather see them (. and ::) dis-unified: namely, . for methods (messages to receiver), :: for changing scope and retrieving constants. Calling a method as Klass::meth, just because the receiver is a class, seems to me to be an unnecessary special case.

-- David Black


@Matz: In my opinion there shouldn't be any methods beginning with a capital letter. Ruby distinguishes between method/variable names and constant/module names by their first letter, so why allow inconsistent exceptions? Maybe I should add that to my RCR, or write a further one about first letters. -- Malte


Malte: When you're saying "there shouldn't be any methods beginning with a capital letter', do you mean that we should change existing methods despite of the incompatibility?

-- matz.


Well, incompatibility isn't nice; however my answer is yes. And furthermore: Ruby shouldn't allow methods beginning with a capital letter at all, as it doesn't allow constants beginning with a lower case letter, too. Just for consistency. --Malte


Name restriction is just for variables (and constants). Ruby have not restrcted method names from the beginning. Hence no inconsistency exists. Do you think it is fair to break compatibity to introduce new "consistency" that you've just brought in?

If so, what should we do to existing methods that begin with capital letter?

-- matz.


Concerning existing methods that begin with a capital letter: Well, it depends. The only such methods I know of are Float(), String() etc., which I never use (because I cannot remember the differences between using these methods or just kindly asking my object to do the job, using to_f, to_s etc.). In fact I also don't like Float() and String() because they look exactly like class names - I have to look at the context in order to know what it is. If there had been a rule that the first letter of method names is always lowercase, these methods would probably have been called to_float, to_string, or similar.

However, it seems that you aren't that enthusiastic about my proposal, are you? --Malte


No. I didn't design variable names and method names to share same rule. If you want to change my mind, define "consistency" first and tell me its benifits.

-- matz.


Well, I'm not in favor or against this, but I'm not sure I understand the problem with constants and methods having the same name. Am I wrong thinking that this problema already exists for constants and methods with the same name in the current scope? -- gabriele renzi


A few comments. First, the way :: is currently implemented, I see no useful reason why it should work for method calls. As David pointed out, some people do use :: when making method calls on a class (I used to do this myself). I don't know where this habit came from, but I suspect it's left over from C++ which requires this syntax when calling static member functions without an object. It makes sense in C++, though, because in C++ classes are not objects.

Secondly, using :: to call methods could be useful. For example, it would be nice if I could write:

  class Foo
    BAR = 42
  end
  #
  def foo
    puts Foo::BAR
  end

then later change Foo to:

  class Foo
    remove_const :BAR
    #
    @@bar = 0
    def BAR
      @@bar += 1
      return @@bar
    end
  end

and still have foo() work as before. This doesn't work, though; instead to get this behavior I have to resort to using const_missing(). Maybe that's a good thing -- having a constant evaluate to a different object every time probably isn't a good idea (though perhaps it's useful for lazily-evaluating a constant).

While I can think of a use for :: to work for methods (iff the method name begins with an uppercase letter), I can't think of a use for . to work for constants. Accepting this RCR without finding such a use would be foolish, IMO.

Lastly, and I think this is getting a little off the subject of the RCR, the Float()/Integer()/etc. methods have always confused me. The to_s method makes sense to me; converting an object to a string representation is a common task, and every object has some string representation. But given a string, how do I convert back to the original type? The to_f/to_i methods don't allow me to do any error checking, and their names don't include the name of the class, so it doesn't make sense to me to use to_xxx as a general method for converting to an object of a particular type. I could instead use Float()/Integer()/etc. but everyone seems so fixed on defining a to_xxx method for their user-defined types that this also doesn't work unless we all agree to start writing both to_xxx methods and toplevel methods for our classes. If we did this, then I could write a nifty method similar to boost::lexical_cast<>:

  class Object
    def to_type(t)
      Kernel.send(t.name, self.to_s)
    end
  end
  #
  i = 42
  p f.to_type(Float) #=> 42

(not that converting from one type to another using a string as an intermediate representation is generally a good idea, but I do like the idea of being able to convert any one type to any other type without having to look up how to do it in the documentation, only to find that the library author has picked yet another representation for this conversion, since it's not standardized).

As for why Float()/Integer()/etc. confuse me (bet you thought I was rambling and forgotten I'd said that, eh?), what in their name indicates that these methods take a string as an argument and construct an object of the same type as their name out of it (or in the case of Integer, an instance of a derived type)? Better IMO would be Foo.from_s, since this at least indicates that I'm converting from a string. Better yet would be a general solution that lets me convert any object to any other type (I'm not looking for an implementation -- that is impossible -- just a good representation). I haven't given this enough thought to write an RCR yet, but maybe someone else will read this and come up with a good idea.

-- Paul Brannan


If you're going to write an RCR about a generalized conversion framework I'd vote for it :) But the point of, say, Integer() vs to_i() is that they have different behaviour. How do you unify the various behaviours (exception on error/default result on error/magic conversion like to_str())? Anyway Object.as(aClass) is much nicer imho :)

-- gabriele renzi


The problem I don't know how to solve with Object.as(aClass) -- which is the same as #to_type -- is defining the conversion to work with multiple types. Ruby only supports dispatching based on the type of the receiver, so to define two types that can both be converted from an Integer would require overriding Integer#as and calling the original if the type doesn't match, which is ugly, IMO. I could combine this with some sort of double-dispatch mechanism, but that's the part I haven't thought through well.

-- Paul Brannan

I was thinking about some kind of structure somewhere (i.e. a @@Transformations Hash in Object) that could old procedures to convert objects indexed by a double key (self.class and aClass). Actually I believe that having some kind of Graph datastructure could be better, so that we could have convertions beetween type A and C if A->B and B->C exist.

-- gabriele renzi


I think all of this would have to be called something other than "type", "to_type", etc., keeping in mind that #type is itself deprecated due to the inadequacy of an object's class name to describe its type.

-- David Black


Actually this (in my vision, can't say Paul agrees) is much more powerful than pure class conversion. You could have object_with_read.as(Readable) where the convertion engine includes a module, or could define a behaviour(an_io.as(CapitalizedWriter) will just wraps calls). This even works fine with object with singleton methods.

You could even assert properties of an object like: 13.as(Prime)#=>13;12.as(Prime) #=>raise an Exception. This would actually work like Common LISP's soft typing+Python's adaption protocol, practically killing the need for to_*, kind_of?,is_a? and reduce the use of respond_to?, yet giving to the developer better conversion service,crash-early behaviour and documentation value.But we're OT, maybe this should go to c.l.r ?

-- gabriele renzi


Strongly opposed 3
Opposed 0
Neutral 0
In favor 0
Strongly advocate 0
ruby picture

This RCR has been superseded by RCR 267.

ruby picture

Powered by .