Skip to content

Instantly share code, notes, and snippets.

@mudge
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mudge/8832334 to your computer and use it in GitHub Desktop.
Save mudge/8832334 to your computer and use it in GitHub Desktop.
A functional conundrum (that isn't PHP specific).
<?php
/* Given an anonymous function with a previously unspecified number of arguments like so: */
$f = function ($x, $y, $z) {
/* Do something interesting with $x, $y and $z. */
return;
};
/* How can I convert it into the following nested function (each function yielding one argument),
* finally calling $f when there are enough arguments?
*/
foo(function ($x) {
return foo(function ($y) use ($x) {
return foo(function ($z) use ($x, $y) {
return $f($x, $y, $z);
});
});
});
@mudge
Copy link
Author

mudge commented Feb 5, 2014

Note that I can introspect the arguments from the given function like so:

<?php
$reflection = new \ReflectionFunction($f);
$reflection->getNumberOfParameters();

This issue is more about how to build the nested function calls.

@mattmacleod
Copy link

This is just currying, I think. In Ruby, it'd be:

irb(main):001:0> f = proc { |a,b,c| puts a,b,c }.curry
=> #<Proc:0x007f91d885d6f0>
irb(main):002:0> f.call(1).call(2).call(3)
1
2
3
=> nil

You can use func_get_args() and call_user_func_array() to do something similar in PHP, though it's ugly:

function f(){
  $x = func_get_args();
  return function($y = null) use ($x) {
    if (is_null($y)){
      // Do something with the combined array
      print_r($x);
    } else {
      array_push($x, $y);
      return call_user_func_array("f",$x);
    }
  };
}

echo f(1)
  ->__invoke(2)
  ->__invoke(3)
  ->__invoke(4)
  ->__invoke(); 

=> Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
)

@mudge
Copy link
Author

mudge commented Feb 5, 2014

Thanks, @mattmacleod: you're right, it is bit of the old schönfinkeling.

My final implementation was taken from underscore-contrib's implementation of curry as I need to call my own function at each level to yield a new argument, only executing the original function at the end: https://github.com/mudge/php-microkanren/blob/master/src/MicroKanren/Mini.php#L58-L94

Reproduced here for posterity:

<?php
function fresh($f)
{
    $reflection = new \ReflectionFunction($f);
    $argCount = $reflection->getNumberOfParameters();

    if ($argCount === 0) {
        return $f();
    } else {
        return callFresh(function ($x) use ($f, $argCount) {
            return collectArgs($f, $argCount, array(), $x);
        });
    }
}

function collectArgs($f, $argCount, $args, $arg)
{
    $args[] = $arg;

    if (count($args) === $argCount) {
        return call_user_func_array($f, $args);
    } else {
        return callFresh(function ($x) use ($f, $argCount, $args) {
            return collectArgs($f, $argCount, $args, $x);
        });
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment