# Closures and type hinting

- Thread: https://externals.io/message/63368
- Original archive: https://news-web.php.net/php.internals/63368
- Started by Christian Kaps on 2012-10-12T08:58:56+00:00
- Messages: 6
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #63368 — Christian Kaps — 2012-10-12T08:58:56+00:00

URL: https://externals.io/message/63368#63368

At the moment it isn't possible to restrict/define the arguments for a 
closure which is defined as parameter for a method. Please look at this 
small example.

interface Broker {

     public function scan(Request $request, Closure $onFound);
}

An implementation of the interface could be as follows:

class BrokerImpl implements Broker {

     /**
      * @var Service
      */
     private $service = null;

     public function scan(Request $request, Closure $onFound) {

         if ($request->contains('some value')) {
             $onFound($this->service);
         }
     }
}

The problem is that I can pass every closure to the scan method.

$broker = new BrokerImpl();
$broker->scan($request, function() { /* do something */ });

Sometimes I would like to restrict the closure passed to this method. 
So that only closures of a certain type which has an argument "Service 
$service" could be passed to this method.

$broker = new BrokerImpl();
$broker->scan($request, function(Service $service) { /* do something */ 
});

Would it not be possible to extend the Closure class and then define an 
abstract method signature for the __invoke method.

class OnFoundClosure extends Closure {

     public abstract function __invoke(Service $service);
}

And then you can define the interface as follows:
interface Broker {

     public function scan(Request $request, OnFoundClosure $onFound);
}

Now if you pass a closure to the scan method which doesn't follow the 
signature of the __invoke method, the engine should throw an error.

What do you think?

---

## #63369 — Anthony Ferrara — 2012-10-12T09:18:42+00:00

URL: https://externals.io/message/63368#63369 · In reply to: #63368

On Oct 12, 2012 10:59 AM, "Christian Kaps" <christian.kaps@mohiva.com>
wrote:
>
> At the moment it isn't possible to restrict/define the arguments for a
closure which is defined as parameter for a method. Please look at this
small example.
>
> interface Broker {
>
>     public function scan(Request $request, Closure $onFound);
> }
>
> An implementation of the interface could be as follows:
>
> class BrokerImpl implements Broker {
>
>     /**
>      * @var Service
>      */
>     private $service = null;
>
>     public function scan(Request $request, Closure $onFound) {
>
>         if ($request->contains('some value')) {
>             $onFound($this->service);
>         }
>     }
> }
>
> The problem is that I can pass every closure to the scan method.
>
> $broker = new BrokerImpl();
> $broker->scan($request, function() { /* do something */ });
>
> Sometimes I would like to restrict the closure passed to this method. So
that only closures of a certain type which has an argument "Service
$service" could be passed to this method.
>
> $broker = new BrokerImpl();
> $broker->scan($request, function(Service $service) { /* do something */
});
>
> Would it not be possible to extend the Closure class and then define an
abstract method signature for the __invoke method.
>
> class OnFoundClosure extends Closure {
>
>     public abstract function __invoke(Service $service);
> }
>
> And then you can define the interface as follows:
> interface Broker {
>
>     public function scan(Request $request, OnFoundClosure $onFound);
> }
>
> Now if you pass a closure to the scan method which doesn't follow the
signature of the __invoke method, the engine should throw an error.
>
> What do you think?
>
> --
> 
> 
>

Honestly, I would suggest using a class directly if you want that kind of
type control. Closures are meant to be a conscience, not the end all be all
implementation.

I understand what you are looking for. But to me, unless we implemented
function pointers (typeable callbacks), I can't see the generalized use
case. At least that isn't already solvable using classes, interfaces and
polymorphism...

Anthony

---

## #63380 — Stas Malyshev — 2012-10-12T21:01:01+00:00

URL: https://externals.io/message/63368#63380 · In reply to: #63368

Hi!

> Now if you pass a closure to the scan method which doesn't follow the 
> signature of the __invoke method, the engine should throw an error.
> 
> What do you think?

You are trying to take typing way beyond what PHP (or probably any
mainstream dynamic language that exists now) provides. There are
languages which provide static type systems capable of doing such
things, but PHP isn't one of them and I don't think it should be. PHP
has no static type control, and IMHO doing type safety validation at
runtime does not seem to be a good proposition for PHP.
-- 
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227

---

## #63381 — Nikita Popov — 2012-10-12T21:06:24+00:00

URL: https://externals.io/message/63368#63381 · In reply to: #63368

