Last active
April 28, 2016 04:05
-
-
Save el-chogo/de33a39b39917e956bdfd60edaf3a10c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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])); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.