initialize always

T

trans. (T. Onoma)

It is rather a common occurrence that I find myself creating a mixin module
that has instance vars in it, for example:

module Example
def add( ex )
@examples << ex
end
end

class X
include Examples
end

Now the class that includes this module requires @examples to be predefined.
But short of adding @examples = [] to the #initialize method of the class
(which would ruin the goal of using the module to begin with) one ends up
doing something like:

module Example
def add( ex )
( @examples || [] ) << ex
end
end

But this seems quite computationally wasteful to me. Another alternative is to
add an intialize to the module and a super call to the class:

module Example
def initialize
@examples = []
end
def add( ex )
@examples << ex
end
end

class X
include Examples
def initialize
super()
end
end

But again, by having to modify the class we're defeating the one of the basic
purposes of module "componentization": SOC.

So I was thinking/wondering if it were possible to have an a variant
initializer that _always_ calls super? it would make sense to have one for
both begin and end positions. So the above example becomes:

module Example
def initialize_pre
@examples = []
end
def add( ex )
@examples << ex
end
end

class X
include Examples
end

When X.new is called initialize_pre automatically proceeds the #initialize
method. #initialize_post would do the same for when the #initialize method
finishes. Of course these would automatically work their way up the entire
class/module chain.

BTW this is not necessarily the same as method wraps (consider the subtle
differences) --rather these are strictly callbacks of initialization.

Thoughts?
T.
 
T

trans. (T. Onoma)

On Saturday 27 November 2004 12:26 pm, trans. (T. Onoma) wrote:
| module Example
|     def add( ex )
|       ( @examples || [] ) << ex
|     end
|   end

Make that:

    ( @examples ||= [] ) << ex

T.
 
F

Florian Gross

trans. (T. Onoma) said:
So I was thinking/wondering if it were possible to have an a variant
initializer that _always_ calls super? it would make sense to have one for
both begin and end positions. So the above example becomes:

module Example
def initialize_pre
@examples = []
end
def add( ex )
@examples << ex
end
end

Rite will have AOP-style method:pre, :post and :wrap hooks. However I'd
also like to see a solution for this quite common problem before the
release of Ruby 2. Maybe there already is a good way of doing this that
I'm just not aware of.
 
T

trans. (T. Onoma)

| > So I was thinking/wondering if it were possible to have an a variant
| > initializer that _always_ calls super? it would make sense to have one
| > for both begin and end positions. So the above example becomes:
| >
| > module Example
| > def initialize_pre
| > @examples = []
| > end
| > def add( ex )
| > @examples << ex
| > end
| > end
|
| Rite will have AOP-style method:pre, :post and :wrap hooks. However I'd
| also like to see a solution for this quite common problem before the
| release of Ruby 2. Maybe there already is a good way of doing this that
| I'm just not aware of.

I doubt that's a good thing. To me pre, post and wrap are pretty much hacks.
And I do not believe these hooks will even work here. Consider:

module M
def initialize:pre
print "A"
end
def initialize
print "M"
end
end

class C
include M
def initialize
super
print "C"
end
end

C.new ; puts

what will be the output?

T.
 
S

Sam Stephenson

So I was thinking/wondering if it were possible to have an a variant
initializer that _always_ calls super? it would make sense to have one for
both begin and end positions. So the above example becomes:

module Example
def initialize_pre
@examples = []
end
def add( ex )
@examples << ex
end
end

class X
include Examples
end

When X.new is called initialize_pre automatically proceeds the #initialize
method. #initialize_post would do the same for when the #initialize method
finishes. Of course these would automatically work their way up the entire
class/module chain.

BTW this is not necessarily the same as method wraps (consider the subtle
differences) --rather these are strictly callbacks of initialization.

Thoughts?
T.

One way to get this behavior now would be to implement
Module#included, using it with Kernel.set_trace_func to watch
instantiation for the including class, and having it call your
initialize_pre and initialize_post methods.

But since it's possible to have only one tracer at a time, that seems
like a pretty inflexible solution.

Sam
 
T

trans. (T. Onoma)

Not sure I explained this well enough. Let me elaborate just a tad.

|
| module M
| def initialize:pre
| print "A"
| end
| def initialize
| print "M"
| end
| end
|
| class C
| include M
| def initialize
| super
| print "C"
| end
| end
|
| C.new ; puts
|
| what will be the output?

Of course the answer is

#-> AMC

But why? Now consider the slight change:

class C
include M
def initialize
print "C"
super
end
end

Is the result CAM or ACM ?

HTH,
T.
 
F

Florian Gross

trans. (T. Onoma) said:
Not sure I explained this well enough. Let me elaborate just a tad.