On Fri, Oct 12, 2012 at 10:58 AM, Christian Kaps
<christian.kaps@mohiva.com> wrote:
> At the moment it isn't possible to restrict/define the arguments for a
> closure which is defined as parameter for a method. Please look at this
> small example.
>
> interface Broker {
>
>     public function scan(Request $request, Closure $onFound);
> }
>
> An implementation of the interface could be as follows:
>
> class BrokerImpl implements Broker {
>
>     /**
>      * @var Service
>      */
>     private $service = null;
>
>     public function scan(Request $request, Closure $onFound) {
>
>         if ($request->contains('some value')) {
>             $onFound($this->service);
>         }
>     }
> }
>
> The problem is that I can pass every closure to the scan method.
>
> $broker = new BrokerImpl();
> $broker->scan($request, function() { /* do something */ });
>
> Sometimes I would like to restrict the closure passed to this method. So
> that only closures of a certain type which has an argument "Service
> $service" could be passed to this method.
>
> $broker = new BrokerImpl();
> $broker->scan($request, function(Service $service) { /* do something */ });
>
> Would it not be possible to extend the Closure class and then define an
> abstract method signature for the __invoke method.
>
> class OnFoundClosure extends Closure {
>
>     public abstract function __invoke(Service $service);
> }
>
> And then you can define the interface as follows:
> interface Broker {
>
>     public function scan(Request $request, OnFoundClosure $onFound);
> }
>
> Now if you pass a closure to the scan method which doesn't follow the
> signature of the __invoke method, the engine should throw an error.
>
> What do you think?

Please also don't forget that PHP doesn't have strict function
signatures. E.g. you can write

    function($foo) {
        echo $foo;
    }

but you could also write

    function() {
        $foo = func_get_arg(0);
        echo $foo;
    }

So I'm not sure just how much sense function signature checking makes in PHP ;)

Nikita

---

## #63391 — Christian Kaps — 2012-10-13T11:31:08+00:00

URL: https://externals.io/message/63368#63391 · In reply to: #63380

Hi
> > […]
> You are trying to take typing way beyond what PHP (or probably any
> mainstream dynamic language that exists now) provides. There are
> languages which provide static type systems capable of doing such
> things, but PHP isn't one of them and I don't think it should be. PHP
> has no static type control, and IMHO doing type safety validation at
> runtime does not seem to be a good proposition for PHP.

I thought it would be a good idea to make the closure functionality 
consistent with the rest of the language. Because closures are currently 
implemented as class. And with classes it is possible to define static 
type checks at runtime. The other thing is that I can define a static 
type control for closures. It works only the other way around. Because 
the caller can define the static type control and not the interface 
designer.

$closure = function(Foo $foo, Bar $bar) {};
function execute(Closure $closure) {
     $closure(1, 2);
}

execute($closure); // PHP Catchable fatal error:  Argument 1 passed to 
{closure}() must be an instance of Foo, integer given

Normally as interface designer I would like to define what a caller of 
the interface should be passed to a certain method. Yes I know with 
primitive types, this isn't possible in PHP. But with closure I have the 
option to restrict the interface. Sure I can do this only for one type 
of a closure. Quasi the the smallest common denominator. But it's possible.

So when we look at the above example then it throws an error, because 
the caller passes the wrong type of a closure to the interface. But what 
is wrong, I have passed a closure to the method! So it seems that the 
interface lies. And for me this is the inconsistency in the 
implementation. I cannot tell the caller, what type of closure he must 
pass to the interface. And yes, for me a closure can be of a different 
type. If closures have different parameter signatures than they are of a 
different type.

But this is only my subjective opinion. And I'm guessing by the 
reactions of my post that I'm all alone with it  (o;

Cheers,
Christian

---

## #63392 — Christian Kaps — 2012-10-13T11:36:03+00:00

URL: https://externals.io/message/63368#63392 · In reply to: #63381

Am 12.10.2012 23:06, schrieb Nikita Popov:
> On Fri, Oct 12, 2012 at 10:58 AM, Christian Kaps
> <christian.kaps@mohiva.com> wrote:
> > […]
> Please also don't forget that PHP doesn't have strict function
> signatures. E.g. you can write
>
>      function($foo) {
>          echo $foo;
>      }
>
> but you could also write
>
>      function() {
>          $foo = func_get_arg(0);
>          echo $foo;
>      }
>
> So I'm not sure just how much sense function signature checking makes in PHP ;)
>
> Nikita
>
What did you mean with strict function signatures?
Is this not a strict function signature?

function(Foo $foo) {}

Christian
