Multiple constructors?

I’ve seen how Ruby uses an initialize() method as a constructor.

Can this method be overloaded? After playing with IRB I’m assuming it can’t?

Thanks very much again; Ruby and this list are great.

Christopher J. Meisenzahl CPS, CSTE
Senior Software Testing Consultant
Spherion
christopher.j.meisenzahl@citicorp.com

Actually, so far as I can tell, no method in Ruby can be overloaded.
(What looks like overloading is redefinition.)

Inasmuch as I’ve cared about overloading by type, I’ve handled the
dispatching myself. For good examples of this, look at my
initializers in Text::Format and MIME::Types. In all of these, I
handle a “copy constructor” case, a Hash case, and a NilClass case;
I also handle other types as appropriate.

-austin
– Austin Ziegler, austin@halostatue.ca on 2002.11.21 at 10.16.15

···

On Fri, 22 Nov 2002 00:06:36 +0900, christopher.j.meisenzahl@citicorp.com wrote:

I’ve seen how Ruby uses an initialize() method as a constructor.

Can this method be overloaded? After playing with IRB I’m assuming
it can’t?

Thanks very much again; Ruby and this list are great.

I’ve seen how Ruby uses an initialize() method as a constructor.

Can this method be overloaded? After playing with IRB I’m assuming it can’t?

Thanks very much again; Ruby and this list are great.

For better or worse, no method can be overloaded, unless you ask Guy for his
modified Ruby :slight_smile:

If you need to branch on type, you can do this

def initialize(value)
case value
when String then …
when Regexp then …
else …
end

If you need variable arguments, you can do

def initialize (value, *values)
process(value)
values.each do |v|

end
end

Then, of course, there’s default arguments:

def initialize(size=5, shape=SQUARE, colour=nil, params={})
@colour = nil || @colour || DEFAULT_COLOUR

end

Finally, if your method expects a String, do the following:

def initialize(s)
process(s.to_s) # Now it can handle numbers, etc, too.
end

(This doesn’t always feel right. Use your judgement in each case.)

So there are ways to handle the need for varying parameter lists (a la
overloaded methods). None of them are a perfect fit from the point of view of
some other languages, but it’s pretty rare that the above approaches can’t
provide a decent solution.

Note to FAQ maintainer: this proposed FAQ entry is in response to
ruby-talk:56440. Please examine any other responses if/when entering this.

Gavin

···

From: christopher.j.meisenzahl@citicorp.com

Ruby does not have overloading. As an alternative, you can do one of
the following:

  1. Define initialize() like this:
    class Foo
    def initialize(*args)
    @host = @port = @filename = nil
    if args.length == 2 then
    @host = args[0]
    @port = args[1]
    elsif args.length == 1 then
    @filename = args[0]
    else
    raise ArgumentError, “Invalid number of arguments”
    end
    end
    end
    You can interpret args however you wish; it’s an array containing all
    the arguments to your method. You can also do your own type checking
    if you wish.

  2. Define multiple constructors with different names:

    class Foo
    private_class_method :new

    # for lack of a better name
    def self.socket_new(host, port)
      obj = new()
      obj.initialize_with_host_and_port(host, port)
      return obj
    end
    
    # for lack of a better name
    def self.file_new(filename)
      obj = new()
      obj.initialize_with_filename(filename)
      return obj
    end
    
    def initialize
      @host = @port = @filename = nil
    end
    
    def initialize_with_filename(filename)
      @filename = filename
    end
    
    def initialize_with_host_and_port(host, port)
      @host = host
      @port = port
    end
    

    end

    the only thing I don’t like about this second method is that
    initialize_with_filename and initialize_with_host_and_port are both
    public methods; I don’t know any way to make them private
    (initialize() is always a private method).

Hope this gives you some ideas,

Paul

···

On Fri, Nov 22, 2002 at 12:06:36AM +0900, christopher.j.meisenzahl@citicorp.com wrote:

