FAQ
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

Search Discussions

  • Stas Malyshev at Jul 12, 2012 at 4:30 pm
    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
  • Rasmus Lerdorf at Jul 12, 2012 at 4:38 pm

    On 07/12/2012 09:30 AM, Stas Malyshev wrote:
    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
  • Stas Malyshev at Jul 12, 2012 at 4:46 pm
    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
  • Anthony Ferrara at Jul 12, 2012 at 5:01 pm
    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
  • Stas Malyshev at Jul 12, 2012 at 5:34 pm
    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 by `assert($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
  • Rasmus Lerdorf at Jul 12, 2012 at 5:12 pm

    On 07/12/2012 09:46 AM, Stas Malyshev wrote:
    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
  • Stas Malyshev at Jul 12, 2012 at 5:19 pm
    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
  • Rasmus Lerdorf at Jul 12, 2012 at 5:23 pm

    On 07/12/2012 10:19 AM, Stas Malyshev wrote:
    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
  • Matthew Weier O'Phinney at Jul 13, 2012 at 1:41 pm

    On 2012-07-12, Stas Malyshev wrote:
    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
  • Matthew Weier O'Phinney at Jul 13, 2012 at 1:41 pm

    On 2012-07-12, Rasmus Lerdorf wrote:
    On 07/12/2012 09:30 AM, Stas Malyshev wrote:
    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
  • Stas Malyshev at Jul 13, 2012 at 5:01 pm
    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
  • Gustavo Lopes at Jul 12, 2012 at 5:40 pm

    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
  • Christer Edvartsen at Jul 12, 2012 at 5:42 pm

    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

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupphp-internals @
categoriesphp
postedJul 12, '12 at 2:10p
activeJul 13, '12 at 5:01p
posts14
users6
websitephp.net

People

Translate

site design / logo © 2022 Grokbase