|
| module M
| def initialize:pre
| print "A"
| end
| def initialize
| print "M"
| end
| end
|
| class C
| include M
| def initialize
| super
| print "C"
| end
| end
|
| C.new ; puts
|
| what will be the output?

Of course the answer is

#-> AMC

I'm not sure it is that. I'm waiting for somebody that knows how this
will be handled in Rite to answer. I can see AMC being a valid answer of
course, but I am not sure if it will be the one Rite gives.
 
R

Robert Klemme

trans. (T. Onoma) said:
It is rather a common occurrence that I find myself creating a mixin module
that has instance vars in it, for example:

module Example
def add( ex )
@examples << ex
end
end

class X
include Examples
end

Now the class that includes this module requires @examples to be predefined.
But short of adding @examples = [] to the #initialize method of the class
(which would ruin the goal of using the module to begin with) one ends up
doing something like:

module Example
def add( ex )
( @examples || [] ) << ex
end
end

But this seems quite computationally wasteful to me. Another alternative is to
add an intialize to the module and a super call to the class:

module Example
def initialize
@examples = []
end
def add( ex )
@examples << ex
end
end

class X
include Examples
def initialize
super()
end
end

But again, by having to modify the class we're defeating the one of the basic
purposes of module "componentization": SOC.

So I was thinking/wondering if it were possible to have an a variant
initializer that _always_ calls super? it would make sense to have one for
both begin and end positions. So the above example becomes:

module Example
def initialize_pre
@examples = []
end
def add( ex )
@examples << ex
end
end

class X
include Examples
end

When X.new is called initialize_pre automatically proceeds the #initialize
method. #initialize_post would do the same for when the #initialize method
finishes. Of course these would automatically work their way up the entire
class/module chain.

BTW this is not necessarily the same as method wraps (consider the subtle
differences) --rather these are strictly callbacks of initialization.

Thoughts?
T.

I think I did an RCR on this matter but I can't find it. Maybe I was stuck
or it got lost somewhere. I'll have to dig into this at home. If I find
the info I'll get back. From what I remember basically the idea was to
automatically invoke super under certain circumstances. The idea was to
do that depending on the argument list and the presence of an explicite
"super".

Kind regards

robert
 
T

trans. (T. Onoma)

On Monday 29 November 2004 04:22 am, Robert Klemme wrote:
| I think I did an RCR on this matter but I can't find it. Maybe I was stuck
| or it got lost somewhere. I'll have to dig into this at home. If I find
| the info I'll get back. From what I remember basically the idea was to
| automatically invoke super under certain circumstances. The idea was to
| do that depending on the argument list and the presence of an explicite
| "super".

Cool. Let me know.

T.
 
R

Robert Klemme

Robert Klemme said:
trans. (T. Onoma) said:
It is rather a common occurrence that I find myself creating a mixin module
that has instance vars in it, for example:

module Example
def add( ex )
@examples << ex
end
end

class X
include Examples
end

Now the class that includes this module requires @examples to be predefined.
But short of adding @examples = [] to the #initialize method of the class
(which would ruin the goal of using the module to begin with) one ends up
doing something like:

module Example
def add( ex )
( @examples || [] ) << ex
end
end

But this seems quite computationally wasteful to me. Another
alternative
is to
add an intialize to the module and a super call to the class:

module Example
def initialize
@examples = []
end
def add( ex )
@examples << ex
end
end

class X
include Examples
def initialize
super()
end
end

But again, by having to modify the class we're defeating the one of
the
basic
purposes of module "componentization": SOC.

So I was thinking/wondering if it were possible to have an a variant
initializer that _always_ calls super? it would make sense to have one for
both begin and end positions. So the above example becomes:

module Example
def initialize_pre
@examples = []
end
def add( ex )
@examples << ex
end
end

class X
include Examples
end

When X.new is called initialize_pre automatically proceeds the #initialize
method. #initialize_post would do the same for when the #initialize method
finishes. Of course these would automatically work their way up the entire
class/module chain.

BTW this is not necessarily the same as method wraps (consider the subtle
differences) --rather these are strictly callbacks of initialization.

Thoughts?
T.

I think I did an RCR on this matter but I can't find it. Maybe I was stuck
or it got lost somewhere. I'll have to dig into this at home. If I find
the info I'll get back. From what I remember basically the idea was to
automatically invoke super under certain circumstances. The idea was to
do that depending on the argument list and the presence of an explicite
"super".

Finally today I found a sheet of paper while cleaning my desk. (It's good
to do that once in a while. :))

It hadn't made it to an RCR though. The suggestion would have been this:
if a Module defines method initialize without an argument list then
implicitely change that to be initialise(*a,&b) and implicitely add super
as first line in the method: Thus

module Foo
def initialize
@bar = 0
end
end

becomes

