Implementing Generics, and none scalar default properties.

php.internals

Dominic Grostate

10 years ago
A couple of things that would really help me in PHP are generics and the ability to set default values of properties to instances of objects or calls to static methods or functions (expressions in general). I don't know if either of these have experimental implementations already as I couldn't find any, but if it would add some interest I may be willing to tackle the source to help push it forward. Could you please advise me on any fatal barriers that might prevent this being worth while for me. Thank you. Dominic

Ben Scholzen 'DASPRiD'

10 years ago
Hey Dominic On 23.09.2015 16:53, Dominic Grostate wrote:
> A couple of things that would really help me in PHP are generics and the > ability to set default values of properties to instances of objects or > calls to static methods or functions (expressions in general). > > I don't know if either of these have experimental implementations already > as I couldn't find any, but if it would add some interest I may be willing > to tackle the source to help push it forward. > > Could you please advise me on any fatal barriers that might prevent this > being worth while for me. Thank you. > > Dominic
So far I only wrote up the RFC and kicked off a discussion here on the mailing list about it. As I wrote several times already, I'd appreciate someone tackling the implementation side to get a patch working! Cheers,
-- Ben Scholzen 'DASPRiD' Community Review Team Member | mail@dasprids.de Zend Framework | http://www.dasprids.de

Johannes Schlueter

10 years ago
Hi, Would be good to split complete different subjects to different threads anyways: On Wed, 2015-09-23 at 15:53 +0100, Dominic Grostate wrote:
> the > ability to set default values of properties to instances of objects or > calls to static methods or functions (expressions in general).
This causes questions on the order of execution and leads to waste of CPU time in case the constructor overrides the default value while not bringing much benefit over initialization in the constructor. It also is complicated regarding exceptions being thrown. Take the following code: <?php foo(); class A { protected $p1 = new Something(); private $p2 = new SomethingElse($this->p1, $this->p3); private $p3 = new Something(); private static $s1 = new StaticSomething(); public function __construct() {} } bar(); class B extends A { private $p4 = new Something(); public function __construct() { parent::__construct(); $this->p1 = new Whatever(); } } baz(); try { $b = new B(); } catch (Exception $e) { } ?> Some Questions: * Where, relative to foo(), bar() and baz() will StaticSomething's constructor be called? How do you capture exceptions from there? * When will $p2 be initialized, which properties can it access? (Only the ones initialized before? None?) * In B's constructor we're overwriting $p1, so $p2 will be initialized with the wrong value. I'm sure there are more questions ... doing this i the constructor makes things clear and explicit with about the same amount of code to write. johannes

Dominic Grostate

10 years ago
Hi, I see what you mean with static properties, that would be more difficult. I can answer on non-static however. * When will $p2 be initialized, which properties can it access? - On class instantiation. It won't have access to $this, because the object won't have been created yet. It will have access to the same entities as a static method. * In B's constructor we're overwriting $p1, so $p2 will be initialized with the wrong value. - It won't have access to $this. In effect they are pre-constructor assignments, just like any other property. Concerning reassignment on constructor leading to wasted code, I can only place faith in the effectiveness of the user to override those properties to NULL. Anyway sorry about the mixed post. I've mainly been working on implementing generics, so I have a separate thread somewhere for that. Dominic On 25 Sep 2015 1:09 pm, "Johannes Schlüter" <johannes@schlueters.de> wrote:

Stas Malyshev

10 years ago
Hi!
> ability to set default values of properties to instances of objects or > calls to static methods or functions (expressions in general).
That is what constructors are for. I.e. I can understand initializing static properties (though it gives a lot of potential for weird race conditions) but for non-statics constructor is exactly the place where initialization should happen.
-- Stas Malyshev smalyshev@gmail.com

Dominic Grostate

10 years ago
I'm thinking primarily of the benefit to base or abstract classes. For base classes which expect certain properties be set, they are exposed to the danger of remaining unset if the derived class overrides the constructor without calling the parent. This solution is analogous to: class Foo { private $timestamp; private function __construct() { $this->timestamp = new DateTime(); $this->init(); } public function getTimestamp() { return $this->timestamp; } protected function init() { } } class Bar extends Foo { protected function init() { // pseudo constructor of Bar } } The problem now though is that derived classes of Foo are unable to do the same thing, whereas instantiated default properties would allow: class Foo { private $timestamp = new \DateTime(); public function getTimestamp() { return $this->timestamp; } } class Baz extends Foo { private $uuid = Uuid::create(); public function getUuid() { return $this->uuid; } } class Bar extends Baz { public function __construct() { // real constructor of Bar } } $bar = new Bar(); var_dump($bar->getTimestamp()); var_dump($bar->getUuid()); With Traits this is even more necessary, because every class (derived or base) would need to set the properties in each constructor. On 25 September 2015 at 22:21, Stanislav Malyshev <smalyshev@gmail.com> wrote:

Stas Malyshev

10 years ago
Hi!
> I'm thinking primarily of the benefit to base or abstract classes. For
Both can have constructors.
> base classes which expect certain properties be set, they are exposed to > the danger of remaining unset if the derived class overrides the > constructor without calling the parent.
You should always call the parent ctor. There's pretty much no reason to ever not to call parent ctor.
-- Stas Malyshev smalyshev@gmail.com

