Tuesday, August 24, 2010

transmogrified function composition

Using the transmogrifier something like function composition such as:

(define (compose . fs)
(if (null? fs) (lambda (x) x)
(lambda (x) ((car fs) ((apply compose (cdr fs)) x)))))

Becomes possible like:

$compose = [$funcs |
empty($funcs) ? [$_] :

Which becomes:

$compose = function($funcs) use(&$compose) {
return empty($funcs) ? function($x) { return $x; } :
function($_ = false) use(&$funcs, &$compose) {
return apply(car($funcs), array(apply($compose(cdr($funcs)), array($_))));};};

I am not doing an exact copy here because I am not using ". rest" args as I would probably call this function like:

echo $compose({square negate cube})(500);
//shorter than
echo $compose('square', 'negate', 'cube')(500);

I think the above illustrates that really php IS capable of these sorts of things it's just that in it's incredibly gnarly. With a few syntax tweaks (square bracket closures w/ lexical scope, squiggly bracket arrays w/ barewords as strings, ability to use return value as function) - it's almost livable.

Friday, August 13, 2010

php quasiquote preprocessor

Sometimes itches must be scratched. Last nights musing leads to tonights finished quasiquote for php: http://github.com/shaunxcode/php-quasiquote-preprocessor. It was nice to implement just quasiquote ignoring the rest of the lisp reader as before I had tried to "Add" quasiquote to my nearly complete lisp. As I discovered this was a mistake. In the future I am going to start the reader with quasiquote and build as much of the system with macros as possible.

Thursday, August 12, 2010

Refactoring Voltron query DSL

This is an area of the framework I have not had a need to look at in a while and from this latest inspection it was blatantly not very well thought out. Admitting your own fallibility, I think, is probably the first step to being able to refactor your own code. Previously I had decided I really wanted to avoid seeing stuff like:

$UserModel->getWhere(array('type' => 'and', value' => array(
array('type' => '=', 'field' => 'name', 'value' => 'shaun'),
array('type' => 'or', 'value' => array(
array('type' => 'between', 'field' => 'age', 'value' => array(69, 100)),
array('type' => 'like', 'field' => 'l_name', 'value' => '%name%'))))));

So I went with a half baked dsl

Q::is('name', 'shaun'),
Q::is('age', Q::between(array(69, 100))),
Q::is('l_name', Q::like('%name%')))));

On first inspection that is not too bad but it rapidly gets out of control if you need to actually do something more in depth (nested or statements etc.). Not only that but all of the :: is pretty ugly as is the fact I can't use words like 'and', 'or' '=' etc. as method names. I also had this idea that "is" should be used for all operators. I have now abandoned that in the name of consistency and to reduce the complexity of the code which actually generates the sql.

Taking a prefix notation approach and refactoring W (for where instead of Q for query, leaving room for say F for the from stuff for defining joins and S for select) to merely be a global function which returns arrays like in the first example:

W('=', 'name', 'shaun'),
W('between', 'age', 69, 100),
W('like', 'l_name', '%name%'))));

I pine for quasiquote:

(= name shaun)
(between age 69 100)
(like l_name %name%))));