Skip to content

Instantly share code, notes, and snippets.

@el-chogo
Last active April 28, 2016 04:05
Show Gist options
  • Save el-chogo/de33a39b39917e956bdfd60edaf3a10c to your computer and use it in GitHub Desktop.
Save el-chogo/de33a39b39917e956bdfd60edaf3a10c to your computer and use it in GitHub Desktop.
<?php
namespace Linoge\Functional;
use ReflectionFunction;
use ReflectionMethod;
use ReflectionClass;
class F {
public static function trace() {
return call_user_func_array(F::curry(function($args) {
if (is_array($args))
{
call_user_func_array('print_r', $args);
} else {
call_user_func_array('print_r', [$args]);
}
return $args;
}), func_get_args());
}
public static function add() {
return call_user_func_array(F::curry(function($x,$y) {
return $x + $y;
}), func_get_args());
}
public static function __curry($fn, $args, $n, $right = false, $firstCall = true) {
if ($n == 0) {
if ($right) {
return call_user_func_array($fn, array_reverse($args));
}
return call_user_func_array($fn, $args);
} else {
return function() use ($fn, $args, $n, $right, $firstCall) {
$args1 = func_get_args();
$fargs = array_merge($args, $args1);
if ($firstCall == false && count($args) == 0 ) {
return F::__curry($fn, $fargs, 0, false);
}
return F::__curry($fn,
$fargs,
$n - count($args1), $right, false);
};
}
}
public static function safeProp() {
return call_user_func_array(F::curry(function($prop, $obj) {
return Maybe::of(F::prop($prop, $obj));
}), func_get_args());
}
public static function curry($fn) {
$rfl = new ReflectionFunction($fn);
$args = $rfl->getNumberOfParameters();
return F::__curry($fn, [], $args);
}
public static function curryN($fn, $n) {
if ($n == 0) {
return $fn;
}
else {
return F::__curry($fn, [], $n);
}
}
public static function curryRight($fn) {
$rfl = new ReflectionFunction($fn);
$args = $rfl->getNumberOfParameters();
return F::__curry($fn, [], $args, true);
}
public static function prop() {
return call_user_func_array(F::curry(
function($prop, $obj) {
if (!is_array($obj)) {
if (isset($obj->{$prop})) {
return $obj->{$prop};
}
return null;
} else {
if (isset($obj[$prop])) {
return $obj[$prop];
}
return null;
}
}), func_get_args());
}
public static function call() {
return call_user_func_array(F::curry(
function($fn, $args) {
if (is_array($args)) {
return call_user_func_array($fn, $args);
} else {
return call_user_func_array($fn, [$args]);
}
}), func_get_args());
}
public static function setProp() {
return call_user_func_array(F::curry(
function($prop, $new_val, $obj) {
$o = clone $obj;
$o->{$prop} = $new_val;
return $o;
}), func_get_args());
}
public static function method() {
return call_user_func_array(F::curry(
function($method, $args, $obj) {
return call_user_func_array([$obj, $method], $args);
}), func_get_args());
}
public static function hasMethod() {
return call_user_func_array(F::curry(
function($method, $obj) {
return method_exists($obj, $method);
}), func_get_args());
}
public static function map() {
return call_user_func_array(F::curry(
function ($fn, $xs) {
if (is_object($xs)
&& is_subclass_of($xs, 'Linoge\Functional\Monad')
&& F::hasMethod('map', $xs)) {
return F::method('map', [$fn], $xs);
}
$new_arr = [];
foreach($xs as $x) {
$new_arr[] = $fn($x);
}
return $new_arr;
}), func_get_args());
}
public static function id() {
return call_user_func_array(F::curry(
function($x) {
return $x;
}), func_get_args());
}
public static function filter() {
return call_user_func_array(F::curry(function($fn, $xs) {
return array_filter($fn, $arr);
}), func_get_args());
}
public static function last() {
return call_user_func_array(F::curry(
function($xs) {
$length = count($xs);
return $xs[$length - 1];
}), func_get_args());
}
public static function and() {
return call_user_func_array(F::curry(
function ($x, $y) {
return $x && $y;
}), func_get_args());
}
public static function every() {
return call_user_func_array(F::curry(
function($f, $xs) {
$res = true;
foreach($xs as $x) {
$res = $res && $f($x);
}
return $res;
}), func_get_args());
}
public static function not() {
return call_user_func_array(F::curry(
function($f, $x) {
return ! $f($x);
}), func_get_args());
}
public static function join() {
return call_user_func_array(F::curry(
function($monad) {
if (is_object($monad)) {
return $monad->join();
}
// Arrays are monads
if (is_array($monad)) {
if (count($monad) == 1
&& is_array($monad[0])) {
return $monad[0];
}
}
}), func_get_args());
}
public static function chain() {
return call_user_func_array(F::curry(
function($f, $monad) {
return $monad->map($f)->join();
}), func_get_args());
}
public static function compose() {
$composeBinary = function ($f, $g) {
return function($x = null) use ($f,$g) {
if ($x) {
return $f($g($x));
} else {
return $f($g());
}
};
};
return array_reduce(func_get_args(), $composeBinary, F::id());
}
}
abstract class Monad {
public abstract function map($x);
public abstract function join();
}
class Maybe extends Monad {
public function __construct($x) {
$this->value = $x;
}
public static function of($x) {
return new self($x);
}
public function map($f) {
if (F::prop('value', $this) != null) {
return new $this($f(F::prop('value', $this)));
}
else {
return $this;
}
}
public function join() {
if (is_object($this->value) &&
get_class($this->value) == get_class($this)) {
return $this->value;
} else {
return $this;
}
}
}
class IO extends Monad {
public function __construct($fn) {
$this->value = $fn;
}
public static function of($x) {
return new self(function() use ($x) {
return $x;
});
}
public function map($fn) {
return new $this(F::compose($fn, F::prop('value', $this)));
}
public function __unsafePerformIO() {
return ($this->value)();
}
public function join() {
$thiz = $this;
return new IO(function() use ($thiz) {
$stg1 = $thiz->__unsafePerformIO();
if (is_object($stg1) && get_class($stg1) == get_class($this)) {
return $thiz->__unsafePerformIO()->__unsafePerformIO();
}
return $stg1;
});
}
}
// Curries every method of any object, including constructor!
class CurryTransform {
public function __construct($x) {
$this->contained = $x;
}
public function __call($name, $args) {
$rflMethod = new ReflectionMethod(get_class($this->contained), $name);
$params = $rflMethod->getNumberOfParameters();
return call_user_func_array(F::curryN(
function() use ($name) {
return F::method($name, func_get_args(), $this->contained);
}, $params), $args);
}
public static function take($className) {
$rflClass = new ReflectionClass($className);
$params =$rflClass->getConstructor()->getNumberOfParameters();
return call_user_func_array(F::curryN(
function() use ($rflClass) {
return new self($rflClass->newInstanceArgs(func_get_args()));
}, $params), array_diff(func_get_args(), [$className]));
}
}
@el-chogo
Copy link
Author

el-chogo commented Apr 27, 2016

I don't know if CurryTransform would be the actual name for the thing I've created. It intends to automatically curry every object function.

I think it's more of a wrapper. But thought it was a cool name.

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