Better types without runtime checking

php.internals

r . gottesheim

8 years ago
There's always a lot of talk about types in the PHP community. Specifically, many developers want property types, generics, function signature types, union and intersection types, and more. Those talks (and RFCs) always end with the same result: "We can't do it because performance issues." Has there ever been a discussion about adding some of those features syntactically, but ignoring them during runtime? At least until someone finds a performant way to check them at runtime. That way we could have advanced type checking in our editors at least. The idea will sound familiar to TypeScript users. It works great for that language. I'm excited to hear your opinions.

Yasuo Ohgaki

8 years ago
On Tue, Jun 5, 2018 at 4:23 PM Rudolph Gottesheim <r.gottesheim@loot.at> wrote:
> There's always a lot of talk about types in the PHP community. > Specifically, many developers want property types, generics, function > signature types, union and intersection types, and more. Those talks > (and RFCs) always end with the same result: "We can't do it because > performance issues." > > Has there ever been a discussion about adding some of those features > syntactically, but ignoring them during runtime? At least until someone > finds a performant way to check them at runtime. That way we could have > advanced type checking in our editors at least. > > The idea will sound familiar to TypeScript users. It works great for > that language. > > I'm excited to hear your opinions. >
DbC style has no performance issue at all. In addition, you can perform much better validation than simple type checks. function f($username) { assert(is_string($username) && preg_match('\\A[a-z]\\z', $username); .... }
-- Yasuo Ohgaki yohgaki@ohgaki.net

Theodore Brown

8 years ago
One Tue, Jun 5, 2018 at 2:28 AM Yasuo Ohgaki <yohgaki@ohgaki.net> wrote: > On Tue, Jun 5, 2018 at 4:23 PM Rudolph Gottesheim <r.gottesheim@loot.at> > wrote: > >> There's always a lot of talk about types in the PHP community. >> Specifically, many developers want property types, generics, function >> signature types, union and intersection types, and more. Those talks >> (and RFCs) always end with the same result: "We can't do it because >> performance issues." >> >> Has there ever been a discussion about adding some of those features >> syntactically, but ignoring them during runtime? At least until someone >> finds a performant way to check them at runtime. That way we could have >> advanced type checking in our editors at least. >> >> The idea will sound familiar to TypeScript users. It works great for >> that language. >> >> I'm excited to hear your opinions. > > DbC style has no performance issue at all. In addition, you can perform > much better validation than simple type checks. > > function f($username) { > assert(is_string($username) && preg_match('\\A[a-z]\\z', $username); > .... > } Assertions are not a replacement for syntactical type declarations, since they cannot be checked until runtime. They are also more verbose, and don't help editors provide autocompletion when writing code. +1 to the possibility of a PHP static type checker which can be run independently from the runtime to support more advanced types without degrading performance.

Michael Morris

8 years ago
On Tue, Jun 5, 2018 at 2:22 AM Rudolph Gottesheim <r.gottesheim@loot.at> wrote:
> There's always a lot of talk about types in the PHP community. > Specifically, many developers want property types, generics, function > signature types, union and intersection types, and more. Those talks > (and RFCs) always end with the same result: "We can't do it because > performance issues." > > Has there ever been a discussion about adding some of those features > syntactically, but ignoring them during runtime? At least until someone > finds a performant way to check them at runtime. That way we could have > advanced type checking in our editors at least. > > The idea will sound familiar to TypeScript users. It works great for > that language. >
It's an interesting compromise, but I worry about the confusion it may sow, particularly for incoming programmers from languages that do enforce such things - such as Java.

Dan Ackroyd

8 years ago
> Has there ever been a discussion about adding some of those features > syntactically, but ignoring them during runtime? At least until someone > finds a performant way to check them at runtime. That way we could have > advanced type checking in our editors at least.
You can have that today: https://preprocess.io/ The main thing that would missing would be a mapping of source file + line to the running code, which would make debugging harder. cheers Dan

Rowan Collins

8 years ago
On 05/06/2018 08:22, Rudolph Gottesheim wrote:
> There's always a lot of talk about types in the PHP community. > Specifically, many developers want property types, generics, function > signature types, union and intersection types, and more. Those talks > (and RFCs) always end with the same result: "We can't do it because > performance issues." > > Has there ever been a discussion about adding some of those features > syntactically, but ignoring them during runtime? At least until > someone finds a performant way to check them at runtime. That way we > could have advanced type checking in our editors at least.
Hi, I believe this is part of the original aim of Hack-Lang: the additional type features of that language are not part of the run-time, but checked by a static analysis tool offline. A number of languages have similar setups - Python and Dart both have the types as offline and/or development-mode-only features. Indeed, PHP seems to have gone a very unusual route in having a dynamic language with type constraints which are effectively always-on assertions. One of the problems with moving PHP to this offline-checking model is that people will expect new type constraints to work like existing ones, so may be caught out by them not being checked at run-time. Worse, static checks may not be able to detect certain violations; I don't know much about Hack's model, but I understand it includes both different levels of strictness, and restrictions on dynamic language features - things like references, "variable variables", and non-object callables. I do think it's an interesting question to think about though. Regards,
-- Rowan Collins [IMSoP]

Ryan Jentzsch

8 years ago
Why would something like this not work? strict class MyClass { protected int $foo = 1; public string $bar = "strict keyword in front of class allows/enforces strict properties"; private string $isItReallyThatDifficult = "to implement this?"; } On Tue, Jun 5, 2018 at 4:17 PM, Rowan Collins <rowan.collins@gmail.com> wrote:

Rowan Collins

8 years ago
On 6 June 2018 at 07:14, Ryan Jentzsch <ryan.jentzsch@gmail.com> wrote:
> Why would something like this not work? > > strict class MyClass > { > protected int $foo = 1; > public string $bar = "strict keyword in front of class allows/enforces > strict properties"; > private string $isItReallyThatDifficult = "to implement this?"; > } >
The problem is not in defining what code has type annotations, it's what to do with them afterwards. The performance hit comes from the fact that *every* time you run: $foo->bar = $baz; The engine now needs to check whether $foo is a class with type constraints, and what the type constraint of bar is, and whether the type of $baz matches that constraint. The answer will be "no constraint" most of the time, but it doesn't know that until it checks, at run-time, every time. Nor is it just straight-forward assignments that would need to change, there are knock-on effects across the language, such as assignment by reference: strict class MyClass { public int $foo; public string $bar; } $a = new MyClass; $inst =& $a->foo; // If this is allowed... $inst = 'bad'; // ...then this needs to error $s = 'good'; $a->bar =& $s; // And if this is allowed... $s = 42; // ... then this needs to error The previous proposal for typed properties simply raised errors when you tried to use reference assignment with any typed property, which feels like an arbitrary restriction to me. The alternative is to allow *every variable in the language* to carry type constraint information. I mused some more on this here: http://rwec.co.uk/q/php-type-system In short, yes, it really is that difficult. Regards,
-- Rowan Collins [IMSoP]

Ryan Jentzsch

8 years ago
Thanks Rowan for the detailed explanation. I thought that leveraging `__get` / `__set` internally would have addressed many of the type checking issues that you mentioned. I've used __get/__set in userland to enforce property types and hoped that an internal C based solution would something that is reasonable to code and implement. Also, can anyone in internals respond to this inquiry? https://github.com/php/php-src/pull/1797#issuecomment-394434391 On Wed, Jun 6, 2018 at 4:00 AM Rowan Collins <rowan.collins@gmail.com> wrote:

Marco Pivetta

8 years ago
On Fri, Jun 8, 2018 at 9:29 AM, Ryan Jentzsch <ryan.jentzsch@gmail.com> wrote:
> Thanks Rowan for the detailed explanation. > I thought that leveraging `__get` / `__set` internally would have addressed > many of the type checking issues that you mentioned. > I've used __get/__set in userland to enforce property types and hoped that > an internal C based solution would something that is reasonable to code and > implement. > > Also, can anyone in internals respond to this inquiry? > https://github.com/php/php-src/pull/1797#issuecomment-394434391
Added type checking to __get and __set is one of the primary reasons for a downvote of the typed properties RFC ( https://wiki.php.net/rfc/typed-properties) Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/

Rowan Collins

8 years ago
On 8 June 2018 at 08:29, Ryan Jentzsch <ryan.jentzsch@gmail.com> wrote:
> Also, can anyone in internals respond to this inquiry? https://github.com/ > php/php-src/pull/1797#issuecomment-394434391 >
For ease of reference, that comment says:
> Is there a document somewhere that summarizes the reasons why this
proposal was rejected? Have any of the items been addressed since then? I would be happy to be proved wrong, but I think the answer is almost certainly "no, but feel free to write one". Documentation is notoriously the thing that gets put off most easily, and documentation *of a discussion* is just not going to be at the top of anyone's todo list. But, all the discussions are public - on that PR thread, and on the discussion of the RFC on this list (available through any of several archives) - so theoretically anyone could go through and summarise the key points. I know that's not what you want to hear, but it's just human nature - we had the discussion, reached a conclusion, and moved on with the next topic. Regards,
-- Rowan Collins [IMSoP]

Nikita Popov

8 years ago
On Fri, Jun 8, 2018 at 10:50 AM, Rowan Collins <rowan.collins@gmail.com> wrote:
> On 8 June 2018 at 08:29, Ryan Jentzsch <ryan.jentzsch@gmail.com> wrote: > > > Also, can anyone in internals respond to this inquiry? > https://github.com/ > > php/php-src/pull/1797#issuecomment-394434391 > > > > For ease of reference, that comment says: > > > Is there a document somewhere that summarizes the reasons why this > proposal was rejected? Have any of the items been addressed since then? > > I would be happy to be proved wrong, but I think the answer is almost > certainly "no, but feel free to write one". Documentation is notoriously > the thing that gets put off most easily, and documentation *of a > discussion* is just not going to be at the top of anyone's todo list. But, > all the discussions are public - on that PR thread, and on the discussion > of the RFC on this list (available through any of several archives) - so > theoretically anyone could go through and summarise the key points. > > I know that's not what you want to hear, but it's just human nature - we > had the discussion, reached a conclusion, and moved on with the next topic. >
From what I remember, the key points were: * No support for types on static properties. * No support for taking references to typed properties. * Performance: Feature was not *entirely* free if not used. FTR, Bob and me plan to bring this RFC back up for PHP 7.3 soon, with support for static properties and for references. A mostly finished implementation has been lying around for a while already, so we think it's time to wrap this up and give it another try. Nikita

Rowan Collins

8 years ago
On 8 June 2018 at 10:22, Nikita Popov <nikita.ppv@gmail.com> wrote:
> > FTR, Bob and me plan to bring this RFC back up for PHP 7.3 soon, with > support for static properties and for references. A mostly finished > implementation has been lying around for a while already, so we think it's > time to wrap this up and give it another try. > >
I presume you mean 7.4, since 7.3 is in alpha already, and this is quite a major change? But good to hear there's still will to work on this; I shall be interested to hear the details of where this will take the language. Regards,
-- Rowan Collins [IMSoP]

Pedro Magalhães

8 years ago
On Fri, Jun 8, 2018 at 1:28 PM Rowan Collins <rowan.collins@gmail.com> wrote:
> On 8 June 2018 at 10:22, Nikita Popov <nikita.ppv@gmail.com> wrote: > > > > > FTR, Bob and me plan to bring this RFC back up for PHP 7.3 soon, with > > support for static properties and for references. A mostly finished > > implementation has been lying around for a while already, so we think > it's > > time to wrap this up and give it another try. > > > > > I presume you mean 7.4, since 7.3 is in alpha already, and this is quite a > major change? > > But good to hear there's still will to work on this; I shall be interested > to hear the details of where this will take the language. > > Regards, > -- > Rowan Collins > [IMSoP] >
AFAIK, alpha1 was tagged but it still didn't branch off master. That'll only happen on July 17th. (https://wiki.php.net/todo/php73) With that said, basically any RFC that goes to vote before July 3rd should still be on time to make it into 7.3. Regards, Pedro

Rowan Collins

8 years ago
On 8 June 2018 at 15:36, Pedro Magalhães <mail@pmmaga.net> wrote:
> On Fri, Jun 8, 2018 at 1:28 PM Rowan Collins <rowan.collins@gmail.com> > wrote: > >> I presume you mean 7.4, since 7.3 is in alpha already, and this is quite a >> major change? >> > > AFAIK, alpha1 was tagged but it still didn't branch off master. That'll > only happen on July 17th. (https://wiki.php.net/todo/php73) > > With that said, basically any RFC that goes to vote before July 3rd should > still be on time to make it into 7.3. >
I'm partly just surprised because the list has been so quiet recently; is there actually a long list of previously undiscussed features that people are going to try to get into 7.3 at the last minute? Maybe I'm being over-cautious, but it feels like landing big features like this just before beta would make any testing of alpha releases rather pointless. Regards,
-- Rowan Collins [IMSoP]

Rasmus Schultz

8 years ago
On Tue, Jun 5, 2018 at 9:22 AM, Rudolph Gottesheim <r.gottesheim@loot.at> wrote:
> ... > Has there ever been a discussion about adding some of those features > syntactically, but ignoring them during runtime?
That's what Hack does, and one of the main reasons we dropped Hack in favor of PHP. PHP is a reflective language - if we add e.g. generics or property type-hints, omitting run-time reflection and/or run-time type-checks would be inconsistent with PHP as a language in general. Inconsistencies in a language are surprising and generally "bad" - if type-hints lead to run-time type-checks, it's natural to expect that to work predictably no matter where you type-hint. You shouldn't have to learn where or when type-hints actually work at run-time and where they don't. Similarly, if type-hints are reflected, it's natural to expect all type-hints to exist in the reflection model somewhere, anywhere that type-hinting is possible. You shouldn't have to search the manual to learn that type-hints aren't reflected for properties for some reason, perhaps because it just hasn't been implemented yet. Much of the "good" that we expect to be able to accomplish with generics, for example, won't be possible (or won't make sense) without being consistently reflected and run-time type-checked. For example, in a DI container, I'd expect to be able to do something like this: class Container { public function get<T>(?string $name): T { // ... } // ... } When calling get(), I'd expect a type-check for the the generic return-type T, to protect me against internal errors in the Container implementation - if it returns the wrong type, I want it to fail immediately at that point, rather than potentially exiting the call-stack that made this error traceable, making this extremely difficult to debug. Similarly, if we had property type-hints: class Foo { int $bar; } $foo = new Foo(); $foo->bar = "oops"; Here, I'd expect a type-check when assigning to $bar - omitting that would be extremely bad, as, very likely, by the time something else fails because this object is in an invalid state, we've exited the call stack where the bad value was assigned, making this potentially very difficult to debug. I'd also expect to be able to reflect on the property-types in a DI container, or perhaps in a class that maps form post-data to properties and performs conversions, etc. I'm the author of the generics RFC and one of the property-type RFCs, and both of those RFCs call for both reflection and run-time type-checking, for the sake of keeping the language consistent. Hack made some very regrettable decisions in this area, and ultimately those features were useful at design-time only, for IDE-support - but we have php-doc for that purpose, and in my opinion, adding those features before (or unless) we're willing/able to also make them work consistently with the language, does more harm than good. Making these features appear to be language features, when in fact that they're only implemented in the parser and ignored by the interpreter, would make for an extremely confusing experience.

Rowan Collins

8 years ago
Hi Rasmus, While I agree with your general point regarding consistency, I just want to take issue with sentence: On 11 June 2018 at 15:12, Rasmus Schultz <rasmus@mindplay.dk> wrote:
> Hack made some very regrettable decisions in this area, and ultimately > those features were useful at design-time only, for IDE-support - but > we have php-doc for that purpose, and in my opinion, adding those > features before (or unless) we're willing/able to also make them work > consistently with the language, does more harm than good.
As far as I know, every other language which has added type annotations to a dynamically typed language has done so either using an offline tool (treating them like compiler warnings), or as a debug-only run-time flag (treating them like assertions). The creator of "gradual typing", which is explicitly the basis of Python's type annotations, and at least implicitly used by many other languages, specifically stated that it was intended to be checked at compile-time, not run-time [1]:
> A *gradual type checker* is a type checker that checks, at compile-time,
for type errors in some parts of a program, but not others, as directed by which parts of the program have been annotated with types. Arguably, it is PHP which made the regrettable decision in adding run-time type hints in the first place. If we removed those, we could implement gradual typing, and maybe add an ability to reflect on the type annotations, without the headache of how this interacts with run-time performance. Nonetheless, we are where we are, so I agree that it would be bad now to add type annotations which did not behave at run-time like the existing ones. [1] http://wphomes.soic.indiana.edu/jsiek/what-is-gradual-typing/
-- Rowan Collins [IMSoP]

Yasuo Ohgaki

8 years ago
On Mon, Jun 11, 2018 at 11:32 PM, Rowan Collins <rowan.collins@gmail.com> wrote:
> Hi Rasmus, > > While I agree with your general point regarding consistency, I just want to > take issue with sentence: > > On 11 June 2018 at 15:12, Rasmus Schultz <rasmus@mindplay.dk> wrote: > > > Hack made some very regrettable decisions in this area, and ultimately > > those features were useful at design-time only, for IDE-support - but > > we have php-doc for that purpose, and in my opinion, adding those > > features before (or unless) we're willing/able to also make them work > > consistently with the language, does more harm than good. > > > > As far as I know, every other language which has added type annotations to > a dynamically typed language has done so either using an offline tool > (treating them like compiler warnings), or as a debug-only run-time flag > (treating them like assertions). The creator of "gradual typing", which is > explicitly the basis of Python's type annotations, and at least implicitly > used by many other languages, specifically stated that it was intended to > be checked at compile-time, not run-time [1]: >
Making "type hint" a "true type hint" is useful with DbC as well as static analysis tools. Perhaps, declare(strict_types=-1); // -1 indicates assert() type check. i.e. asset is disabled, no type checks. How it should behave with object would be debatable. Regards,
-- Yasuo Ohgaki yohgaki@ohgaki.net