Hello, all.
On one or two occasions I've encountered a problem when designing a base
class, where I wish to implement important set-up functionality in the
constructor, but am limited in how to ensure that the base constructor
functionality is unmolested whilst not restricting any inheriting classes
in their use of a constructor, as would be the case when using the final
keyword. I also don't feel 100% comfortable trusting the inheriting class
to call the parent constructor.
The idea I'm presenting for discussion here is to have some kind of
(keyword-driven?) hierarchy for constructors, whereby it's possible to have
for example sequential constructors, using syntax similar to:
... public sequential function __construct($ ...
In this scenario, constructors defined as sequential are, as the name
implies, called in sequence.
If the introduction of a new keyword is problematic, perhaps this same
behaviour could be enacted in cases where an inheriting class has a
constructor and the base class' constructor is defined as final i.e. rather
than causing an error, the final constructor is executed first, followed by
any other constructors in the hierarchy.
If this keyword is introduced, perhaps it this idea could also be expanded
to allow for sequential methods in general, which would behave similarly -
and may be a gateway to function overloading (or similar behaviour).
I'm open to people pointing out design flaws/implementation difficulties
with this; pull no punches.
Thanks.
For what it's worth, some time ago I prototyped something like that, without
modifying the language, in the form of a "Universal" class that you may
inherit from without defining __contruct/__destruct yourself.
See class definition below. In a using class (herarchy) define static methods
_pre_init, _post_init, _pre_exit and _post_exit. They will receive the
instance as their sole argument. __construct first calls _pre_init up the
inheritance chain, then _post_init on the way back down; likewise __destruct
calls _pre_exit and _post_exit
In the current form it is only moderately useful, because it doesn't support
constructor arguments. These throw up the question of how to pass them to the
"sequential" constructors, and what these would then usefully be able to do
with them...
best regards
Patrick
class Universal {
static private $_universal_pre_inits = [];
static private $_universal_post_inits = [];
static private $_universal_pre_exits = [];
static private $_universal_post_exits = [];
public function __construct() {
$class = get_class($this);
$initclass = $class.'°universal_init';
if (!class_exists($initclass, false))
Universal::_universal_inits_and_exits($class);
$initclass::_universal_init($this);
}
public function __destruct() {
$initclass = get_class($this).'°universal_init';
$initclass::_universal_exit($this);
}
final protected function _universal_inits_and_exits($class) {
$pre_inits = [];
$post_inits = [];
$pre_exits = [];
$post_exits = [];
$c = new ReflectionClass($class);
try {
$m = $c->getMethod('_pre_init');
if ($m->class == $class)
$pre_inits[] = [ $class, '_pre_init' ];
$m = $c->getMethod('_pre_exit');
if ($m->class == $class)
$pre_exits[] = [ $class, '_pre_exit' ];
} catch (Exception $e) {};
$parent = get_parent_class($class);
if ($parent && $parent != 'Universal') {
if (!class_exists($parent.'°universal_init', false))
Universal::_universal_inits_and_exits($parent);
$pre_inits = array_merge($pre_inits, self::
$_universal_pre_inits[$parent]);
$pre_exits = array_merge($pre_exits, self::
$_universal_pre_exits[$parent]);
$post_inits = array_merge($post_inits, self::
$_universal_post_inits[$parent]);
$post_exits = array_merge($post_exits, self::
$_universal_post_exits[$parent]);
}
try {
$m = $c->getMethod('_post_init');
if ($m->class == $class)
$post_inits[] = [ $class, '_post_init' ];
$m = $c->getMethod('_post_exit');
if ($m->class == $class)
$post_exits[] = [ $class, '_post_exit' ];
} catch (Exception $e) {};
self::$_universal_pre_inits[$class] = $pre_inits;
self::$_universal_pre_exits[$class] = $pre_exits;
self::$_universal_post_inits[$class] = $post_inits;
self::$_universal_post_exits[$class] = $post_exits;
$code = "class {$class}°universal_init extends {$class}{";
$code .= 'static protected function _universal_init($o){';
foreach (array_merge($pre_inits, $post_inits) as $init)
$code .= "{$init[0]}::{$init[1]}(\$o);";
$code .= '} static protected function _universal_exit($o){';
foreach (array_merge($pre_exits, $post_exits) as $exit)
$code .= "{$exit[0]}::{$exit[1]}(\$o);";
$code .= '}}';
eval($code);
}
}
On Wednesday 03 July 2013 13:45:57 Terence Copestake wrote:
Hello, all.
On one or two occasions I've encountered a problem when designing a base
class, where I wish to implement important set-up functionality in the
constructor, but am limited in how to ensure that the base constructor
functionality is unmolested whilst not restricting any inheriting classes
in their use of a constructor, as would be the case when using the final
keyword. I also don't feel 100% comfortable trusting the inheriting class
to call the parent constructor.The idea I'm presenting for discussion here is to have some kind of
(keyword-driven?) hierarchy for constructors, whereby it's possible to have
for example sequential constructors, using syntax similar to:... public sequential function __construct($ ...
In this scenario, constructors defined as sequential are, as the name
implies, called in sequence.If the introduction of a new keyword is problematic, perhaps this same
behaviour could be enacted in cases where an inheriting class has a
constructor and the base class' constructor is defined as final i.e. rather
than causing an error, the final constructor is executed first, followed by
any other constructors in the hierarchy.If this keyword is introduced, perhaps it this idea could also be expanded
to allow for sequential methods in general, which would behave similarly -
and may be a gateway to function overloading (or similar behaviour).I'm open to people pointing out design flaws/implementation difficulties
with this; pull no punches.Thanks.
I suggest you read this recent thread for related commentary.
http://www.serverphorums.com/read.php?7,712222
In there, I refer to your proposal as Contractual Call Super and I
find it an interesting concept that helps avoid the "advisory call
super" antipattern.
However --
If the introduction of a new keyword is problematic, perhaps this same
behaviour could be enacted in cases where an inheriting class has a
constructor and the base class' constructor is defined as final i.e. rather
than causing an error, the final constructor is executed first, followed by
any other constructors in the hierarchy.
-- this is a horrible idea. final
has distinct semantic meaning. It
means "cannot override." It doesn't mean "overrideable, but we'll call
any/all supers before/after we run yours." It's a BC break.
Fuggedaboutit.
The feature you want isn't in the language -- maybe it should be --
and you can't change the meaning of existing keywords.
-- S.
a new keyword is problematic, perhaps this same
behaviour could be enacted in cases where an inheriting class has a
constructor and the base class' constructor is defined as final i.e. rather
than causing an error, the final constructor is executed first, followed by
Most often if I need a super __construct(), I don't need it exactly
before or exactly after the bottom constructor but at a specific point
where I can setup super's input data and do stuff to its output.
What is the typical use case for calling all supers (before or after the
class' __construct itself) ?
--
Ralf Lang
Linux Consultant / Developer
Tel.: +49-170-6381563
Mail: lang@b1-systems.de
B1 Systems GmbH
Osterfeldstraße 7 / 85088 Vohburg / http://www.b1-systems.de
GF: Ralph Dehner / Unternehmenssitz: Vohburg / AG: Ingolstadt,HRB 3537
Most often if I need a super __construct(), I don't need it exactly
before or exactly after the bottom constructor but at a specific point
where I can setup super's input data and do stuff to its output.
I've most often seen, or reluctantly implemented, the Call Super
antipattern by putting the call at the bottom of sub code.
It's true that it's an antipattern regardless of "advisory" placement.
But to me, somewhere-in-the-middle is even smellier than usual, since
in human hands that's got to be more prone to failure.
I think if Contractual Call Super were considered for the language it
should have before and after variants, not just after. There isn't a
practical way to declare "at some point," is there, even if you wanted
it?
What is the typical use case for calling all supers (before or after the
class' __construct itself) ?
I can't think of a valid case in my memory for all supers. Typically
you mean to call the immediate super or the root super. Which is of
course the problem with the "advisory" form of this pattern, since
innocently inserting an intermediate class breaks your code.
If this feature were introduced, I see no reason for it to be allowed
to call more than one method for each occurrence of 'sequential'
keyword.
That is,
class A { sequentialBefore function __construct() {} }
class Asub extends A { function __construct() {} }
class Asubsub extends Asub { function __construct() {} }
would run A::__construct and then Asubsub::construct()
class A { sequentialBefore function __construct() {} }
class Asub extends A { sequentialBefore function __construct() {} }
class Asubsub extends Asub { function __construct() {} }
would run A::__construct, Asub::__construct, Asubsub::__construct
class A { sequentialBefore function __construct() {} }
class Asub extends A { sequentialAfter function __construct() {} }
class Asubsub extends Asub { function __construct() {} }
would run A::__construct, Asubsub::construct(), Asub::__construct
I'm not particularly cheering for this feature but enjoy thinking
about how it might work. If you see my notes on the previous thread
you can see I'm pretty adamant about Call Super being very bad
unless it's baked into the language in an interesting way.
-- S.
Most often if I need a super __construct(), I don't need it exactly
before or exactly after the bottom constructor but at a specific point
where I can setup super's input data and do stuff to its output.I've most often seen, or reluctantly implemented, the Call Super
antipattern by putting the call at the bottom of sub code.It's true that it's an antipattern regardless of "advisory" placement.
But to me, somewhere-in-the-middle is even smellier than usual, since
in human hands that's got to be more prone to failure.I think if Contractual Call Super were considered for the language it
should have before and after variants, not just after. There isn't a
practical way to declare "at some point," is there, even if you wanted
it?
No, that's why I am asking. Why is it an anti-pattern to call a known
super constructor? Not knowing the implementation details, I still know
the documented api (which is all a class should ask for).
What is the typical use case for calling all supers (before or after the
class' __construct itself) ?I can't think of a valid case in my memory for all supers. Typically
you mean to call the immediate super or the root super. Which is of
course the problem with the "advisory" form of this pattern, since
innocently inserting an intermediate class breaks your code.
This makes it more complicated. Do you mean next super or root super (or
root-1 as any class object is an "object" ?
I don't mean to disrupt any ideas. I just think how I would use or
expect to use the feature.
If this feature were introduced, I see no reason for it to be allowed
to call more than one method for each occurrence of 'sequential'
keyword.That is,
class A { sequentialBefore function __construct() {} }
class Asub extends A { function __construct() {} }
class Asubsub extends Asub { function __construct() {} }would run A::__construct and then Asubsub::construct()
class A { sequentialBefore function __construct() {} }
class Asub extends A { sequentialBefore function __construct() {} }
class Asubsub extends Asub { function __construct() {} }would run A::__construct, Asub::__construct, Asubsub::__construct
class A { sequentialBefore function __construct() {} }
class Asub extends A { sequentialAfter function __construct() {} }
class Asubsub extends Asub { function __construct() {} }would run A::__construct, Asubsub::construct(), Asub::__construct
I'm not particularly cheering for this feature but enjoy thinking
about how it might work. If you see my notes on the previous thread
you can see I'm pretty adamant about Call Super being very bad
unless it's baked into the language in an interesting way.
Not that it would be an argument but just for understanding: Do you know
any scripting language which has this? This would help me seeing the
idea as I know some of them.
--
Ralf Lang
Linux Consultant / Developer
Mail: lang@b1-systems.de
B1 Systems GmbH
Osterfeldstraße 7 / 85088 Vohburg / http://www.b1-systems.de
GF: Ralph Dehner / Unternehmenssitz: Vohburg / AG: Ingolstadt,HRB 3537
No, that's why I am asking. Why is it an anti-pattern to call a known
super constructor?
Guess I'd send you to my comments in the earlier thread as I think I
exhausted my ability to dismantle (advisory a..k.a. "pretty please")
Call Super there. Or "?call super antipattern".
Of course, most every antipattern began as a brand name for a common
approach, then once it graduated to a non-judgmental recommendation
the backlash began. The cynic in me knows that some patterns are
declared anti- so they can make developers feel cool about their code
even if it works no more efficiently, is no better documented, and is
only negligibly more manageable than "uncool" stuff. Nevertheless I
find the arguments against Call Super compelling even in a closed
environment where you control your own API. YMMV...
-- S.
Not that it would be an argument but just for understanding: Do you know
any scripting language which has this?
Dropping the "scripting" part... IIRC, C++ calls ctors without
arguments automatically like in my 'sequentialBefore' napkin sketch.
C# has language-level support for 'sequentialBefore' type logic in its
initialization list as well (which helps to smooth away the anti-ness
by keeping it out of the ctor code).
Also, here's a fascinatingly stupid bug-as-feature in AS3 (which I
don't remember anything about, having used it basically procedurally
along time ago) http://blogs.adobe.com/cantrell/archives/2008/09/be_careful_how.html.
(Call Super hoisting out of conditionals? How could that have seemed
like a good idea?)
So there is precedent for Contractual Call Super. I don't think the
above implementations are ideal, though, so if someone did go down
this path I think it should be (a) not automatic but dependent on
keyword(s); (b) flexible as far as executing before/after; (c)
deeply-thought-through as far as its impact on class hierarchies that
expand and contract over time.
-- S.
On Wed, Jul 3, 2013 at 11:10 PM, Sanford Whiteman
swhitemanlistens-software@cypressintegrated.com wrote:
Not that it would be an argument but just for understanding: Do you know
any scripting language which has this?Dropping the "scripting" part... IIRC, C++ calls ctors without
arguments automatically like in my 'sequentialBefore' napkin sketch.C# has language-level support for 'sequentialBefore' type logic in its
initialization list as well (which helps to smooth away the anti-ness
by keeping it out of the ctor code).Also, here's a fascinatingly stupid bug-as-feature in AS3 (which I
don't remember anything about, having used it basically procedurally
along time ago) http://blogs.adobe.com/cantrell/archives/2008/09/be_careful_how.html.
(Call Super hoisting out of conditionals? How could that have seemed
like a good idea?)So there is precedent for Contractual Call Super. I don't think the
above implementations are ideal, though, so if someone did go down
this path I think it should be (a) not automatic but dependent on
keyword(s); (b) flexible as far as executing before/after; (c)
deeply-thought-through as far as its impact on class hierarchies that
expand and contract over time.-- S.
--
Hello,
I've been thinking about this for a bit and even if you are right
about being nice to have a way to call a function always after
constructor. It could happen. You could have a DB class and in
constructor the user/pass/host/options and then a separate method for
init().
But more often than not, I want to be able to dictate what the program
is doing when I'm using libraries and so on that I'm extending, not
the libraries do stuff on their own. In most of the cases, I know much
better that the library author how I want to use the library for my
use case.
In your case, if you want to have a method always called then just
document it properly. Except for cases when you don't have access to
the source code, encrypted maybe?, I really don't see why just
documenting this isn't enough.
Think for a moment on the reverse of this: you have this nice feature
in the language, but then the people start abusing it. Could you
imagine yourself trying to use the latest XGreatLib v3.0 but you want
to skip some constructor due the fact that you know better. What do
you do then?
And yeah, maybe the library authors are not that careless to use such
an abusive functionality, but what if you have this in your code and
you decide to remove the new keyword 'super' or whatever so that you
can skip that constructor? Could it be that you just broke your whole
app because of this?
Please think of the downsides as well when thinking of new features
like this, the implications for the whole eco-system could be huge for
just wanting to skip some proper documentation.
Cheers.
Florin Patan
https://github.com/dlsniper
http://www.linkedin.com/in/florinpatan
I've been thinking about this for a bit and even if you are right
about being nice to have a way to call a function always after
constructor. It could happen. You could have a DB class and in
constructor the user/pass/host/options and then a separate method for
init().
But that's not Call Super. Call Super specifically refers to always
calling an overridden method of the same name (and usually same
signature in languages supporting overloading) in a base class
(usually the closest parent).
Let's not mix up concepts: the relationship between methods of the
same name in the a class hierarchy has specific semantic baggage, if
not a single agreed-upon meaning, thus how some people say
"you should always be able to call super"
while others say
"you should never just assume you can call super *and* should never
author an API that requires it"
and still others say
"you can't assume, but the requirement happens, and when it is
required the language should be able to enforce the
requirement."
But more often than not, I want to be able to dictate what the
program is doing when I'm using libraries and so on that I'm
extending, not the libraries do stuff on their own.
The question regarding Call Super isn't about libraries doing "stuff
on their own" vs. "stuff you dictate."
It's about "doing stuff on their own" vs. "dictating that you remember
to do stuff for bug-free operation, being unable to do that stuff on
their own."
Languages that have Call Super as a flexible, baked-in language
feature are able to contractually ensure Call Super if a superclass
author chooses to use the pattern. Doesn't mean you have to use it,
but it means you don't have to cross your fingers if you do use it.
(In the case of C++, you're always using the pattern if you have a
base ctor.)
Languages that don't support Call Super still have to support code
using the pattern in the real world, because not every user will use
the alternative Template Method due to seeming complexity (even if TM
is less prone to error). But those languages won't offer any way of
catching omissions in a subclass.
-- S.