From: Robert Klemme Date: 2007-12-14T18:07:35+09:00 Subject: Re: Is there any way to redefine the `new' method? 2007/12/14, Robert Klemme : > 2007/12/13, Rick DeNatale : > > On 12/13/07, pluskid wrote: > > > Hi! I'm writing a toy interpreter for scheme in Ruby. I created a > > > class to represent scheme Symbol. > > > And as the scheme Symbol, I want only one instance for symbols with > > > the same name. I try to > > > do it like this: > > > > > > class Symbol > > > alias orig_new new > > > def new(name) > > > @@symbols[name] ||= orig_new(name) > > > @@symbols[name] > > > end > > > end > > > > > > But I failed. It says `new' is undefined. Is there any way to do the > > > tricky? Thanks! > > > > Well. > > > > First, Ruby has a core class called Symbol, which is the class of > > those funny :abc thingies. You probably don't want to mess with it, > > and it might just already do what your are looking for. Note that you > > can't do Symbol.new symbol instances are created either as literals, > > or with conversion methods like "abc".to_sym > > Good point. > > > Second, Ruby probably does instance instantiation and initialization a > > bit differently than whatever language you might be familiar with. > > > > The new method which is an instance method of the class Class, is > > normally never overridden, and it really isn't intended to be. It > > effectively calls another method initialize to allocate the space for > > the object, and then calls initialize on the result passing the > > arguments of new. > > > > The way Class#new does this is a bit magical. You can't override > > allocate easily since ruby doesn't use normal method lookup to invoke > > it. > > In this case you don't need to override #allocate - #new is perfectly > ok (see below). > > > This works for ruby since, unlike many other OO languages, the > > space needed for an object doesn't depend on things how many instance > > variables it has, since these are acquired dynamically and found via a > > hash. > > It's not that difficult: > > $ ./scheme-symbols.rb > [134314620] > 09:32:48 ~/ruby > $ cat ./scheme-symbols.rb > #!/usr/bin/env ruby > > module Scheme > Symbol = Struct.new :name > > class Symbol > @names = Hash.new do |h, k| > k = k.dup.freeze > h[k] = __new(k) > end > > class << self > alias __new new > def new(name) > @names[name] > end > end > end > > end > > syms = (1..2).map { Scheme::Symbol.new "foo" } > p syms.map {|o| o.object_id}.uniq I had forgotton one #freeze: module Scheme Symbol = Struct.new :name class Symbol @names = Hash.new do |h, k| k = k.dup.freeze h[k] = __new(k).freeze end class << self alias __new new def new(name) raise ArgumentError, "not a string" unless String === name @names[name] end end end end Cheers robert -- use.inject do |as, often| as.you_can - without end