Does Ruby not support multiple "initialize" methods for a class?

Hi,

Q1 - Does Ruby not support multiple "initialize" methods for a class?

Q2 - If no is there a reason for this out of curiosity?

Q3 - What approach is recommended if this constraint does exist?

Cheers
Greg

I am not sure what you want to achieve here. There is nothing magic
about initialize; it is just a method that is called from new. Maybe
you're talking of multiple new methods instead of multiple initialize
methods?

new used to be magic; however, the magic has been moved, so these days
new is the same as

def Class.new(*args)
  object = allocate
  object.initialize(*args)
  object
end

with "allocate" being the 'magic' method to allocate new objects.
With this, you can easily create multiple new methods, too.

Eivind.

···

On 3/20/07, Greg Hauptmann <greg.hauptmann.ruby@gmail.com> wrote:

Q1 - Does Ruby not support multiple "initialize" methods for a class?

Q2 - If no is there a reason for this out of curiosity?

Q3 - What approach is recommended if this constraint does exist?

Greg Hauptmann wrote:

Hi,

Q1 - Does Ruby not support multiple "initialize" methods for a class?

No.

Q2 - If no is there a reason for this out of curiosity?

Method overloading isn't supported in general - it's not just constructors.

Q3 - What approach is recommended if this constraint does exist?

Default values and varargs, options hashes, or different class hierarchies. It depends on exactly what you're trying to achieve. Do you have an example?

···

--
Alex

ruby doesn't have method overloading. So no multiple initialize methods.

Out of curiosity, why you need multiple initialize methods in a
language like ruby?

···

On 3/20/07, Greg Hauptmann <greg.hauptmann.ruby@gmail.com> wrote:

Hi,

Q1 - Does Ruby not support multiple "initialize" methods for a class?

Q2 - If no is there a reason for this out of curiosity?

Q3 - What approach is recommended if this constraint does exist?

Cheers
Greg

You could use:

class Invitation
  attr_reader :id, :date, :user, :text, :email

  def Invitation.withId(id)
    return self.new(id)
  end

  def Invitation.withAll(id, date, user, email, text)
      return self.new(id, date, user, email, text)
  end

  def initialize(id = 0, date = '', user = '', email = '', text = '')
    super()
    @id = id
    @date = date
    @email = email
    @user = user
    @text = text
  end
end

then:

invitation = Invitation.withId(1)

or with all params.

···

On 3/20/07, Greg Hauptmann <greg.hauptmann.ruby@gmail.com> wrote:

Hi,

Q1 - Does Ruby not support multiple "initialize" methods for a class?

Q2 - If no is there a reason for this out of curiosity?

Q3 - What approach is recommended if this constraint does exist?

I have been using the following pattern to implement multiple
constructors for a class. It is only a variation of the already
proposed solutions, but by using "instance_eval" I could avoid having
to create separate new/initialize-like methods for each constructor.

/Johan Holmberg

···

On 3/20/07, Greg Hauptmann <greg.hauptmann.ruby@gmail.com> wrote:

Hi,

Q1 - Does Ruby not support multiple "initialize" methods for a class?

Q2 - If no is there a reason for this out of curiosity?

Q3 - What approach is recommended if this constraint does exist?

#-------------------------------
class Point

    def self.create_cartesian(x,y)
        allocate.instance_eval do
            @x = x
            @y = y
            self
        end
    end

    def self.create_polar(length,angle)
        allocate.instance_eval do
            @x = length * Math.cos(angle)
            @y = length * Math.sin(angle)
            self
        end
    end

end
#-------------------------------

I have a class that can be created from a couple of scenarios, and for each
the initial data to populate the class is different. I was going to create
two "initialize" methods with their own specific method signature to cover
off these two scenarios however I guess I will need to perhaps just create
two separate/normal methods for this. Perhaps then it would be like:

a = Klass.new
a.create_for_scenario_one (...)

Of course the only thing that the withId and withAll (*which in more
normal Ruby style should be with_id and with _all) do is throw an
error if you don't call them with the right number of parameters.

With the above code you can also write:

  Invitation.new