Pavel Kouřil

10 years ago
On Fri, Sep 25, 2015 at 11:53 PM, Stanislav Malyshev <smalyshev@gmail.com> wrote:
> Hi! > >> I'm thinking primarily of the benefit to base or abstract classes. For > > Both can have constructors. > >> base classes which expect certain properties be set, they are exposed to >> the danger of remaining unset if the derived class overrides the >> constructor without calling the parent. > > You should always call the parent ctor. There's pretty much no reason to > ever not to call parent ctor. > > -- > Stas Malyshev > smalyshev@gmail.com >
Since there is no reason not to call parent ctor, maybe PHP should somehow make every class have default empty ctor, if it doesn't have one defined already - so you can write automatically parent::__construct() everywhere? Regards Pavel Kouřil

Stas Malyshev

10 years ago
Hi!
> Since there is no reason not to call parent ctor, maybe PHP should > somehow make every class have default empty ctor, if it doesn't have > one defined already - so you can write automatically > parent::__construct() everywhere?
I completely agree with you, that's why I submitted https://wiki.php.net/rfc/default_ctor. Unfortunately, it was rejected.
-- Stas Malyshev smalyshev@gmail.com

Dominic Grostate

10 years ago
An alternative that rfc might be to add a modifier to ctor, something like. required public function __construct(); A required function cannot be final, because the intention is to allow it to be overridden, but it must be called by the child impl at some point. What do u think? On 26 Sep 2015 8:59 am, "Stanislav Malyshev" <smalyshev@gmail.com> wrote:

Ben Scholzen 'DASPRiD'

10 years ago
By the way, Rasmus updated the RFC quite a bit, you guys may want to take a look at it again. On 26.09.2015 12:23, Dominic Grostate wrote:
> An alternative that rfc might be to add a modifier to ctor, something like. > > required public function __construct(); > > A required function cannot be final, because the intention is to allow it > to be overridden, but it must be called by the child impl at some point. > What do u think?
-- Ben Scholzen 'DASPRiD' Community Review Team Member | mail@dasprids.de Zend Framework | http://www.dasprids.de

Ryan Pallas

10 years ago
Hi Ben and Rasmus, On Thu, Jan 7, 2016 at 6:24 PM, Ben Scholzen 'DASPRiD' <mail@dasprids.de> wrote:
> By the way, Rasmus updated the RFC quite a bit, you guys may want to take > a look at it again. > >
I just have a couple questions about the rfc, but overall I think its great and I really hope it makes it into PHP! Note that the choice of the keyword is to indicate upper bounds is based on
> the rejection of perhaps more obvious alternatives - repurposing the > extends or implements keywords would be misleading, since they would work > precisely the same way; worse, permitting both keywords would render > consumer code invalid if an upper bound type provided by a library is > refactored between class and interface. Repurposing instanceof would also > be misleading, since the upper bound is checking the type-hint, not an > instance. >
Why not use "as" which is already the keyword used to define aliases? Probably of the form class foo<DateTimeInterface as DateType> I think this feels more natural, as its how we do aliasing with a use statement. Also, this way it follows the same style as other aliases "RealName as Alias". I'm not saying is doesn't work, but I wonder if you'd considered as and if so, why you decided against it. When autoloading is triggered e.g. by a new statement with a generic type,
> autoloading is triggered as normal, with only the class-name (without type > parameters) being supplied. >
In other words, a statement like new Map<int,string>() will trigger
> auto-loading of class Map.
What order are things autoloaded (or does it even matter)? For example, given the following - does it matter what order things are autoloaded? It feels like it would but I'm not sure what would get resolved first: class Gen<IDType, DisplayType, StorageType> {} class ListGenerator implements HtmlGenerator {} class FtpWriter extends FileIO {} new Gen<int, HtmlGenerator, FileIO>(123, new ListGenerator(), new FtpWriter()); Again, I'm not sure if it matters but would there be any problems (and this goes to the whole list, since your RFC says you're not a C coder) with the order of autoloading with relation to resolving validity of types? In your second example in the Trait section you have:
> class Box<T> > { > use Box<T>; > }
I think you meant to say "use Container<T>;" as that is the name of your trait. Again, really awesome RFC and I hope it can be implemented and accepted! ~Ryan

Björn Larsson

10 years ago
Den 2016-01-08 kl. 02:24, skrev Ben Scholzen 'DASPRiD':
> By the way, Rasmus updated the RFC quite a bit, you guys may want to > take a look at it again. > > On 26.09.2015 12:23, Dominic Grostate wrote: >> An alternative that rfc might be to add a modifier to ctor, something >> like. >> >> required public function __construct(); >> >> A required function cannot be final, because the intention is to >> allow it >> to be overridden, but it must be called by the child impl at some point. >> What do u think? >
Reading the "Patches and test section" I wonder if some leverage could be drawn from Hacks implementation of generics? Regards //Björn Larsson PS Then we of course have the famous https://github.com/ircmaxell/PhpGenerics to lean on ;-)