module Foo
def initialize(*a,&b)
super
@bar = 0
end
end

Note: identifiers for arguments and block may have to be generated to be
unique.

All other cases (i.e. combinations of with / without argument list and
with / without occurrence of "super" in the body of this method) should
remain unchanged, because they would cause too much hassle.

Pro: this change allows for easy initialization of instance variables
needed by modules even if there are multiple modules included:

class Base
def initialize(x)
@x = x
end
end

class Test < Base
include Foo
include Bar

def initialize(a, b)
super(a)
@b = b
end
end

What do others think?

Kind regards

robert
 
D

David A. Black

Hi --

Finally today I found a sheet of paper while cleaning my desk. (It's good
to do that once in a while. :))

It hadn't made it to an RCR though. The suggestion would have been this:
if a Module defines method initialize without an argument list then
implicitely change that to be initialise(*a,&b) and implicitely add super
as first line in the method: Thus

module Foo
def initialize
@bar = 0
end
end

becomes

module Foo
def initialize(*a,&b)
super
@bar = 0
end
end

Note: identifiers for arguments and block may have to be generated to be
unique.

All other cases (i.e. combinations of with / without argument list and
with / without occurrence of "super" in the body of this method) should
remain unchanged, because they would cause too much hassle.

Pro: this change allows for easy initialization of instance variables
needed by modules even if there are multiple modules included:

class Base
def initialize(x)
@x = x
end
end

class Test < Base
include Foo
include Bar

def initialize(a, b)
super(a)
@b = b
end
end

What do others think?

My initial reaction is that it's too magic for my taste... too much
written in "invisible ink". If I write: def meth; ...; end then I
want it to fail if it's called with arguments. But I'm also not
understanding the 'pro' point very thoroughly, or maybe I've just
never had this problem. I think I'm just being thick, but can you
explain a little more how/when/why this would be useful?


David
 
G

Glenn Parker

Robert said:
module Foo
def initialize
@bar = 0
end
end

becomes

module Foo
def initialize(*a,&b)
super
@bar = 0
end
end

Pro: this change allows for easy initialization of instance variables
needed by modules even if there are multiple modules included:

What do others think?

What happens when somebody naively updates the first version of module
Foo from above as follows?

module Foo
def initialize(default = 0)
@bar = default
end
end

It might seem like a safe enough change, but it wouldn't be.

I think you can't change the meaning of "initialize" that drastically,
but you could define a new "super_initialize" if it was really important.
 
R

Robert Klemme

Glenn Parker said:
What happens when somebody naively updates the first version of module
Foo from above as follows?

module Foo
def initialize(default = 0)
@bar = default
end
end

It might seem like a safe enough change, but it wouldn't be.

Good point.
I think you can't change the meaning of "initialize" that drastically,
but you could define a new "super_initialize" if it was really
important.

What would that do? The aim was to have a method that does initialization
but does not disturb the calling chain of initialize. Maybe it would be
better to have a new method (initialize_locally or whatever) that does not
call super and is called individually, i.e. for each class in the chain of
ancestors.

Kind regards

robert
 
R

Robert Klemme

David A. Black said:
Hi --



My initial reaction is that it's too magic for my taste... too much
written in "invisible ink". If I write: def meth; ...; end then I
want it to fail if it's called with arguments. But I'm also not
understanding the 'pro' point very thoroughly, or maybe I've just
never had this problem. I think I'm just being thick, but can you
explain a little more how/when/why this would be useful?

Well, often you write a class and mixin a module that needs some state.
Currently the only safe way to initialize that is to write it like this:

module Foo
def initialize(*a,&b)
super
@foo_state = "something"
end
end

If you do not define initialize for a module you end up writing accessors
like this all the time:

module Foo
def foo_state
@foo_state ||= "something"
end
end

Which is less performant and has slightly different semantics (just think
of the case that nil or false is a legal value for @foo_state).

So the idea was to safe some typing and change initialize methods without
arguments and without a super in them the way I described.

Another option would be to disallow arguments and super for module
initialize and to do the change (implicit super etc.), which has some
reasons in favour of it but may break a lot of existing code. If I was
designing module initialization that's probably the way I'd do it.
Alternatively we could have a special method local_initialize that is
called automatically for each class in #ancestors.

Kind regards

robert
 
T

trans. (T. Onoma)