Invitation.new(42)
Invitation.new(99, '10/22/2006')
Invitation.new(35, '3/2/1907', 'Teddy Roosevelt')
Invitation.new(67, '10/12/2010', 'Arthur C. Clarke', 'TheMan@theFuture.org')
or
Invitation.new(88, '6/6/1945', 'Dwight Eisenhower', 'leader@us.mil',
'I like Ike')

Any of these work as defaults are provide dor all.

Another approach would be to use a Hash to provide 'keyword' arguments.

Invitation.new(:id => 20, :email => 'somene@somewhere.com')

The initialize method would need to pick the hash argument apart.

    def initialize(options={})
         # process options and set iv's etc.
    end

You could combine this with additional parameters before the hash parameter.

* Normal ruby style is to reserve 'camel case' for Globals (e.g. class
and module names) and use underscores as separators in variable names.
I keep tripping up on that when my old Smalltalk habits kick in.
It's not crucial, but when in Ruby do as the Rubyists do.

···

On 3/20/07, Emilio Tagua <miloops@gmail.com> wrote:

You could use:

class Invitation
  attr_reader :id, :date, :user, :text, :email

  def Invitation.withId(id)
    return self.new(id)
  end

  def Invitation.withAll(id, date, user, email, text)
      return self.new(id, date, user, email, text)
  end

  def initialize(id = 0, date = '', user = '', email = '', text = '')
    super()
    @id = id
    @date = date
    @email = email
    @user = user
    @text = text
  end
end

then:

invitation = Invitation.withId(1)

or with all params.

--
Rick DeNatale

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

I've heard it said -- and I tend to agree -- that regular use of
"patterns" is a sign that the language lacks something. I wonder if
that is the case with Ruby, as applies to multiple constructor behavior
implementation.

I say "wonder" because I really am not certain in this case.

···

On Wed, Mar 21, 2007 at 08:32:37AM +0900, johan556@gmail.com wrote:

On 3/20/07, Greg Hauptmann <greg.hauptmann.ruby@gmail.com> wrote:
>Hi,
>
>Q1 - Does Ruby not support multiple "initialize" methods for a class?
>
>Q2 - If no is there a reason for this out of curiosity?
>
>Q3 - What approach is recommended if this constraint does exist?
>

I have been using the following pattern to implement multiple
constructors for a class. It is only a variation of the already
proposed solutions, but by using "instance_eval" I could avoid having
to create separate new/initialize-like methods for each constructor.

