Hello all,
At present, there's now way to type hint over a generic structure that it
iteratable using foreach(). You can accept arrays using the array hint, and
objects using traversable, but you cannot hint both. This yields code that
wants to accept that to look like this:
function foo($a) {
if (!is_array($a) && !$a instanceof Traversable) {
raise error;
}
}
Would it be worth while adding a new type hint that checks for this
condition? I'd propose Iterable:
function foo(Iterable $a) {
foreach ($a as ...) {}
}
It's just a quick thought and wanted some feedback on it.
Thanks
Anthony
Hi!
Would it be worth while adding a new type hint that checks for this
condition? I'd propose Iterable:
I see more and more multiplication of weird ad-hoc type checks. First we
had "callable", now "traversable", then we invent more and more weird
functional types with complex logic. I don't like this development at
all. It's ad-hoc introducing of half-baked, unstandartized, undesigned
strict typing. Strict typing is not a good idea for PHP, and weird
strict typing based on complex conditions hidden from the user is even
worse IMO.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Hi!
Would it be worth while adding a new type hint that checks for this
condition? I'd propose Iterable:I see more and more multiplication of weird ad-hoc type checks. First we
had "callable", now "traversable", then we invent more and more weird
functional types with complex logic. I don't like this development at
all. It's ad-hoc introducing of half-baked, unstandartized, undesigned
strict typing. Strict typing is not a good idea for PHP, and weird
strict typing based on complex conditions hidden from the user is even
worse IMO.
For non-interchangeable types it is already strict by definition. I
don't see a problem with type hints that make life easier on both the
caller (by generating better error messages) and the callee (by having
to write less boilerplate type verification code).
You may have a point on the ad-hoc nature of it and that we need to do
it once and for all in a more organized fashion, but the basic premise
looks ok to me.
-Rasmus
Hi!
For non-interchangeable types it is already strict by definition. I
don't see a problem with type hints that make life easier on both the
caller (by generating better error messages) and the callee (by having
to write less boilerplate type verification code).
It doesn't make the life of the caller easier. On the contrary, it makes
each call into a minefield - will it blow up with a system-level error
when you call it? Added bonus of this one is that there's no sure way to
check for it - at least for callable we had is_callable, here we just
have to add boilerplate code for every call. And good luck making them
handle it in an unified way - since it's not library code, then probably
will do each different thing, and about 50% of places will forget it or
not know downstream inserted this surprise into the execution stream.
You may have a point on the ad-hoc nature of it and that we need to do
it once and for all in a more organized fashion, but the basic premise
looks ok to me.
I think the basic premise (PHP needs more strict typing that it is
completely unable to properly handle and that leads to runtime errors
that had no choice but blow up the whole app) is wrong, but ad-hoc
implementation of it by just dragging random pieces into the type system
is even more wrong.
Note that no other dynamic language is doing such things. I wonder why.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Stas,
For non-interchangeable types it is already strict by definition. I
don't see a problem with type hints that make life easier on both the
caller (by generating better error messages) and the callee (by having
to write less boilerplate type verification code).It doesn't make the life of the caller easier. On the contrary, it makes
each call into a minefield - will it blow up with a system-level error
when you call it?
Well, I'm not so sure that I agree there. If you're passing an incompatible
type, it's an application level error, not a runtime level one. And
additionally, it enables better defensive programming where code ensures
what's passed is what it needs (and the language helps enforce that).
Similar to design-by-contract...
Added bonus of this one is that there's no sure way to
check for it - at least for callable we had is_callable, here we just
have to add boilerplate code for every call.
I don't think you need boilerplate for every call. Either you create what
you're going to pass to said function call, or you type-hint it properly on
that method. The onus is on the creator of the variable, not the consumer.
Additionally, we could add other is_* functions for these agregate type
hints. is_iterable()
, etc...
And good luck making them
handle it in an unified way - since it's not library code, then probably
will do each different thing, and about 50% of places will forget it or
not know downstream inserted this surprise into the execution stream.
How would downstream affect you? I would understand upstream, but I don't
get that point... Could you elaborate further?
You may have a point on the ad-hoc nature of it and that we need to do
it once and for all in a more organized fashion, but the basic premise
looks ok to me.I think the basic premise (PHP needs more strict typing that it is
completely unable to properly handle and that leads to runtime errors
that had no choice but blow up the whole app) is wrong, but ad-hoc
implementation of it by just dragging random pieces into the type system
is even more wrong.
It's a Recoverable error, so no, it doesn't have no choice but to blow up
the whole ap. You can install an error handler and recover from them. The
point being that it pushes the call-time checks used by defensive
programming into the language instead of as requiring a lot of copy-paste
to validate arguments...
Note that no other dynamic language is doing such things. I wonder why.
Well, that's an odd point, because almost all of the other dyanmic typed
languages (at least of the popular ones) are object oriented at core. So
this sort of type hinting can be done by assert($obj instanceof Foo)
.
With PHP, given the first-class primitives, this is not as straight
forward. I'm not saying that we shouldn't take cues from other languages,
but to implement or not based on what other languages do is a bit odd,
given that PHP is unique in a lot of ways from those other languages...
Just my $0.02 at least...
Anthony
Hi!
I don't think you need boilerplate for every call. Either you create
what you're going to pass to said function call, or you type-hint it
properly on that method. The onus is on the creator of the variable, not
the consumer.Additionally, we could add other is_* functions for these agregate type
hints.is_iterable()
, etc...
And pretty soon we'd have dozens of is* functions for types ranging from
"valid IPv6 address" to "properly initialized object of one of the four
types named below", driven by random use cases. I don't think it's a
proper way to design a language type system.
How would downstream affect you? I would understand upstream, but I
don't get that point... Could you elaborate further?
If you call some function, that calls some function, that calls some
function that has strict type check, you need to ensure that that type
check is satisfied, otherwise your code blows up in runtime and you'd
have no way to correct for it. Since PHP does not have static type
checking, the only way to ensure this does not happen is to catalogue
all code paths that have such things happening and insert checks that
ensure you pass the correct type. Of course, in practice nobody would do
that - they'd just right the code assuming everything is fine and then
when it does blow up they'd sit for days with a debugger trying to
figure out why the damn variable is of the wrong type here in 1.5% of
hard-to-reproduce cases. When we're talking about proper types such
occasions would be rare enough, but once we start introducing
use-case-driven pseudo-types, IMHO it will quickly become a mess.
It's a Recoverable error, so no, it doesn't have no choice but to blow
up the whole ap. You can install an error handler and recover from them.
"recoverable" errors is another in the gallery of misnomers we have
around this - it's not recoverable. How exactly you recover from it? If
you handled it in the code, you could return or assume some default
value, you could take different code path, etc. - but in centralized
error handler pretty much only thing you can do is to display a nice
error message and shut down the whole thing.
The point being that it pushes the call-time checks used by defensive
programming into the language instead of as requiring a lot of
copy-paste to validate arguments...
Which I think is exactly wrong thing to do, because PHP does not have
proper tools to handle it - if pushing it into the language means we
promote "log and shut down" approach to handling any exceptional
situation. For some cases, it may be appropriate, but I think we're
taking it too far by creating ad-hoc type system based on it.
Well, that's an odd point, because almost all of the other dyanmic typed
languages (at least of the popular ones) are object oriented at core. So
this sort of type hinting can be done byassert($obj instanceof Foo)
.
It can be done in PHP too. However I don't think people actually do it.
With PHP, given the first-class primitives, this is not as straight
forward. I'm not saying that we shouldn't take cues from other
languages, but to implement or not based on what other languages do is a
bit odd, given that PHP is unique in a lot of ways from those other
languages...
I still call you to think why such things aren't done, instead of
refusing to consider it by just saying "PHP is unique, so we don't care
what happens with the rest of the world".
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Hi!
For non-interchangeable types it is already strict by definition. I
don't see a problem with type hints that make life easier on both the
caller (by generating better error messages) and the callee (by having
to write less boilerplate type verification code).It doesn't make the life of the caller easier. On the contrary, it makes
each call into a minefield - will it blow up with a system-level error
when you call it? Added bonus of this one is that there's no sure way to
check for it - at least for callable we had is_callable, here we just
have to add boilerplate code for every call. And good luck making them
handle it in an unified way - since it's not library code, then probably
will do each different thing, and about 50% of places will forget it or
not know downstream inserted this surprise into the execution stream.
For non-interchangeable types the code is going to blow up anyway. If
you pass a MySQL resource to a function that takes a callable and that
function does $arg(); the result is a non-catchable fatal error:
Fatal error: Function name must be a string
Compare to adding 'callable':
Catchable fatal error: Argument 1 passed to func() must be callable,
resource given
This gives quite a bit more info since we now know that it was an
argument and specifically which argument it was, what its type was and
what it should have been vs. having a fatal from somewhere deep in the
function itself. So I disagree with you on it not making life easier for
the caller in this specific case where there is no way for the type to
be coerced into something that makes sense.
-Rasmus
Hi!
This gives quite a bit more info since we now know that it was an
argument and specifically which argument it was, what its type was and
what it should have been vs. having a fatal from somewhere deep in the
function itself. So I disagree with you on it not making life easier for
the caller in this specific case where there is no way for the type to
be coerced into something that makes sense.
You've traded bad error message for slightly better error message, but
on the way you've lost the ability to actually handle this situation.
Which exactly what bothers me - we're teaching people that the right way
of handling any unexpected situation is to rely on post-mortem error
logging after it blows up in runtime. I'm not sure it's such a good idea.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Hi!
This gives quite a bit more info since we now know that it was an
argument and specifically which argument it was, what its type was and
what it should have been vs. having a fatal from somewhere deep in the
function itself. So I disagree with you on it not making life easier for
the caller in this specific case where there is no way for the type to
be coerced into something that makes sense.You've traded bad error message for slightly better error message, but
on the way you've lost the ability to actually handle this situation.
Which exactly what bothers me - we're teaching people that the right way
of handling any unexpected situation is to rely on post-mortem error
logging after it blows up in runtime. I'm not sure it's such a good idea.
I'm not sure what the right way would be to handle this situation.
Should the function be doing this check manually, and if so, what would
it tell the caller? "Hey, I was expecting a callable function but you
seem to have passed me a MySQL resource handle". What else could it do?
I completely agree on wanting to avoid trending people towards strict
typing for interchangeable types, but this is completely different.
-Rasmus
For non-interchangeable types it is already strict by definition. I
don't see a problem with type hints that make life easier on both the
caller (by generating better error messages) and the callee (by having
to write less boilerplate type verification code).It doesn't make the life of the caller easier. On the contrary, it makes
each call into a minefield - will it blow up with a system-level error
when you call it?
I think you're reading way more into this, or didn't read the same
sample I did from Anthony.
foreach() allows an array or a Traversable object. The proposal is to
create a typehint that spans the set of (array + Traversable) so that
folks don't have to do a check in each and every method where they want
to accept both so they can iterate. I've written the same or similar
checks to what Anthony posted hundreds of times, and seen it many, many
more than that.
--
Matthew Weier O'Phinney
Project Lead | matthew@zend.com
Zend Framework | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Would it be worth while adding a new type hint that checks for this
condition? I'd propose Iterable:I see more and more multiplication of weird ad-hoc type checks. First we
had "callable", now "traversable", then we invent more and more weird
functional types with complex logic. I don't like this development at
all. It's ad-hoc introducing of half-baked, unstandartized, undesigned
strict typing. Strict typing is not a good idea for PHP, and weird
strict typing based on complex conditions hidden from the user is even
worse IMO.For non-interchangeable types it is already strict by definition. I
don't see a problem with type hints that make life easier on both the
caller (by generating better error messages) and the callee (by having
to write less boilerplate type verification code).You may have a point on the ad-hoc nature of it and that we need to do
it once and for all in a more organized fashion, but the basic premise
looks ok to me.
I wouldn't call it ad hoc, actually, but more a recognition of what practices
and patterns are now occurring. A few years ago, I'd have type-hinted on array
and been done with it. But more and more often, I'm interested in either an
array or something Traversable, and I end up with boilerplate just like Anthony
had in his original post on this thread. And I see it everywhere.
--
Matthew Weier O'Phinney
Project Lead | matthew@zend.com
Zend Framework | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Hi!
I wouldn't call it ad hoc, actually, but more a recognition of what practices
and patterns are now occurring. A few years ago, I'd have type-hinted on array
I do not think strict typing is a right way to do it. Strict typing has
specific meaning in many languages, including (unfortunately, IMHO) in
PHP - that the value has specific type. (Ab)using it to mean "the value
conforms to specific multi-type use case" seems not the right way to me,
unless we design very different type system from what we have now - but
even then we should start with designing it, not with ad-hoc use cases.
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Em Thu, 12 Jul 2012 18:30:43 +0200, Stas Malyshev smalyshev@sugarcrm.com
escreveu:
Would it be worth while adding a new type hint that checks for this
condition? I'd propose Iterable:I see more and more multiplication of weird ad-hoc type checks. First we
had "callable", now "traversable", then we invent more and more weird
functional types with complex logic. I don't like this development at
all. It's ad-hoc introducing of half-baked, unstandartized, undesigned
strict typing. Strict typing is not a good idea for PHP, and weird
strict typing based on complex conditions hidden from the user is even
worse IMO.
I agree with everything Stas said.
Additionally, while "callable" has a problem that "iterable" wouldn't have
-- namely, whether something is callable is context sensitive* -- its
implementation was accompanied by a homogenization of callable types
(e.g., you can now do $a = $func = ['A', 'foo']; $a()). Traversable types
are by no means interchangeable. In fact the only thing they have in
common is that can be passed to foreach. On everything else, they're
different. It makes to sense to a pseudo-type for two entities that all
they have in common is that one property.
- Example:
class A {
public static function foo(callable $r) {
B::foo($r);
}
private static function test() {}
}
class B {
public static function foo(callable $r) {}
}
A::foo(['A', 'test']);
--
Gustavo Lopes
Would it be worth while adding a new type hint that checks for this
condition? I'd propose Iterable:
I'd love to see something like this added to core.
--
Christer Edvartsen
http://twitter.com/#!/cogocogo