On Wednesday 15 December 2004 09:47 am, Robert Klemme wrote:
| |
| > Hi --
| >
| > On Wed, 15 Dec 2004, Robert Klemme wrote:
| > > Finally today I found a sheet of paper while cleaning my desk. (It's
|
| good
|
| > > to do that once in a while. :))
| > >
| > > It hadn't made it to an RCR though. The suggestion would have been
|
| this:
| > > if a Module defines method initialize without an argument list then
| > > implicitely change that to be initialise(*a,&b) and implicitely add
|
| super
|
| > > as first line in the method: Thus
| > >
| > > module Foo
| > > def initialize
| > > @bar = 0
| > > end
| > > end
| > >
| > > becomes
| > >
| > > module Foo
| > > def initialize(*a,&b)
| > > super
| > > @bar = 0
| > > end
| > > end
| > >
| > > Note: identifiers for arguments and block may have to be generated to
|
| be
|
| > > unique.
| > >
| > > All other cases (i.e. combinations of with / without argument list and
| > > with / without occurrence of "super" in the body of this method)
|
| should
|
| > > remain unchanged, because they would cause too much hassle.
| > >
| > > Pro: this change allows for easy initialization of instance variables
| > > needed by modules even if there are multiple modules included:
| > >
| > > class Base
| > > def initialize(x)
| > > @x = x
| > > end
| > > end
| > >
| > > class Test < Base
| > > include Foo
| > > include Bar
| > >
| > > def initialize(a, b)
| > > super(a)
| > > @b = b
| > > end
| > > end
| > >
| > > What do others think?
| >
| > My initial reaction is that it's too magic for my taste... too much
| > written in "invisible ink". If I write: def meth; ...; end then I
| > want it to fail if it's called with arguments. But I'm also not
| > understanding the 'pro' point very thoroughly, or maybe I've just
| > never had this problem. I think I'm just being thick, but can you
| > explain a little more how/when/why this would be useful?
|
| Well, often you write a class and mixin a module that needs some state.
| Currently the only safe way to initialize that is to write it like this:
|
| module Foo
| def initialize(*a,&b)
| super
| @foo_state = "something"
| end
| end
|
| If you do not define initialize for a module you end up writing accessors
| like this all the time:
|
| module Foo
| def foo_state
| @foo_state ||= "something"
| end
| end
|
| Which is less performant and has slightly different semantics (just think
| of the case that nil or false is a legal value for @foo_state).
|
| So the idea was to safe some typing and change initialize methods without
| arguments and without a super in them the way I described.
|
| Another option would be to disallow arguments and super for module
| initialize and to do the change (implicit super etc.), which has some
| reasons in favour of it but may break a lot of existing code. If I was
| designing module initialization that's probably the way I'd do it.
| Alternatively we could have a special method local_initialize that is
| called automatically for each class in #ancestors.

Yes, I think your idea is a good one for what it accomplishes but not for how
it does so. It agree with David, it is too "magic". Nonetheless, your
analysis is right on, and I think something really ought to be done about
this. IMO, It happens far too often to continue to be overlooked.

I also like the implicit super notion. But I think the best way to achieve it
may be via an initialize callback. Barring that that your idea of an always
called local_initialize is, AFAICT, the remaining alternative --and perhaps
the better one anyways.

T.
 
T

trans. (T. Onoma)

On Wednesday 15 December 2004 09:47 am, Robert Klemme wrote:
| [snip]
|
| > > What do others think?
| >
| > My initial reaction is that it's too magic for my taste... too much
| > written in "invisible ink". If I write: def meth; ...; end then I
| > want it to fail if it's called with arguments. But I'm also not
| > understanding the 'pro' point very thoroughly, or maybe I've just
| > never had this problem. I think I'm just being thick, but can you
| > explain a little more how/when/why this would be useful?
|
| Well, often you write a class and mixin a module that needs some state.
| Currently the only safe way to initialize that is to write it like this:
|
| module Foo
| def initialize(*a,&b)
| super
| @foo_state = "something"
| end
| end
|
| If you do not define initialize for a module you end up writing accessors
| like this all the time:
|
| module Foo
| def foo_state
| @foo_state ||= "something"
| end
| end
|
| Which is less performant and has slightly different semantics (just think
| of the case that nil or false is a legal value for @foo_state).
|
| So the idea was to safe some typing and change initialize methods without
| arguments and without a super in them the way I described.
|
| Another option would be to disallow arguments and super for module
| initialize and to do the change (implicit super etc.), which has some
| reasons in favour of it but may break a lot of existing code. If I was
| designing module initialization that's probably the way I'd do it.
| Alternatively we could have a special method local_initialize that is
| called automatically for each class in #ancestors.

Yes, I think your idea is a good one for what it accomplishes but not for how
it does so. It agree with David, it is too "magic". Nonetheless, your
analysis is right on, and I think something really ought to be done about
this. IMO, It happens far too often to continue to be overlooked.

I also like the implicit super notion. But I think the best way to achieve it
may be via an initialize callback. Barring that that your idea of an always
called local_initialize is, AFAICT, the remaining alternative --and perhaps
the better one anyways.

T.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top