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


$UserModel->getWhere(Q::andWhere(
Q::is('name', 'shaun'),
Q::orWhere(
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:


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


I pine for quasiquote:

$userModel->getWhere(
`(and
(= name shaun)
(or
(between age 69 100)
(like l_name %name%))));

Tuesday, July 27, 2010

quasiquote for primitive lisp

I was searching for a basic quasiquote implementation in lisp which mentioned a paper by Alan Bawden. Anyway it was difficult to find via google so I figured I would type it up here for posterity. The original paper can be found: here.


(define (qq-expand x)
(cond ((tag-comma? x)
(tag-data x))
((tag-comma-atsign? x)
(error "Illegal"))
((tag-backquote? x)
(qq-expand (qq-expand (tag-data x))))
((pair? x)
`(append ,(qq-expand-list (car x))
,(qq-expand (cdr x))))
(else '', x)))

(define (qq-expand-list x)
(cond ((tag-comma? x)
`(list ,(tag-data x)))
((tag-comma-atsign? x)
(tag-data x))
((tag-backquote? x)
(qq-expand-list (qq-expand (tag-data x))))
((pair? x)
`(list (append ,(qq-expand-list (car x))
,(qq-expand (cdr x)))))
(else ''(,x))))

Monday, July 19, 2010

PHP to javascript fluent expressions

There are many times in Voltron views where I need to make a jquery/javascript call, initially I compromised and created the UI::ScriptSnippet in which I would put the typical "$(document).ready(function(){})" type deal in place. This was becoming increasingly more obnoxious particularly when the rest of the view code was so clean. So I give you the JSCallBuilder:


UI::JSCall('object')->method(array('a' => 'b'));
// renders to object.method({'a': 'b'});

UI::JSReady('object')->method(array('a' => 'b'));
//renders to <\script type="text/javascript">$(document).ready(function(){ object.method({'a', 'b'}) });<\/script>
*/note the escaped script tags for bloggers sake? */


The source to make this happens is relatively trivial: http://code.google.com/p/phpviewadapter/source/browse/trunk/UI/JSCallBuilder.php

Saturday, July 17, 2010

solving the word cube in voltron

In response to: http://programmingpraxis.com/2010/07/13/word-cube/


/* returns array of words found in the 9 letters provided */
function solveCube($letters) {
$words = newType(TFile, 'words.txt')->splitBy("\n");

return newType(TString, $letters)
->asArray
->powerSet('contains', $letters[4])
->map('permutations', I('asString')->in($words))
->map('join');
}

echo solveCube('ncbciune');


There are a few alternative ways that could be written and I am not sure if the idea of just having an optional filter as arguments to powerSet and permutations is entirely intuitive but the reality is that w/o the filter permutations will die on anything over 8 unless you crank the memory up in php.ini and thats just filthy.

Next post I will delve into iterative permutation and powerset internals on Array as that is obviously where the meat of that solution lies.

Monday, July 5, 2010

voltron model calculated fields

In the field definition of a voltron model you usually specify the field and the type as a key => val associative array. In the case that you have a calculated field the current practice is to specify field => array(Type::Calculated, 'methodNameOnRecord'). With fluent lambdas I have added a short cut:


class TimeRecord extends Voltron_Model
{
protected $table = 'time_record';

protected $fields = array(
'id' => Type::Primary,
'created' => Type::Timestamp,
'created_hour' => Type::Calculated('created')->asDateTime->formatAs('H'));
}


The magic lies with in the created_hour type definition which can also be written as Type::Calculated(I()->created->asDateTime->formatAs('H')).

Because of the oo and fluent nature of Voltron the majority of calculated fields can be defined this way rather than requiring an actual method definition in the record class.

Wednesday, June 30, 2010

voltron and "fluent lambdas"

For lack of a better name I am sticking with "fluent lambda" which essentially is how I get around the lack of nice lambdas in php. Even in php 5.3 I find the function($val) { return $val + 1; } syntax to be overkill. I would much rather see L(val)->add(1)

Lets say you have a list of names and you want to reverse them, uppercase them and then join them by an & symbol. Currently in raw php you have a few options but the cleanest is probably something to the effect of either:

Idiomatic php:

$names = array('peter', 'paul', 'mary');
foreach($names as $key => $val) {
$names[$key] = strtoupper(strrev($val));
}
echo join('&', $names);


Functional php:

$names = array('peter', 'paul', 'mary');
echo join('&', array_map(create_function('$val', 'return strtoupper(strrev($val));'), $names));


Functional php 5.3:

$names = array('peter', 'paul', 'mary');
echo join('&', array_map(function($val) { return strtoupper(strrev($val)); }, $names))


Voltron:

$names = newArray(VString, 'peter', 'paul', 'mary');
echo $names->map(L(val)->reverse->upper)->join('&');


This works by the global function L which takes either 'key' or 'val as an argument and then returns a created function which passes along the same method chain.

Where the real advantage comes to not passing around "string lambdas" i.e. the create_function approach - is when you start dealing with nested lambdas i.e.

functional php:

$listOfList = array(
array('a', 'b', 'c'),
array('d', 'e', 'f'));

echo join('<hr>', array_map(create_function('$val', 'return join(\',\', array_map(create_function(\'$val\', \'return strtoupper($val);\'), $val));'), $listOfList));


Voltron:

$listOfList = newArray(VArray,
newArray(VString, 'a', 'b', 'c'),
newArray(VString, 'd', 'e', 'f'));

echo $listOfList->map(L(val)->map(L(val)->upper)->join(','))->join('<hr>');


So whats next? I am probably going to integrate this with my closure class from my lisp so that you can pass in vars, but immediately my needs are mainly for calling methods on objects in a given array. But I can imagine that being one of the first annoyances people run into.

Basically I am trying to get at the idea that oneliners don't have to be made of gnarleston heston.

perl:

@p=(0,1);until($#p>20){print"$p[-2]\n";push @p,$p[-2]+$p[-1]}


Voltron:

echo newRange(0, 1)->expand(L(x)->add(y), 20)->join("\n");


Here is an example of finding primes.

Python:

noprimes = [j for i in range(2, 8) for j in range(i*2, 50, i)]
primes = [x for x in range(2, 50) if x not in noprimes]


Voltron:

$noprimes = newRange(2, 8)->map(N(VRange, L(y)->times(2), 50, y))->flatten;
$primes = newRange(2, 50)->diff($noprimes);

Wednesday, September 30, 2009

APL jot dot in scheme and php

So I am implementing APL in php... As a matter of practice of course I am prototyping things. The ultimate scheme yields an oo/fluent expression approach but immediately here is how I would implement jot dot in php and scheme:

PHP:

function jotDot($fun, $v1, $v2) {
$result = array();
foreach($v1 as $x) {
$result[$x] = array();
foreach($v2 as $y) $result[$x][$y] = $fun($x, $y);
}
return $result;
}


Scheme:

(define (mapcar func list)
(if (null? list) '()
(cons (func (car list)) (mapcar func (cdr list)))))

(define (jotDot func v1 v2)
(mapcar (lambda (x) (mapcar (lambda (y) (func x y)) v2)) v1))


I am not going to lie - the scheme is definitely more elegant but it is not as readable as the php. I think that is sort of interesting. Maybe mapcar is not the right abstraction for the job and there is a better approach. Oh yeah in APL:

The eventual "fluent expression" APL->php interpreted code to do something like calculating a times table up to 12 would look like:

$V = APL()->indexGenerator(12);
$V->jotDot('times', $V);