On Wed, Jan 3, 2018 at 3:26 PM, Rowan Collins <rowan.coll...@gmail.com> wrote:
> Hi Michael, > > On 02/01/2018 10:35, Michael Morris wrote: > >> I would like to propose a clean way to add some strong typing to PHP in a >> manner that is almost fully backward compatible (there is a behavior >> change >> with PHP 7 type declarations). As I don't have access to the add RFC's to >> the wiki I'll place this here. >> > > Thanks for putting this together. Perhaps unlike Andreas, I think it is > good to look at typing changes as a unified framework, rather than > considering "typed properties", "typed variables", etc, as separate > concerns. If we don't, there is a real risk we'll end up making decisions > now that hobble us for future changes, or over-complicating things in one > area because we're not yet ready to make changes in another. > My thoughts exactly. PHP already has enough warts born of piecemeal design - a cursory look at the PHP string functions shows this very well. We have functions with haystack/needle and needle/haystack. Some function names are _ delimited, some aren't (or were meant to be camel cased but since PHP function labels aren't case sensitive), and so on. When I see an RFC based on types it worries me precisely because without a core plan of action we are inviting more language fragmentation. > > My own thoughts on the subject from a while ago are here: > http://rwec.co.uk/q/php-type-system In that post, I borrowed the term > "container" from Perl6 for the conceptual thing that type constraints are > stored against; in PHP's case, this would include variables, object > properties, class static properties, function parameters, and return > values. I think a good plan for introducing typing is one that considers > all of these as equals. > > That was one of the most enjoyable reads I've had in awhile and I can't think of anything there I disagree with. I'm still working through your references for how Python is handling things and the treatise on the nature of types. > The biggest issue with any proposal, though, is going to be performance. I > don't think this is an incidental detail to be dealt with later, it is a > fundamental issue with the way type hints in PHP have evolved. PHP is > extremely unusual, if not unique, in exclusively enforcing type constraints > at runtime. Other languages with "gradual typing" such as Python, Hack, and > Dart, use the annotations only in separate static analysers and/or when a > runtime debug flag is set (similar to enabling assertions). > > Has the bus already left the station forever on this? I think it's clear that what we are discussing here can't go into effect before PHP 8. Further, it could very well be on of if not the key feature of PHP 8. In majors backwards compatibility breaks are considered were warranted. I'm not familiar with the Zend Engine as I probably should be. I bring the perspective of an end user. From what you've posted am I correct in stating that PHP Type Hints / scalar Type Declarations are in truth syntactic sugar for asserting the type checks. Hence we read this function foo( ClassA $a, ClassB $b, string $c ) {} But the engine has to do the work of this... function foo ( $a, $b, $c ) { assert( $a instanceof ClassA, TypeError ); assert( $b instanceof ClassB, TypeError ); assert( is_string($c), InvalidArgument ); } If that is indeed the case, why not disable these checks according to the zend.assertions flag, or if that's too bold a move create a php.ini flag that allows them to be disabled in production. Existing code would be unaffected if it has been fully debugged because, in accordance with the principles of Design by Contract, a call with an illegal type should be impossible. For code that isn't up to par though we have the possibility of data corruption when the code proceeds past the call to wherever the reason for that type hint is. I'll hazard that most of the time that will be a call to method on non-object or something similar. PHP programmers however would need to get used to the idea that their type hints mean nothing when assertions are turned off (or if handled by a separate flag, when that flag is turned off). I'm ok with this, but I'm a big proponent of Design by Contract methodology as a supplement to Test Driven Design. Another thing to consider is that if the existing type hints are so expensive, this change might grant a welcome speed boost. Extending that to all containers means every assignment operation would > effectively need to check the value on the right-hand-side against the > constraint on the left-hand-side. Some of those checks are non-trivial, > e.g. class/interface constraints, callable; or in future maybe "array of > Foo", "Foo | Bar | int", "Foo & Bar", etc. There are ways to ease this a > bit, like passing around a cache of type constraints a value has passed, > but I think we should consider whether the "always-on runtime assertions" > model is the one we want in the long term. > > > If the type is omitted, scalar is assumed. >> > > As Andreas pointed out, you mean "mixed" here (accepts any value), rather > than "scalar" (accepts int, string, float, and bool). > > Yes. I admitted to him in a previous post that I had made that mistake. > > The variables created by this pattern auto cast anything assigned to them >> without pitching an error. >> > > My initial thought was that this makes the assignment operator a bit too > magic for my taste. It's conceptually similar to the "weak mode" for scalar > type hints (and could perhaps use the same setting), but those feel less > magic because they happen at a clear scope boundary, and the cast only > happens once. But on reflection, the consistency makes sense, and assigning > to an object property defined by another library is similar to calling a > method defined by another library, so the separation of caller and callee > has similar justification. > > > PHP 7 introduced type declarations. >> > > This is incorrect, and leads you to a false conclusion. PHP 7 introduced > *scalar* type declarations, which extended an existing system which had > been there for years, supporting classes, interfaces, the generic "array" > constraint, and later pseudo-types like "callable". > > I don't think it's tenable to change the meaning of this syntax, but it > would certainly be possible to bikeshed some modifier to simultaneously > declare "check type on function call, and declare corresponding local > variable as fixed type". > > Or go back to using the under utilized assert() statement :D Or, if it's really important to the programmer, they can re-declare the variable to lock the type down. I only suggested this change to bring about consistency. > > > COMPARISON BEHAVIOR >> When a strongly typed variable (autocasting or not) is compared to a >> scalar >> variable only the scalar switches types. The strict comparison operator is >> allowed though it only blocks the movement of the scalar. >> >> Comparisons between strongly typed variables are always strict and a >> TypeError results if their types don't match. This actually provides a way >> to force the greater than, lesser than, and spaceship operation to be >> strict. >> > > I like this idea. The over-eager coercion in comparisons is a common > criticism of PHP. > > > In general I really like the outline of this; there's a lot of details to > work out, but we have to start somewhere. > > Well, after this post I'm going to write a second draft pursuant to what you and Andre have taught me and addressing some of the concerns that have been raised.