--
CCD CopyWrite Chad Perrin [ http://ccd.apotheon.org ]
Leon Festinger: "A man with a conviction is a hard man to change. Tell
him you disagree and he turns away. Show him facts and figures and he
questions your sources. Appeal to logic and he fails to see your point."

Alle martedì 20 marzo 2007, Greg Hauptmann ha scritto:

I have a class that can be created from a couple of scenarios, and for each
the initial data to populate the class is different. I was going to create
two "initialize" methods with their own specific method signature to cover
off these two scenarios however I guess I will need to perhaps just create
two separate/normal methods for this. Perhaps then it would be like:

a = Klass.new
a.create_for_scenario_one (...)

You could just declare an initialize method which takes any number of
arguments, then detect the scenario according to the passed argument. For
example, assuming that in one case the first argument is a number and in the
second is a string, you could do:

class MyClass

def initialize(*args)
  if args[0].is_a? Numeric
   #perform initialization in first case
  elsif args[0].is_a? String
   #perform initialization in second case
  else raise TypeError
  end
end

end

I hope this helps

Stefano

Eivind had your answer. Use new/initialize for one version of your
constructor and then roll your own for the second version:

class Myclass

   def self.new2(*args)
     object = allocate
     object.initialize2(*args)
     object
   end

   def initialize2(*args)
     # other initialization code
   end
end

Gary Wright

···

On Mar 20, 2007, at 8:26 AM, Greg Hauptmann wrote:

I have a class that can be created from a couple of scenarios, and for each
the initial data to populate the class is different. I was going to create
two "initialize" methods with their own specific method signature to cover
off these two scenarios however I guess I will need to perhaps just create
two separate/normal methods for this. Perhaps then it would be like:

a = Klass.create_scenario_one(...)
b = Klass.create_scenario_two(...)

That's the more common idiom in Ruby.

-austin

···

On 3/20/07, Greg Hauptmann <greg.hauptmann.ruby@gmail.com> wrote:

I have a class that can be created from a couple of scenarios, and for each
the initial data to populate the class is different. I was going to create
two "initialize" methods with their own specific method signature to cover
off these two scenarios however I guess I will need to perhaps just create
two separate/normal methods for this. Perhaps then it would be like:

a = Klass.new
a.create_for_scenario_one (...)

--
Austin Ziegler * halostatue@gmail.com * http://www.halostatue.ca/
               * austin@halostatue.ca * You are in a maze of twisty little passages, all alike. // halo • statue
               * austin@zieglers.ca

you can use class methods to return objects, as others have mentioned, but it
also extremely easy to do your own function signature matching in ruby

     harp:~ > cat a.rb
     class C
       def initialize *argv, &block
         case argv
         when match(Fixnum)
           initialize_from_int argv[0]
         when match(Fixnum, Float)
           initialize_from_int_float argv[0], argv[1]
         else
           raise ArgumentError, argv.inspect
         end
       end

       def initialize_from_int int
         @int = int
         @float = 0.0
       end

       def initialize_from_int_float int, float
         @int = int
         @float = float
       end

       class Pattern
         def initialize template
           @template = template
         end
         def === objects
           return false unless @template.size == objects.size
           @template.each_with_index do |pat, idx|
             return false unless pat === objects[idx]
           end
           return true
         end
       end
       def match *template
         Pattern.new template
       end
     end

     p C.new(42)
     p C.new(42,42.0)

     harp:~ > ruby a.rb
     #<C:0xb75cff08 @float=0.0, @int=42>
     #<C:0xb75cfcd8 @float=42.0, @int=42>

however, function signature overloading is very confusing when mixed with
default parameters. most people simply ignore this and live with the bugs in
c++.

regards.

-a

···

On Tue, 20 Mar 2007, Greg Hauptmann wrote:

I have a class that can be created from a couple of scenarios, and for each
the initial data to populate the class is different. I was going to create
two "initialize" methods with their own specific method signature to cover
off these two scenarios however I guess I will need to perhaps just create
two separate/normal methods for this. Perhaps then it would be like:

a = Klass.new
a.create_for_scenario_one (...)

--
be kind whenever possible... it is always possible.
- the dalai lama

> >Hi,
> >
> >Q1 - Does Ruby not support multiple "initialize" methods for a class?
> >
> >Q2 - If no is there a reason for this out of curiosity?
> >
> >Q3 - What approach is recommended if this constraint does exist?
> >
>
> I have been using the following pattern to implement multiple
> constructors for a class. It is only a variation of the already
> proposed solutions, but by using "instance_eval" I could avoid having
> to create separate new/initialize-like methods for each constructor.

I've heard it said -- and I tend to agree -- that regular use of
"patterns" is a sign that the language lacks something. I wonder if
that is the case with Ruby, as applies to multiple constructor behavior
implementation.

I say "wonder" because I really am not certain in this case.

Well, it's a choice. You can either have overloading or dynamic typing, but
not both. The "pattern" is just a matter of having different names for your
constructors. The simple, common case of a single constructor is supported
by the new/initialize separation, but multiple constructors are still
pretty easy. Note that Objective-C, another Smalltalk-like,
dynamically-typed language, uses the same pattern.

CCD CopyWrite Chad Perrin [ http://ccd.apotheon.org ]

--Greg

···

On Wed, Mar 21, 2007 at 10:09:36AM +0900, Chad Perrin wrote:

On Wed, Mar 21, 2007 at 08:32:37AM +0900, johan556@gmail.com wrote:
> On 3/20/07, Greg Hauptmann <greg.hauptmann.ruby@gmail.com> wrote:

Chad Perrin wrote:

I have been using the following pattern to implement multiple
constructors for a class. It is only a variation of the already
proposed solutions, but by using "instance_eval" I could avoid having
to create separate new/initialize-like methods for each constructor.

I've heard it said -- and I tend to agree -- that regular use of
"patterns" is a sign that the language lacks something. I wonder if
that is the case with Ruby, as applies to multiple constructor behavior
implementation.

I say "wonder" because I really am not certain in this case.

Hi Chad! I don't think Ruby "lacks" anything, it's just that it has a
different way of doing things. It's a trade-off between the flexibility
of a dynamic language and some other things like methods with same name
and different parameters (how are you going to distinguish between one
method that uses a string and an integer and another one that uses an
integer and a hash, if you don't have types defined at the parameter
level?). If you want to do things "like Java" or "like C++", then I
think you should open your mind and try to do things in a different way,
in a rubbish way.

Regards,

Nando

···

On Wed, Mar 21, 2007 at 08:32:37AM +0900, johan556@gmail.com wrote:

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

Chad Perrin wrote:

I've heard it said -- and I tend to agree -- that regular use of
"patterns" is a sign that the language lacks something. I wonder if
that is the case with Ruby, as applies to multiple constructor behavior
implementation.
  

I don't consider this to be a pattern, I consider overloading constructors an anti-pattern.

One of the few good Java books (among all the mediocre or even worse ones) is "Effective Java" by Joshua Bloch:

In the first chapter (actually chapter 2) in the first item Bloch advises Java programmers to consider using static factory methods instead of public and/or overloaded constructors. He lists the following advantages:

: One advantage of static factory methods is that, unlike constructors, they have names.
: If the parameters to a constructor do not, in and of themselves, describe the object being
: returned, a static factory with a well-chosen name can make a class easier to use and the
: resulting client code easier to read.
: [...]

The same is true in Ruby, if you use class methods (methods of a class's eigenclass). The workarounds in the initialize method even *look* code smelly. In Java overloading a constructor once or twice doesn't seem to be such a bad idea. But other programmers tend to just copy and paste another constructor below the others. Once you have a class that comes into the two digit range of constructors, it gets quite difficult to remember all the different possible permutations of types. (Yes, I had to maintain code like that.)

And I think, overloading in general isn't such a good idea either. It can lead to really funny semantic errors, if it teams up with Java's numeric type promotion for example. (Yeah, this happened to me, too.)

: A second advantage of static factory methods is that, unlike constructors, they are not
: required to create a new object each time they're invoked. This allows immutable classes
: (Item 13) to use preconstructed instances or to cache instances as they're constructed and to
: dispense these instances repeatedly so as to avoid creating unnecessary duplicate objects.

This is an advantage in Java only, in Ruby "constructors" already have this advantage, the are actually factory methods - and classes are nothing but object factories, objects that can make other objects. Ruby doesn't have "real" constructors like Java has. In Ruby a "constructor" is a simple method, defined in class Class and thus can be shadowed by defining a method in a class's eigenclass, where new objects can be allocated, configured, cached, and so on. These changes are also transparent for client code, which doesn't have to be changed to, e. g., profit from caching.

: A third advantage of static factory methods is that, unlike constructors, they can return
: an object of any subtype of their return type. This gives you great flexibility in choosing
: he class of the returned object.

This is a Java advantage only, because Ruby doesn't have to work around a rigid type system. You just have to take care not to screw up.

: One application of this flexibility is that an API can return objects without making their
: classes public. Hiding implementation classes in this fashion can lead to a very compact API.
: This technique lends itself to interface-based frameworks, where interfaces provide natural
: return types for static factory methods.

Can be done in Ruby as well for both "new" factory methods or better named factory methods.

: The main disadvantage of static factory methods is that classes without public or
: protected constructors cannot be subclassed.
: [...]

This isn't a disadvantage in Ruby, because private methods *can* be called from subclasses.

: A second disadvantage of static factory methods is that they are not readily
: distinguishable from other static methods. They do not stand out in API documentation in
: the way that constructors do. Furthermore, static factory methods represent a deviation from
: the norm. Thus it can be difficult to figure out from the class documentation how to instantiate
: a class that provides static factory methods instead of constructors. This disadvantage can be
: reduced by adhering to standard naming conventions.

This might be a disadvantage, but you should choose meaningful names for all methods and document accordingly anyway.

···

--
Florian Frank

This looks a lot nicer using "case":

class MyClass

  def initialize(*args)
    case args0
    when Numeric
      # ...perform numeric initialization
    when String
      # ...perform string initialization
    else
      raise TypeError
    end
  end

end

Of course, you can get a lot fancier with your "case" statement using
regular expressions, ranges, multiple matchers per case, etc. The sky's the
limit...

···

On 3/20/07, Stefano Crocco <stefano.crocco@alice.it> wrote:

You could just declare an initialize method which takes any number of
arguments, then detect the scenario according to the passed argument. For
example, assuming that in one case the first argument is a number and in
the
second is a string, you could do:

class MyClass

def initialize(*args)
  if args[0].is_a? Numeric
   #perform initialization in first case
  elsif args[0].is_a? String
   #perform initialization in second case
  else raise TypeError
  end
end

--
Avdi

In the interest of ridiculousness, how about using the multi gem for
this? :slight_smile:

require 'rubygems'
require 'multi'

class Foo
  def initialize(*args)
    multi(:init, Integer) { |i|
      puts 'integer used'
      # do integer stuff...
    }

    multi(:init, Float) { |f|
      puts 'float used'
      # do float stuff...
    }

    init(*args)
  end
end

···

On Mar 20, 10:42 am, ara.t.how...@noaa.gov wrote:

On Tue, 20 Mar 2007, Greg Hauptmann wrote:
> I have a class that can be created from a couple of scenarios, and for each
> the initial data to populate the class is different. I was going to create
> two "initialize" methods with their own specific method signature to cover
> off these two scenarios however I guess I will need to perhaps just create
> two separate/normal methods for this. Perhaps then it would be like:

> a = Klass.new
> a.create_for_scenario_one (...)

you can use class methods to return objects, as others have mentioned, but it
also extremely easy to do your own function signature matching in ruby

Gregory Seidman wrote:

Hi,

Q1 - Does Ruby not support multiple "initialize" methods for a class?

Q2 - If no is there a reason for this out of curiosity?

Q3 - What approach is recommended if this constraint does exist?

I have been using the following pattern to implement multiple
constructors for a class. It is only a variation of the already
proposed solutions, but by using "instance_eval" I could avoid having
to create separate new/initialize-like methods for each constructor.

I've heard it said -- and I tend to agree -- that regular use of
"patterns" is a sign that the language lacks something. I wonder if
that is the case with Ruby, as applies to multiple constructor behavior
implementation.

I say "wonder" because I really am not certain in this case.

Well, it's a choice. You can either have overloading or dynamic typing, but
not both.

Not true. Perl 6 will have optional static typing [1]. Sydney faked it with a Behavior you could import (that used the parser to handle it, not a compile time step, IIRC).

It's possible. The question is whether or not it's desirable. I vote yes, others say no, and still others are in favor of some sort of type inferencing. It's been brought up before - you can search the archives. :slight_smile:

Regards,

Dan

[1] The Beauty of Perl 6 Parameter Passing

···

On Wed, Mar 21, 2007 at 10:09:36AM +0900, Chad Perrin wrote:

On Wed, Mar 21, 2007 at 08:32:37AM +0900, johan556@gmail.com wrote:

On 3/20/07, Greg Hauptmann <greg.hauptmann.ruby@gmail.com> wrote:

Nando Sanchez wrote:

<snip>

Hi Chad! I don't think Ruby "lacks" anything, it's just that it has a different way of doing things. It's a trade-off between the flexibility of a dynamic language and some other things like methods with same name and different parameters (how are you going to distinguish between one method that uses a string and an integer and another one that uses an integer and a hash, if you don't have types defined at the parameter level?). If you want to do things "like Java" or "like C++", then I think you should open your mind and try to do things in a different way, in a rubbish way.

        ^^^^^^^

I think you mean "ruby-ish". :slight_smile:

Dan