I’ve seen how Ruby uses an initialize() method as a constructor.

Can this method be overloaded? After playing with IRB I’m assuming it can’t?

In Ruby there are actually two methods that act as a constructor, the
new method of the class and the initialize method of the object. The new
method allocates a new object and then calls initialize on it.

If you want to define multiple constructors, define multiple “new”
methods on your class and have those call different “initialize” methods
on the object.

For example the following code will print:

“initialize 1”
“initialize 2”

class C
def C.new_1()
c = C.new()
c.initialize_1()
c
end

def C.new_2()
    c = C.new()
    c.initialize_2()
    c
end

def initialize_1()
    p "initialize 1"
end

def initialize_2()
    p "initialize 2"
end

end

c1 = C.new_1()
c2 = C.new_2()

Cheers,
Nat.

···

On Thu, 2002-11-21 at 15:06, christopher.j.meisenzahl@citicorp.com wrote:

I’ve seen how Ruby uses an initialize() method as a constructor.

Can this method be overloaded? After playing with IRB I’m assuming it can’t


Nat Pryce nat.pryce@b13media.com
B13media

Hello christopher,

Thursday, November 21, 2002, 6:06:36 PM, you wrote:

Can this method be overloaded?

you can overload yourself:

def initialize (*args)

case true
when args.size==1 then …
when args.size==2 and Array===args[1] then …

end
end

or use overload.rb, which autocreates this ‘case’ statement for you
automagically

···


Best regards,
Bulat mailto:bulatz@integ.ru

Hi –

  1. Define multiple constructors with different names:

    class Foo
    private_class_method :new

    # for lack of a better name
    def self.socket_new(host, port)
      obj = new()
      obj.initialize_with_host_and_port(host, port)
      return obj
    end
    
    # for lack of a better name
    def self.file_new(filename)
      obj = new()
      obj.initialize_with_filename(filename)
      return obj
    end
    
    def initialize
      @host = @port = @filename = nil
    end
    
    def initialize_with_filename(filename)
      @filename = filename
    end
    
    def initialize_with_host_and_port(host, port)
      @host = host
      @port = port
    end
    

    end

    the only thing I don’t like about this second method is that
    initialize_with_filename and initialize_with_host_and_port are both
    public methods; I don’t know any way to make them private
    (initialize() is always a private method).

Maybe you could do:

def self.my_new
obj = new
class << obj; public :other_initialize; end
obj.other_initialize
class << obj; private :other_initialize; end
obj
end

def other_initialize
# …
end
private :other_initialize

