2007/12/14, Robert Klemme <shortcutter / googlemail.com>:
> 2007/12/13, Rick DeNatale <rick.denatale / gmail.com>:
> > On 12/13/07, pluskid <pluskid / gmail.com> 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