Created
July 4, 2014 12:27
-
-
Save mathiasverraes/748472e59a77ccc2e1d3 to your computer and use it in GitHub Desktop.
What functional life could be like in php
This file contains 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 | |
$addOne = partial(operator('+'), 1); | |
$double = partial(operator('*'), 2); | |
$greaterThan3 = partial(operator('>'), …(), 3); | |
assert( | |
map([1, 2, 3], $double)->§ | |
== [2, 4, 6] | |
); | |
assert( | |
map([1, 2, 3], $double)->filter($greaterThan3)->§ | |
== [4, 6] | |
); | |
assert( | |
map([1, 2, 3], compose($addOne, $double))->§ | |
== [4, 6, 8] | |
); | |
assert( | |
map([1, 2, 3], compose($double, $addOne))->fold(operator('+')) | |
== 15 | |
); | |
This file contains 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 | |
// threw together some code stolen from various FP libs all over da githubz | |
/** | |
* @property $§ Returns the Collection as an array | |
*/ | |
final class Collection extends ArrayObject | |
{ | |
public function __construct(array $input) | |
{ | |
parent::__construct($input, $flags = 0, $iterator_class = 'ArrayIterator'); | |
} | |
public function __get($name) | |
{ | |
switch($name) { | |
case '§': | |
return array_values($this->getArrayCopy()); | |
break; | |
default: | |
throw new BadMethodCallException; | |
} | |
} | |
public function map(callable $f) { | |
return new Collection(array_map($f, $this)); | |
} | |
public function filter(callable $f) { | |
return new Collection(array_filter($this->§, $f)); | |
} | |
public function fold(callable $f, $initial = 0) { | |
return array_reduce($this->§, $f, $initial); | |
} | |
} | |
function compose($f, $g) { | |
return function() use($f,$g) { | |
$x = func_get_args(); | |
return $g(call_user_func_array($f, $x)); | |
}; | |
} | |
function map(array $array, callable $f) { | |
return new Collection(array_map($f, $array)); | |
} | |
function filter(array $array, callable $f) { | |
return new Collection(array_filter($array, $f)); | |
} | |
function fold(array $array, callable $f, $initial = null) { | |
return new Collection(array_reduce($array, $f, $initial)); | |
} | |
function operator($operator, $arg = null) { | |
$functions = [ | |
'instanceof' => function($a, $b) { return $a instanceof $b; }, | |
'*' => function($a, $b) { return $a * $b; }, | |
'/' => function($a, $b) { return $a / $b; }, | |
'%' => function($a, $b) { return $a % $b; }, | |
'+' => function($a, $b) { return $a + $b; }, | |
'-' => function($a, $b) { return $a - $b; }, | |
'.' => function($a, $b) { return $a . $b; }, | |
'<<' => function($a, $b) { return $a << $b; }, | |
'>>' => function($a, $b) { return $a >> $b; }, | |
'<' => function($a, $b) { return $a < $b; }, | |
'<=' => function($a, $b) { return $a <= $b; }, | |
'>' => function($a, $b) { return $a > $b; }, | |
'>=' => function($a, $b) { return $a >= $b; }, | |
'==' => function($a, $b) { return $a == $b; }, | |
'!=' => function($a, $b) { return $a != $b; }, | |
'===' => function($a, $b) { return $a === $b; }, | |
'!==' => function($a, $b) { return $a !== $b; }, | |
'&' => function($a, $b) { return $a & $b; }, | |
'^' => function($a, $b) { return $a ^ $b; }, | |
'|' => function($a, $b) { return $a | $b; }, | |
'&&' => function($a, $b) { return $a && $b; }, | |
'||' => function($a, $b) { return $a || $b; }, | |
]; | |
if (!isset($functions[$operator])) { | |
throw new \InvalidArgumentException("Unknown operator \"$operator\""); | |
} | |
$fn = $functions[$operator]; | |
if (func_num_args() === 1) { | |
return $fn; | |
} else { | |
return function($a) use ($fn, $arg) { | |
return $fn($a, $arg); | |
}; | |
} | |
} | |
function not($function) { | |
return function() use ($function) { | |
return !call_user_func_array($function, func_get_args()); | |
}; | |
} | |
function partial(callable $f /* , $args...*/) | |
{ | |
$args = func_get_args(); | |
array_shift($args); | |
return function () use ($f, $args) { | |
return call_user_func_array($f, mergeParameters($args, func_get_args())); | |
}; | |
} | |
/** | |
* @return Placeholder | |
*/ | |
function …() | |
{ | |
return Placeholder::create(); | |
} | |
/** | |
* @return Placeholder | |
*/ | |
function placeholder() | |
{ | |
return …(); | |
} | |
/** @internal */ | |
function mergeParameters(array $left, array $right) | |
{ | |
foreach ($left as $position => &$param) { | |
if ($param instanceof Placeholder) { | |
$param = $param->resolve($right, $position); | |
} | |
} | |
return array_merge($left, $right); | |
} | |
final class Placeholder | |
{ | |
private static $instance; | |
private function __construct() | |
{ | |
} | |
public static function create() | |
{ | |
if (self::$instance === null) { | |
self::$instance = new self(); | |
} | |
return self::$instance; | |
} | |
public function resolve(array &$args, $position) | |
{ | |
if (count($args) === 0) { | |
throw new \InvalidArgumentException( | |
sprintf('Cannot resolve parameter placeholder at position %d. Parameter stack is empty', $position) | |
); | |
} | |
return array_shift($args); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment