Skip to content

Instantly share code, notes, and snippets.

@jeremeamia
Last active April 19, 2017 18:15
Show Gist options
  • Save jeremeamia/bafd3557a35c8949f64d57f520169a68 to your computer and use it in GitHub Desktop.
Save jeremeamia/bafd3557a35c8949f64d57f520169a68 to your computer and use it in GitHub Desktop.

Revisions

  1. jeremeamia revised this gist Apr 19, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion fn.php
    Original file line number Diff line number Diff line change
    @@ -57,7 +57,7 @@ function from_method($class, $method = null) {
    }

    list($class, $object) = determine_subject($class);
    if (!method_exists($object ?: $class, $method)) {
    if (!method_exists($class, $method)) {
    throw new IAE('Expected the method to exist.');
    }

  2. jeremeamia revised this gist Apr 19, 2017. 1 changed file with 115 additions and 104 deletions.
    219 changes: 115 additions & 104 deletions fn.php
    Original file line number Diff line number Diff line change
    @@ -1,119 +1,130 @@
    <?php

    namespace fn;

    use Closure as Fn;
    use InvalidArgumentException as IAE;

    const SKIP = "\0...\0";

    /**
    * @param callable $fn
    * @param mixed $scope
    * @return Fn
    */
    function scope(callable $fn, $scope) {
    $object = null;
    if (is_object($scope)) {
    $object = $scope;
    $scope = get_class($scope);
    namespace fn {
    use Closure as Fn;
    use InvalidArgumentException as IAE;

    const SKIP = "\0...\0";

    /**
    * @param callable $fn
    * @param mixed $scope
    * @return Fn
    */
    function scope(callable $fn, $scope) {
    list($scope, $object) = determine_subject($scope);

    return from_callable($fn)->bindTo($object, $scope);
    }

    return closure($fn)->bindTo($object, $scope);
    }

    /**
    * @param string $function
    * @return Fn
    */
    function func($function) {
    if (!is_string($function)) {
    throw new IAE('Expected the name of a function or static method.');

    /**
    * @param string $function
    * @return Fn
    */
    function from_function($function) {
    if (!is_string($function)) {
    throw new IAE('Expected the name of a function or static method to be provided.');
    }

    if (strpos($function, '::') > 0) {
    return from_method(...explode('::', $function, 2));
    }

    if (!is_callable($function)) {
    throw new IAE('Expected the function to exist and be callable.');
    }

    return from_callable($function);
    }

    if (strpos($function, '::') > 0) {
    return method_to_callable(...explode('::', $function, 2));

    /**
    * @param mixed $class
    * @param string|null $method
    * @return Fn
    */
    function from_method($class, $method = null) {
    // Supports:
    // - `$class = ['class', 'method'], $method = null
    // - `$class = [$object, 'method'], $method = null
    // - `$class = 'class', $method = 'method'
    // - `$class = $object, $method = 'method'
    if (!$method) {
    if (is_array($class) && count($class) === 2) {
    list($class, $method) = $class;
    } else {
    throw new IAE('Expected method name to be provided.');
    }
    }

    list($class, $object) = determine_subject($class);
    if (!method_exists($object ?: $class, $method)) {
    throw new IAE('Expected the method to exist.');
    }

    if ($object) {
    return scope(function (...$args) use ($object, $method) {
    return $object->{$method}(...$args);
    }, $object);
    } else {
    return scope(function (...$args) use ($class, $method) {
    return $class::{$method}(...$args);
    }, $class);
    }
    }

    if (!is_callable($function)) {
    throw new IAE('Expected the function to exist and be callable.');

    /**
    * @param callable $fn
    * @return Fn
    */
    function from_callable(callable $fn) {
    return $fn instanceof Fn ? $fn : function (...$args) use ($fn) {
    return $fn(...$args);
    };
    }

    return closure($function);
    }

    /**
    * @param mixed $class
    * @param string|null $method
    * @return Fn
    */
    function method($class, $method = null) {
    if (!$method && is_array($class) && count($class) === 2) {
    $object = null;
    list($class, $method) = $class;
    } elseif (is_string($class) && is_string($method)) {

    function determine_subject($class) {
    $object = null;
    } elseif (is_object($class) && is_string($method)) {
    $object = $class;
    $class = get_class($object);
    } else {
    throw new IAE('Expected a tuple containing a class name (or object instance) and method name.');
    }

    $subject = $object ?: $class;
    if (!method_exists($subject, $method)) {
    throw new IAE('Expected the method to exist.');
    if (is_object($class)) {
    $object = $class;
    $class = get_class($object);
    } elseif (!is_string($class)) {
    throw new IAE('Expected class name or object instance to be provided.');
    }

    return [$class, $object];
    }

    if ($object) {
    return scope(function (...$args) use ($object, $method) {
    return $object->{$method}(...$args);
    }, $object);
    } else {
    return scope(function (...$args) use ($class, $method) {
    return $class::{$method}(...$args);
    }, $class);
    /**
    * Creates a partial application of a function.
    *
    * @param callable $fn
    * @param mixed[] $args
    * @return Fn
    */
    function apply(callable $fn, ...$args) {
    return function (...$partialArgs) use ($fn, $args) {
    foreach ($args as &$arg) {
    if ($arg === SKIP) {
    $arg = array_shift($partialArgs);
    }
    }

    return $fn(...array_merge($args, $partialArgs));
    };
    }
    }

    /**
    * @param callable $fn
    * @return Fn
    */
    function closure(callable $fn) {
    return $fn instanceof Fn ? $fn : function (...$args) use ($fn) {
    return $fn(...$args);
    };
    }

    /**
    * Creates a partial application of a function.
    *
    * @param callable $fn
    * @param mixed[] $args
    * @return Fn
    */
    function apply(callable $fn, ...$args) {
    return function (...$partialArgs) use ($fn, $args) {
    foreach ($args as &$arg) {
    if ($arg === SKIP) {
    $arg = array_shift($partialArgs);
    }
    namespace {
    class Five {
    private static $five = 5;
    private static function getFive() {
    return self::$five;
    }

    return $fn(...array_merge($args, $partialArgs));
    };
    }

    class Five {
    private static $five = 5;
    private static function getFive() {
    return self::$five;
    }
    }

    $trimColons = apply('trim', SKIP, ':');
    var_dump($trimColons(':foo:bar:'));

    $trimColons = fn\apply('trim', fn\SKIP, ':');
    var_dump($trimColons(':foo:bar:'));

    $getFive = method(Five::class, 'getFive');
    var_dump($getFive());
    $getFive = fn\from_method(Five::class, 'getFive');
    var_dump($getFive());
    }
  3. jeremeamia created this gist Apr 19, 2017.
    119 changes: 119 additions & 0 deletions fn.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,119 @@
    <?php

    namespace fn;

    use Closure as Fn;
    use InvalidArgumentException as IAE;

    const SKIP = "\0...\0";

    /**
    * @param callable $fn
    * @param mixed $scope
    * @return Fn
    */
    function scope(callable $fn, $scope) {
    $object = null;
    if (is_object($scope)) {
    $object = $scope;
    $scope = get_class($scope);
    }

    return closure($fn)->bindTo($object, $scope);
    }

    /**
    * @param string $function
    * @return Fn
    */
    function func($function) {
    if (!is_string($function)) {
    throw new IAE('Expected the name of a function or static method.');
    }

    if (strpos($function, '::') > 0) {
    return method_to_callable(...explode('::', $function, 2));
    }

    if (!is_callable($function)) {
    throw new IAE('Expected the function to exist and be callable.');
    }

    return closure($function);
    }

    /**
    * @param mixed $class
    * @param string|null $method
    * @return Fn
    */
    function method($class, $method = null) {
    if (!$method && is_array($class) && count($class) === 2) {
    $object = null;
    list($class, $method) = $class;
    } elseif (is_string($class) && is_string($method)) {
    $object = null;
    } elseif (is_object($class) && is_string($method)) {
    $object = $class;
    $class = get_class($object);
    } else {
    throw new IAE('Expected a tuple containing a class name (or object instance) and method name.');
    }

    $subject = $object ?: $class;
    if (!method_exists($subject, $method)) {
    throw new IAE('Expected the method to exist.');
    }

    if ($object) {
    return scope(function (...$args) use ($object, $method) {
    return $object->{$method}(...$args);
    }, $object);
    } else {
    return scope(function (...$args) use ($class, $method) {
    return $class::{$method}(...$args);
    }, $class);
    }
    }

    /**
    * @param callable $fn
    * @return Fn
    */
    function closure(callable $fn) {
    return $fn instanceof Fn ? $fn : function (...$args) use ($fn) {
    return $fn(...$args);
    };
    }

    /**
    * Creates a partial application of a function.
    *
    * @param callable $fn
    * @param mixed[] $args
    * @return Fn
    */
    function apply(callable $fn, ...$args) {
    return function (...$partialArgs) use ($fn, $args) {
    foreach ($args as &$arg) {
    if ($arg === SKIP) {
    $arg = array_shift($partialArgs);
    }
    }

    return $fn(...array_merge($args, $partialArgs));
    };
    }

    class Five {
    private static $five = 5;
    private static function getFive() {
    return self::$five;
    }
    }

    $trimColons = apply('trim', SKIP, ':');
    var_dump($trimColons(':foo:bar:'));

    $getFive = method(Five::class, 'getFive');
    var_dump($getFive());