skiphog
12/5/2016 - 4:23 PM

cljphp - implementing clojure std lib in php - http://blackwood.io/porting-clojure-php-better-functional-programming/

cljphp - implementing clojure std lib in php - http://blackwood.io/porting-clojure-php-better-functional-programming/

<?php

/**
 * Returns the first item in an array.
 */
function first(array $arr) {
	$copy = array_slice($arr, 0, 1, true);
	return array_shift($copy);
}

/**
 * Returns the last item in an array.
 */
function last(array $arr) {
	$copy = array_slice($arr, 0, NULL, true);
	return array_pop($copy);
}

/**
 * Returns all but the first item in an array.
 */
function rest(array $arr) {
	return array_slice($arr, 1, NULL, true);
}

/**
 * Returns all but the last item in an array.
 */
function butlast(array $arr) {
	$copy = array_slice($arr, 0, -1, true);
	return $copy;
}

/**
 * Returns true if the given predicate is true for all elements.
 * credit: array_every and array_some.php
 * https://gist.github.com/kid-icarus/8661319
 */
function every($callback, array $arr) {
  foreach ($arr as $element) {
    if (!$callback($element)) {
      return FALSE;
    }
  }
  return TRUE;
}

/**
 * Returns true if the given predicate is true for at least one element.
 * credit: array_every and array_some.php
 * https://gist.github.com/kid-icarus/8661319
 */
function some($callback, array $arr) {
  foreach ($arr as $element) {
    if ($callback($element)) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Returns true if the given predicate is not true for all elements.
 */
function not_every($callback, array $arr) {
	return !every($callable, $arr);
}

/**
 * Alias call_user_func_array to apply.
 */
function apply($callback, array $args) {
	return call_user_func_array($callback, $args);
}

/**
 * Takes a fn f and returns a fn that takes the same args as f,
 * has the same effects, if any, and returns the opposite truth value.
 */
function complement($f) {
	return function() use ($f) {
		$args = func_get_args();
		return !apply($f, $args);
	};
}

/**
 * These are the standard arities for these functions in PHP.
 * 
 * array_map($callback, $arr)
 * array_filter($arr, $callback, $flag=NULL)
 * array_reduce($arr, $callback, $initial=NULL)
 *
 * Lets normalize them.
 */

/**
 * Applies to each item in array, return new array.
 */
function map($callback, array $arr) {
	return array_map($callback, $arr);
}

/**
 * Return a new array with elements for which predicate returns true.
 */
function filter($callback, array $arr, $flag=0) {
	return array_filter($arr, $callback, $flag);
}

/**
 * Return a new array with elements for which predicate returns false.
 */
function remove($callback, array $arr, $flag=0) {
	return filter(complement($callback), $arr, $flag);
}

/**
 * cons(truct)
 * Returns a new array where x is the first element and $arr is the rest.
 */
function cons($x, array $arr) {
	return array_merge(array($x), $arr);
}

/**
 * conj(oin)
 * Returns a new arr with the xs added.
 * @param $arr
 * @param & xs add'l args to be added to $arr.
 */
function conj() {
	$args = func_get_args();
	$arr  = first($args);
	return array_merge($arr, rest($args));
}

/**
 * Alias array_merge to concat.
 */
function concat() {
	$arrs = func_get_args();
	return apply('array_merge', $arrs);
}

/**
 * Returns a sequence of the first item in each collection then the second, etc.
 */
function interleave() {
	$arrs = func_get_args();
	$firsts = map('first', $arrs);
	$rests  = map('rest', $arrs);
	if (every(function($a) { return !empty($a); }, $rests)) {
		return concat($firsts, apply('interleave', $rests));
	}
	return $firsts;
}

/**
 * Alias implode to interpose.
 */
function interpose($glue, array $pieces) {
	return implode($glue, $pieces);
}

/**
 * Returns true if number is even.
 */
function even($n) {
	return $n % 2 == 0;
}

/**
 * Returns true if number is odd.
 */
function odd($n) {
	return complement('even');
}

/**
 * Takes a set of fns, returns new fn of variable args
 * that applies the rightmost fn, and so on.
 * Note on lambdas:
 * https://php100.wordpress.com/2009/04/13/php-y-combinator/
 */
function comp() {
	$fns = func_get_args();

	return function() use ($fns) {
		$args = func_get_args();

		$l = function($xs, $fns) use (&$l) {
			if (empty($fns)) {
				return $xs;
			}
			return $l(apply(last($fns), (array)$xs), butlast($fns));
		};

		return $l($args, $fns);
	};
}

function juxt() {
	$fns = func_get_args();

	return function() use ($fns) {
		$args = func_get_args();

		$l = function($fn) use ($args) {
			return apply($fn, $args);
		};

		return map($l, $fns);
	};
}

/**
 * @TODO
 */
function loop(array $bindings) {

}