PHP 10.0 Blog

What if…

duck operator

Posted by Stas on June 5, 2008

Crazy idea for today – operator to check conformance to specific interface without actually implementing it. Why one would want that?
Well, if you are into duck typing style of programming, it may be interesting for you to have an object that implements certain set of functions, but not necessary declares it at class definition. Languages like Smalltalk do it all day along, so why PHP couldn’t? The idea is it looks like this:

interface Cow {
  function moo();
  function eatGrass();
}
/* somewhere else */
class MooingGrassEater {
  function moo() {/*stuff */}
  function eatGrass() {/*stuff */}
  /*stuff */
}
/* somewhere else */
function CowConsumer($classname) {
$foo = new $classname();
if($foo implements Cow) {
  echo "Behold the cow:";
  $foo->eatGrass();
  $foo->moo();
} else {
  echo "$classname is not a cow!";
}

implements here is our duck operator. Note that unlike instanceof, no formal relationship is required, but only practical implementation. So another name would be “common law marriage operator” ;)

Of course, this one would be anathema to “strict OO” camp, so if you subscribe to that, just ignore this post :)

Two challenges to this idea are:

  1. __call() – we have no way to know what __call does. So either we ignore it or say “ok, __call does everything”. I’d go for the latter.
  2. Performance. To check duck implementation one basically would have to match method lists, which amounts to number of is_callable calls equal to the number of methods in interface being checked.

Actually, PHP uses this style sometimes – see, for example, user defined streams. But there’s no nice way to work with it from the consumer side.

About these ads

15 Responses to “duck operator”

  1. which amounts to number of is_callable calls equal to the number of methods in interface being checked

    not exactly.
    Actually, it would be: number of methods you are going to use on that class (interface might be wider, and there are good chances, that you don’t need all of its methods)

  2. gosha said

    From my understanding, “if($foo implements Cow)..” is not what they call “duck typing” or is not “ducky” enough. Ideally we should be able to do it in a “dont ask, do” way


    try {
    $foo->eatGrass();
    } catch(UnknownMethodException $e) {
    ...
    }

    What? Zend Engine still doesn’t support this? Shame on them. ;)

  3. elias said

    Well, i like the idea. Actually i’ve started a RFC yesterday. It’s in german currently but i’ll publish it in english if i’ve reviewed it with a friend.

    But in short:
    I’m differing three concepts. First are concrete interfaces, the current implementation. Second are implicit interfaces. In that case every class (or trait) is an interface and is comparable to each other without implementing and concrete interface (‘interface’ is not needed anymore). Third are duck interfaces. Which work like implicit interfaces but additionally every subset of an class can also be an interface.

    Example with implicit interfaces:
    class SplCountable {
    function count(){}
    }
    class FooCountable {
    function count(){}
    }
    class List implements SplCountable {
    function add($value){}
    function count(){}
    }
    new List instanceof FooCountable; // true
    new List instanceof SplCountable; // true

    Mention that you need to implement (or extend) one of the classes with the same signature!

    Example with duck interfaces:
    class Countable {
    function count(){}
    }
    class List {
    function add($value){}
    function count(){}
    }
    new List instanceof Countable; // true

    The good thing is that there are no more hardwired interfaces needed, so you have less dependencies and more flexibility. The downside is, like you noticed, the performance. Implicit interfaces will do the interface stuff for every class, so it’s an overall performance payoff. Duck interfaces require more performance for each comparison at runtime. For duck interfaces it’s an good idea to use another operator, like in your example.

    Good to see that i’m not alone with my ideas :)

  4. [...] up on this post regarding a “duck operator” in PHP, I went ahead and wrote a very, very, very simple implementation using reflection to get the [...]

  5. gosha said

    good job deleting comments, my friend.
    actually, my failure: haven’t noticed from the start you’re russian – that explains a lot.
    gday

  6. [...] this new post to the PHP 10.0 blog today, Stas talks about duck typing, a method that lets the code decide the [...]

  7. [...] this new post to the PHP 10.0 blog today, Stas talks about duck typing, a method that lets the code decide the [...]

  8. At first glance, this seems like a bad idea. It seems to me to be horribly dangerous to assume that functions eatGrass() and moo() do “what you’d expect” from such named functions based solely on their existence.

    I just can’t imagine how this is a good practice. If you expect a class to implement a function with a given name, then check for it directly with is_callable. I don’t see much of a need to automate this over a set of functions.

    What you are discussing is essentially what Objective-C calls informal protocols.

    An informal protocol is a *definition* of a set of related methods, not all of which have to be implemented. Callers can then just check the existence of the desired function(s) and call them as needed. Informal protocols are just interfaces where the methods are OPTIONAL.

    I use this concept a lot in my PHOCOA framework. You can read more at http://phocoa.com.

  9. Maybe this link will help you : http://php.net/manual/en/function.class-implements.php

    I never try this function, cause i have migrate to python now. But I will be happy if u do some kind of return :)

  10. NiKo said

    You can do this to

    <?php
    interface Mooer {
    function moo();
    }

    class Cow implements Mooer {
    function moo() {
    echo ‘moo!';
    }
    }

    var_dump(new Cow() instanceof Mooer); // bool(true)

  11. Stas said

    Gosha, I think you need to chill a bit. And if for some reason, incomprehensible to me, you are interested in my national roots – I’m Jewish. The comments here are moderated, thanks to our friends the spammers. Unfortunately, I have some things of little importance to do besides the blog – like work, personal life, etc. – so approving comments sometimes could take forever, like a day. Sorry about that.

    Now with that out of the way, to the exception thing. You can do that easily with __call, but I see no reason why – throwing exceptions left in right is not really the style I’d like.

  12. Stas said

    Thierry Stiegler, NiKo – that’s for formal declarations. I was talking about informal contract implementation.

    Alan – optional interfaces would be a nice idea too, but different one :)

  13. robinf said

    One interesting difference compared to formal declaration (explicit interface implementation) is that the duck operator would presumably take visibility into account. I assume this, since you talk about implementing it with is_callable(), which iirc does visibility checks.

    So, for example, in a context where MooingGrassEater’s moo() method is visible, it would be considered to “implement” Cow. But from a different context where moo() is not visible, it wouldn’t be considered to “implement” Cow.

    Would only the method name be used for comparison? What about differing parameter pass semantics (value vs ref), type hints, default values etc…?

  14. [...] I was going through my rss feeds, I read that Stas published an article about duck typing in PHP. I noticed that some of the posts response showed a lack of understanding between the difference of [...]

  15. Pete Hurst said

    Hmm. I think that duck typing will result in code reminiscent of a huge heap of stinking guano.

    I can’t see one good reason in your example for actually coding things that way. What’s wrong with an interface – a known contract for operating on an object?

    Exceptions are a very inefficient way of handling logic. They should only be used to catch errors that you can’t possibly predict or account for. If you *know* that a function might not be there, you should check its existence first, avoiding an exception altogether.

    Don’t encourage sloppy practices! PHP is full enough of them already. This is not from a strict OO camp. This is from the general sanity camp.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: