Skip to content

Instantly share code, notes, and snippets.

@mathiasverraes
Created July 4, 2014 12:27
Show Gist options
  • Save mathiasverraes/748472e59a77ccc2e1d3 to your computer and use it in GitHub Desktop.
Save mathiasverraes/748472e59a77ccc2e1d3 to your computer and use it in GitHub Desktop.
What functional life could be like in php
<?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
);
<?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