Is there any way to redefine the `new' method?

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!

First of all, if you want a list or all symbols try:

Symbol.all_symbols

Next, if you want to define a method on the class object itself (i.e.
a class method) the you should try this:

class Symbol
  def Symbol.my_new(name)
    ...
  end
end

Symbol.my_new('foo')

Now, lastly, you should note that new has no implementation on Symbol.
This is deliberate. If you want to create a symbol from a string you
should use the String#to_sym method:

class Symbol
  def Symbol.my_new(name)
    name.to_sym
  end
end

If you want to get into the details of why all this is the way it is,
then please ask. If you want to name my_new to new go ahead but I
wouldn't count on it being called for symbols being created in literal
style or via String#to_sym.

Brian.

···

On Dec 13, 2007 9:35 AM, 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

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

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. 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.

If Symbol doesn't do the job for you, you might look at things like
Ara's multiton code

http://raa.ruby-lang.org/project/multiton/

···

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!

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

> 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

Cheers

robert

···

2007/12/13, Rick DeNatale <rick.denatale@gmail.com>:

On 12/13/07, pluskid <pluskid@gmail.com> wrote:

--
use.inject do |as, often| as.you_can - without end

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

···

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

--
use.inject do |as, often| as.you_can - without end

Thank you, Robert! I think that's really what I want! :slight_smile:

···

On Dec 14, 5:07 pm, "Robert Klemme" <shortcut...@googlemail.com> wrote:

2007/12/14, Robert Klemme <shortcut...@googlemail.com>:

> 2007/12/13, Rick DeNatale <rick.denat...@gmail.com>:
> > On 12/13/07, pluskid <plus...@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

another way:

module Scheme
  class Symbol
    @names = {}
    def self.new name
      @names[name.to_sym] ||= super
    end
    def initialize name
      @name = name.to_sym
    end
  end
end

foo1 = Scheme::Symbol.new(:foo)
foo2 = Scheme::Symbol.new(:foo)
bar1 = Scheme::Symbol.new(:bar)
bar2 = Scheme::Symbol.new(:bar)
puts ((foo1 == foo2) && (bar1 == bar2) && (bar1 != foo1))

···

--
Posted via http://www.ruby-forum.com/.