RE: [PHP-DEV] Scalar Type Hinting
| From: | John Crenshaw | Date: | Wed, 07 Mar 2012 21:59:32 +0000 |
| Subject: | RE: [PHP-DEV] Scalar Type Hinting | ||
| References: | 1 2 3 4 5 6 7 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-58757@lists.php.net to get a copy of this message | ||
> From: Anthony Ferrara [mailto:ircmaxell@gmail.com]
>
> John,
>
> On Tue, Mar 6, 2012 at 9:04 PM, John Crenshaw <johncrenshaw@priacta.com> wrote:
> > A good number of issues with the current proposal were raised during the discussion on the
> > mailing list. I don't feel like digging them all up right now, but off the top of my head I
> > remember the following being raised and never saw any consensus for how to resolve them:
>
> I went over the replies to the initial POC thread that I posted
> (http://marc.info/?t=133066037200001&r=1&w=2) and I'll rebut your replies.
>
You've been spending a lot of time defending these proposals and trying to prove wrong feedback
that raises concerns. This is preventing you from actually using the feedback to improve the
proposals. You are losing out on perhaps the biggest advantage of the RFC process, which is that
multiple minds can work together to hammer out an idea and make it really shine.
Most of your "rebuttals" focus entirely on whether the RFC contains sufficient information
to make something technically work. That's not the point at all. I've read the RFC. I have
no doubt that it "works", nor I think does anyone else. That's not the issue.
To quote a recent film: "Titan: Oh yeah? What's the difference? Megamind:
PRESENTATION!". The code syntax is the UI that PHP presents to developers. Yes, this
"works" (in the sense that it is possible to implement what the RFC describes), but there
are serious usability and communication problems. Language is also one of the trickiest interfaces
to work with because once you commit to something you are pretty much stuck with it forever
(namespace separator). If every PHP developer is going to have to deal with this until the end of
time it needs to be *awesome*.
> > - inconsistent syntax (one syntax for scalars, a different one for
> > classes)
>
> This is actually discussed in the RFC, as it is not inconsistent (it's actually consistent
> with what the patch tries to achieve). The syntax for classes and normal arrays is a strict check,
> where if the match fails an error is thrown. This syntax attempts to distinguish between that
> functionality by providing a different syntax altogether. And since it's casting the
> parameters, the syntax feels natural (for that goal).
>
You can mince words, but that doesn't change the problem. It is utterly inconsistent with the
expectations in creates. You'll argue that it creates an expectation of a type cast, but
you'd be wrong in far too many cases. The syntax is similar enough to the syntax for parameter
types in other languages that developers will think of it as basically the same thing. The syntax
differs however from existing parameter type syntax.
The behavioral difference is also a problem, being too different from parameter typing to be useful
(it doesn't actually vet the parameter), and yet close enough to validate the confusion
(behaves too similarly to an implicit conversion). In the end you have a bizarre syntax that looks
like one thing but is conceptually another, but with a subtle behavioral difference that is
invisible except when it fails to fail.
> > - conflicting syntax (I.E. array vs. (array), RFC simply "allows"
> > this, and ignores the confusion that this will create for users.)
>
> Actually, it doesn't simply allow that. It did it for a very specific reason.
> "array" is a strict check, and "(array)" is a casting check.
> One will fatal if a non-array is passed, and the other will attempt to convert the parameter to
> an array. Very different functionality, which are both internally consistent with the other
> syntax...
>
I know about the behavioral difference. I'm not talking about a technical conflict, I'm
talking about a conflict in the mind of the developer. Given function(array $a, (array) $b){} the
difference between $a and $b is a very advanced distinction and will be completely lost by the
average developer.
The confusion is made worse by the fact that function(array $a) works, but function(int $a)
doesn't, but function((int) $a) does. I know why, but the average developer just learning PHP
for the first time won't get it at all.
> > - different from the syntax used in the docs
>
> Actually, it's the exact same syntax used for casting in the docs.
> It's different from class type hints, because it's intended to be so.
> If you don't like it, that's fine. But it's intentionally different.
>
Sometimes I think you miss the point on purpose.
In the docs substr is defined as:
string substr ( string $string , int $start [, int $length ] )
If I wanted to write the same thing in my own code I would have to write:
function my_substr ( (string) $string , (int) $start , (int) $length )
One syntax for the docs, a different one in my code.
> > - lack of sufficient function to justify a core change
>
> That's absolutely something to be considered. However, I see erroring on invalid casts as
> a bigger issue not the responsibility of a
> **casting** hint patch. So that's why I mention explicitly in the RFC and my blog post
> that solving that problem should be another RFC (culminating in a series of 3 RFCs that each work
> together very well to fill the overall need).
>
Well, if you argue that this proposal is actually a typecasting enhancement and not a parameter type
enhancement, then yes, you'd be right. That's not how this is presented though,
that's not where it is presented (Scalar Type Hinting discussion), that's not when it was
presented, and that's not how it is getting perceived or measured.
As far as expanding type casting in this way, it doesn't make sense. There is no precedent, it
looks and will get treated like parameter hinting (which you are saying it isn't), there is no
additional functional value, and very little value of any sort beyond casts (just a tiny
documentation boost and a savings in the number of characters in the few cases where a dumb type
cast is good enough.)
> > - chaos surrounding null (to accept and if so to cast or not? Creates
> > a conflict between consistency and implications of the syntax)
>
> That's absolutely a valid concern. And that's what should be being discussed, if
> people really feel that the current implementation is wrong...
>
> > - conflicts with references (RFC tries to address this by simply
> > disallowing references, which IMO just ignores the need that would
> > have caused this sort of code in the first place.)
>
> Actually, it explicitly disallows references. This was added because it was explicitly
> requested on the list in many discussions (including the initial one for the POC). But if you can
> make a case on why to implement it, and why it makes sense, then we can add it back.
>
Again, yes, I know that the RFC takes a position on this. My point is not that the RFC doesn't
take a position, my point was that the position taken "ignores the need that would have caused
this sort of code in the first place."
Reference parameters are basically return values. Just because a parameter will return a value
doesn't mean that the type no longer matters. In fact, it means that the calling function
should EXPECT the type to change, and EXPECT the value to be modified. One example that occurs to me
off hand is preg_match, where the 3rd parameter is documented as an array reference. You can
actually pass anything, but it's going to be an array when the function returns.
> > There were others, but I'm not making an exhaustive list.
>
> Well, the ones that I saw were either rectified, requested (and
> implemented) or (to me) showed a lack of understanding about the rationale and decisions (which
> is valid, but shouldn't really bear that much on the discussion, as I feel if you want to raise
> an issue, you should at least have a cursory understanding of what you're commenting about)...
>
> >> As far as it being crippled, I'm not sure what you mean, just because it's
> >> only doing casting?
> >>
> >
> > Yes, casting is barely better than doing nothing. If I want a dumb typecast I can do that
> > already. What I can't do without massive boilerplate everywhere is an intelligent conversion
> > that accepts safe conversions and gives a warning or error on unsafe conversions.
>
> Again, I personally see casting data-loss a bigger issue than just parameter hinting, which
> should get its own RFC to clean up. That's why I didn't include it here. On purpose...
>
You can't raise errors like this on lossy casts. Giving a warning if an implicit conversion
loses data (123 + '456xyz', or substr('foo', 'bar')) makes sense, but
if I explicitly write (int)'456xyz' or (int)23.7, I must not get a warning about data loss
(otherwise it would be difficult/impossible to force a deliberately lossy conversion without
warnings). Since we can't raise warnings on casts, you really won't be able to do it with
casted parameters either (unless you're willing to accept a consistency break). These will be
stuck in silence forever.
> > Well, the big issue for me is that I think the type casting hints starts from a
> > fundamentally bad foundation. The premise here is based on a syntax that is ugly and strange, and
> > the syntax opens a bunch of new problems that I don't see a way to resolve. The reason for
> > selecting this syntax was to avoid a BC break from reserving some new keywords. I don't really
> > see this as a good trade.
>
> To be perfectly clear, the reason for that syntax was **not** to avoid a BC break. It was to
> avoid a FC break. I wanted a casting hint, and I didn't want to restrict the possibility of
> adding strict hints later. And I wanted to clearly distinguish the current strict hints (array +
> class) and the new casting hints. To me, the most logical way of doing that was to re-use the
> casting syntax. Could we have done foo(int? $foo) or foo(*int $foo) or foo(@int $foo) or whatever?
>
Well, if that is the case, you did not succeed. The FC break you originally worked to avoid is
something that can never happen anyway, and this does not avoid the most critical FC break.
First, "strict" types (in the sense of not allowing an integer to be passed to a string
parameter) are never going to happen in PHP. Even C++ looks for an implicit conversion and uses that
if available. PHP has well defined implicit conversions between scalars, and there is absolutely no
chance that PHP is ever going to implement any form of typing that is more strict than typing in
C++. A strict set of scalar types hint that don't juggle anything is just not in the cards.
Second, (bear with me) the form of typing that people seem to have settled on before this RFC was
introduced is "the same typing as the core, but at the language level." To use a prior
example, the core has:
string substr ( string $string , int $start ){...}
and the following behavior:
substr('foo', 'bar'); // E_WARNING
substr(123, '1'); // '23'
I should be able to write an equivalent:
function my_substr ( string $string , int $start ){..}
and I should get matching behavior:
my_substr('foo', 'bar'); // E_WARNING
my_substr(123, '1'); // '23'
Now here's the point, if somehow this proposal were accepted and incorporated into the language
it would make it nearly impossible to later achieve the above. Having both would be confusing,
because the behavioral difference is extremely narrow. Your syntax at that point really just
silences lossy implicit conversions and would actually be more similar to what one might expect
from:
function my_substr ( @string $string, @int $start ){..}
> Sure. But I'd argue that anything except foo(int $foo) would have lead to FAR more
> confusion.
>
I don't think any other syntax is even an option. This syntax is used by existing type hints in
PHP, and in the PHP documentation, and in basically all generated library documentation, and used
for parameter types in every syntactically similar language.
> This way, it's re-using an existing syntax that **every** intermediate developer is
> intimate with, and applying it to a case that's basically functioning along the same lines...
>
And reusing that syntax leads this straight into the dead end indicated previously when talking
about the FC objective.
John Crenshaw
Priacta, Inc.