(give or take any hidden pitfalls I haven’t spotted :slight_smile:

David

···

On Fri, 22 Nov 2002, Paul Brannan wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

I would avoid this because it is not branching on type, but instead
branching on how an object implements its type, which should be of no
concern to other objects. For example, this code would break if I passed
it an object that acted exactly like a Regexp but was not an instance of
the Regexp class. That object would have the same type as objects of
class Regexp, but would have a different implementation. See
http://www.rubygarden.org/ruby?TypesInRuby for a more detailed
explanation.

If you want to have different constructors for different argument types,
define different constructors on your class. Your code will then be
both more flexible and more readable. E.g. If you had a class that
could be created with either a String and a Regexp you would define
constructors new_from_string(a_string) and new_from_regexp(a_regexp).

Cheers,
Nat.

···

On Thu, 2002-11-21 at 15:27, Gavin Sinclair wrote:

If you need to branch on type, you can do this

def initialize(value)
case value
when String then …
when Regexp then …
else …
end


Nat Pryce nat.pryce@b13media.com
B13media

I just thought of a solution:

class Foo
public
def self.new_with_socket(host, port)
return new(:initialize_with_host_and_port, host, port)
return obj
end

def self.new_with_filename(filename)
  return new(:initialize_with_filename, filename)
end

private
private_class_method :new

def initialize(init_method, *args, &block)
  @host = @port = @filename = nil
  self.send(init_method, *args, &block)
end

def initialize_with_filename(filename)
  @filename = filename
end

def initialize_with_host_and_port(host, port)
  @host = host
  @port = port
end

end

Paul

···

On Fri, Nov 22, 2002 at 12:37:47AM +0900, Paul Brannan wrote:

the only thing I don’t like about this second method is that
initialize_with_filename and initialize_with_host_and_port are both
public methods; I don’t know any way to make them private
(initialize() is always a private method).

If you need to branch on type, you can do this

def initialize(value)
case value
when String then …
when Regexp then …
else …
end

I would avoid this because it is not branching on type, but instead
branching on how an object implements its type, which should be of no
concern to other objects. For example, this code would break if I passed
it an object that acted exactly like a Regexp but was not an instance of
the Regexp class. That object would have the same type as objects of
class Regexp, but would have a different implementation. See
http://www.rubygarden.org/ruby?TypesInRuby for a more detailed
explanation.

That’s fair enough - like I say, none of these approaches is a perfect fit -
but sometimes you can be pretty sure what you’re dealing with. It sort of
depends how far and wide your code is going to spread.

For better or worse, code like that above is seen in widely used libraries.
Even though most experienced rubyists would agree that it’s to be avoided,
sometimes it makes the most sense.

If you want to have different constructors for different argument types,
define different constructors on your class. Your code will then be
both more flexible and more readable. E.g. If you had a class that
could be created with either a String and a Regexp you would define
constructors new_from_string(a_string) and new_from_regexp(a_regexp).

I have an aesthetic problem with that, but again, sometimes it’s the most
desirable solution.

At the end of the day, if I find myself really needing Java-style overloaded
methods, I question my design.

···

From: “Nat Pryce” nat.pryce@b13media.com

On Thu, 2002-11-21 at 15:27, Gavin Sinclair wrote:

Cheers,
Nat.

If you need to branch on type, you can do this

def initialize(value)
case value
when String then …
when Regexp then …
else …
end

I would avoid this because it is not branching on type, but
instead branching on how an object implements its type, which
should be of no concern to other objects.
[…]

If you want to have different constructors for different argument
types, define different constructors on your class. Your code will
then be both more flexible and more readable. E.g. If you had a
class that could be created with either a String and a Regexp you
would define constructors new_from_string(a_string) and
new_from_regexp(a_regexp).

Mmmm. No. That unnecessarily exposes producer implementation details
to the consumer class. If the behaviours are what are important,
then the producer class should use #respond_to? and not the type. In
my cases where I respond to multiple types, I have done so because
I explicitly care about the types used because my code responds
slightly differently to different types in constructors, but only
internally. It should never matter to the consumer that the
responses are internally different – only that the outputs are the
same.

-austin
– Austin Ziegler, austin@halostatue.ca on 2002.11.21 at 12.01.45

···

On Fri, 22 Nov 2002 01:01:24 +0900, Nat Pryce wrote:

On Thu, 2002-11-21 at 15:27, Gavin Sinclair wrote:

This isn’t thread-safe. Consider:

  1. I create two objects
  2. The first constructor makes other_initialize public
  3. The second constructor makes other_initialize public
  4. The first constructor calls other_initialize
  5. The first constructor makes other_initialize private
  6. The second constructor will fail when it calls other_initialize

It’s also possible to call other_initialize from one thread while
another thread is constructing one of these objects.

Paul

···

On Fri, Nov 22, 2002 at 12:47:32AM +0900, dblack@candle.superlink.net wrote:

def self.my_new
obj = new
class << obj; public :other_initialize; end
obj.other_initialize
class << obj; private :other_initialize; end
obj
end

This won’t let me do something like

my_objs = input_objs.map {|i| MyClass.new(i)}

martin

···

Nat Pryce nat.pryce@b13media.com wrote:

If you want to have different constructors for different argument types,
define different constructors on your class. Your code will then be
both more flexible and more readable. E.g. If you had a class that
could be created with either a String and a Regexp you would define
constructors new_from_string(a_string) and new_from_regexp(a_regexp).

Hi –

def self.my_new
obj = new
class << obj; public :other_initialize; end
obj.other_initialize
class << obj; private :other_initialize; end
obj
end

This isn’t thread-safe. Consider:

  1. I create two objects
  2. The first constructor makes other_initialize public
  3. The second constructor makes other_initialize public
  4. The first constructor calls other_initialize
  5. The first constructor makes other_initialize private
  6. The second constructor will fail when it calls other_initialize

It’s also possible to call other_initialize from one thread while
another thread is constructing one of these objects.

I think the privacy is gate-kept separately by each object’s singleton
class:

class A
def talk; puts “hi”; end
private :talk
end

a = A.new
class << a; public :talk; end
a.talk # hi
b = A.new
b.talk # error: private method called

so the manipulation of the privacy should itself be private and
atomic.

David

···

On Fri, 22 Nov 2002, Paul Brannan wrote:

On Fri, Nov 22, 2002 at 12:47:32AM +0900, dblack@candle.superlink.net wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

That’s true but how often does one put arbitrary objects into a
collection and then use them to create something? And if you do, you
can easily put different types of objects into different collections.

Cheers,
Nat.

···

On Fri, 2002-11-22 at 10:17, Martin DeMello wrote:

Nat Pryce nat.pryce@b13media.com wrote:

If you want to have different constructors for different argument types,
define different constructors on your class. Your code will then be
both more flexible and more readable. E.g. If you had a class that
could be created with either a String and a Regexp you would define
constructors new_from_string(a_string) and new_from_regexp(a_regexp).

This won’t let me do something like

my_objs = input_objs.map {|i| MyClass.new(i)}


Nat Pryce nat.pryce@b13media.com
B13media

Oh, good point.

This discussion inspired me to go rewrite some code I wrote the day I
started learning Ruby. I decided to use instance_eval to call the
private initialize_with_xxx methods. Do you think that’s a reasonable
alternative?

Paul

···

On Fri, Nov 22, 2002 at 05:18:14AM +0900, dblack@candle.superlink.net wrote:

I think the privacy is gate-kept separately by each object’s singleton
class:

Well, in general, I think that if you have to select on type at some
point in your code, it’s neater to have it bundled into the constructor.
But now that you mention it, a better way to code the above might be
to have your class extend each object type it accepts with a method to
return the appropriately initialised instance, and avoid explicit type
dispatching altogether. Something like

my_objs = input_objs.map {|i| i.newmyclass}

as you suggested I do with the generators.

martin

···

Nat Pryce nat.pryce@b13media.com wrote:

On Fri, 2002-11-22 at 10:17, Martin DeMello wrote:

Nat Pryce nat.pryce@b13media.com wrote:

If you want to have different constructors for different argument types,
define different constructors on your class. Your code will then be
both more flexible and more readable. E.g. If you had a class that
could be created with either a String and a Regexp you would define
constructors new_from_string(a_string) and new_from_regexp(a_regexp).

This won’t let me do something like

my_objs = input_objs.map {|i| MyClass.new(i)}

That’s true but how often does one put arbitrary objects into a
collection and then use them to create something? And if you do, you
can easily put different types of objects into different collections.

Hi –

···

On Fri, 22 Nov 2002, Paul Brannan wrote:

On Fri, Nov 22, 2002 at 05:18:14AM +0900, dblack@candle.superlink.net wrote:

I think the privacy is gate-kept separately by each object’s singleton
class:

Oh, good point.

This discussion inspired me to go rewrite some code I wrote the day I
started learning Ruby. I decided to use instance_eval to call the
private initialize_with_xxx methods. Do you think that’s a reasonable
alternative?

It does seem more streamlined.

David


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav