tag:blogger.com,1999:blog-85824038128436012082023-11-15T06:45:04.226-08:00Common PHPAn exploration of the limits of PHPUnknownnoreply@blogger.comBlogger53125tag:blogger.com,1999:blog-8582403812843601208.post-4584912004911533102011-08-17T09:53:00.000-07:002011-08-17T10:06:27.505-07:00php 5.4 - skin deep syntactic sugarI was quite excited to see that php 5.4 was getting some sugar for array definitions. e.g. "array(1,2,3)" would finally become "[1,2,3]". I was also excited to see "array dereferencing" was fixed or accessing an array by index after it is returned from a function e.g. "func()[index]". However let us be clear in noting that this does NOT mean it has attained the flexibility one would anticipate. For instance you can not immediately access an array after declaring it such as "['name' => 'peter', 'age' => 30]['age']" results in a syntax error. Of course you could combine both of the new features to do "func(['name' => 'peter', 'age' => 03])['age']" - which probably covers the majority of ways in which it <i>would</i> be used. The other issue I have is the inability to access a function upon it's return from function application e.g. "func(1)(2)" will result in a syntax error. This is problematic for higher order techniques that benefit dynamic languages.
<br />
<br />Why would anyone want features such as those I have outlined above? People designing "in language" domain specific languages benefit greatly from such flexibility. Particularly in a language which already limits facilities for creating such mythical beasts.
<br />
<br />That being said - I will gladly let go of my personal hack for passing in name/value pairs to a function which I have lovingly/jokingly referred to as "appositive expressions" e.g. "func('key', 'value', 'key2', 'value2')" can now become "func(['key' => 'value', 'key2' => 'value2'])". It is not surprising at all that php finally gets this feature as ruby finally bit the bullet and now accepts the json esque ":" in place of the "hash rocket =>". '
<br />
<br />I should mention that the addition of "traits" will go miles in cleaning up some otherwise convoluted approaches to attaining mixins in php! I am very excited to see how this is utilized by the different frameworks.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-14592100232455893862011-06-07T23:08:00.000-07:002011-06-09T23:30:01.466-07:00smalltalk, apl, soy lisp, and higher order operandsAPL, Smalltalk, Lisp. The holy trinity of programming languages. In this post I aim to better explain the Soy Lisp syntax which allows for all three to coexist. <br /><br />Consider first the ability to drop parens when an expression is terminated via ".":<br /><pre><br />(func arg1 arg2)<br />//becomes<br />func arg1 arg2.<br /></pre><br />This creates an immediate problem w/ disambiguation when dealing with arguments, well thankfully we do not lose the ability to return to s-expressions when required e.g.:<br /><pre><br />func arg1 (func2 arg1 arg2).<br /></pre><br />What else can we glean from smalltalk? Perhaps the ability to cascade via ;, e.g.:<br /><pre><br />(((func arg1) arg2) arg3)<br />//becomes<br />func arg1; arg2; arg3.<br /></pre><br />In prior posts I have shown my preference for "square lambdas" e.g: [x | x y]. So I will not continue to rant about the benefits here. The last thing I will borrow from smalltalk is keyword constants (whilst keeping lisp keywords) e.g.:<br /><pre><br />(func :keyword1 var1 :keyword2 (lambda (x) (x :says 'peter))<br />//becomes<br />func keyword1: var1 keyword2: [x | x says: 'peter].<br /></pre><br />The above assumes func is a higher order function which knows how to dispatch keyword selector arguments, which is essentially how I would implement a smalltalk esque language on top of soy. <br /><br />I think it is clear that functional programming in lisp can benefit from the smalltalk syntax whilst not losing the power of macro expansion and s-expressions. <br /><br />This brings us to APL the other language I have toyed w/ writing interpreters for. The real crux of apl is working with higher order operators. e.g. reduce which takes on the left hand an operator which it then uses to reduce a set of numbers on the right. In traditional prefix notation this would end up like:<br /><pre><br />((/ +) 1 2 3 4)<br />//not bad but noisier than<br />+ / 1 2 3 4.<br />//which becomes<br />(+ / 1 2 3 4)<br /></pre><br />Which works just fine because + is a higher order operator which knows if its first argument is another operator it should return a function which does a + reduction.<br /><pre><br />(- (+ 5 5) 5)<br />//becomes<br />5 + 5 - 5.<br /></pre><br />In soy the latter works because numbers are "higher order operands" which can consume operators and return functions which consume more operators/operands as expected. Stay tuned for a web repl for those interested in playing with this. Bare in mind that both forms in all of these examples work with in the same environment as this is all syntactic sugar! Lisp wins again.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-90940288673620228102011-05-27T15:21:00.000-07:002011-05-27T16:22:10.530-07:00Keyword Functions in PHPSo, as spurned by a <a href="http://news.ycombinator.com/item?id=2592154">comment on hacker news</a> relating to the lack of ability for keyword arguments in php I figured I would expound more upon the <a href="http://www.github.com/shaunxcode/phutility">Phutility library</a>, in particular the Func module.<br /><br />Check it out: <br /><pre><br />#say you have a function (perhaps defined by you, perhaps not)<br /><br />namespace SomeNamespace;<br />use Phutility\Func; <br /><br />function testFunc($name, $eyes, $hair = 'blonde', $age = 0, $weight = 200, $goatFace = 'definitely') {<br /> return array(<br /> 'name' => $name, <br /> 'eyes' => $eyes, <br /> 'hair' => $hair, <br /> 'age' => $age, <br /> 'weight' => $weight,<br /> 'goatFace' => $goatFace);<br />}<br /><br />#if you want a keyword wrapper for it:<br /><br />$testFuncWrapper = Func::keyword('\SomeNamespace\testFunc'); <br /><br />$result = $testFuncWrapper(<br /> age, 30,<br /> name, 'peter'<br />);<br /><br />/*would yield a $result of<br />array(<br /> 'name' => 'peter',<br /> 'eyes' => 'blue',<br /> 'hair' => 'blonde',<br /> 'age' => 30,<br /> 'weight' => 1200,<br /> 'goatFace' => 'definitely'<br />)<br />*/<br /><br /># If you want to not have a variable func you would need to do something like:<br />function TestFuncWrapper() {<br /> static $wrapper; <br /> if(!$wrapper) {<br /> $wrapper = Func::keyword('\SomeNamespace\TestFunc');<br /> }<br /><br /> return call_user_func_array($wrapper, func_get_args());<br />}<br /><br />#a little gnarlier but this is the price we pay to call it like:<br /><br />$result = TestFuncWrapper(<br /> goatFace, 'NoWay'<br />);<br /><br />#oops this would yield an exception because name and age are required!<br /></pre><br /><br />If this is at all interesting to you and you'd like to use it or contribute, the source and tests are all <a href="http://www.github.com/shaunxcode/phutility">up on github</a>.Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-8582403812843601208.post-43429851680043185122011-05-25T15:25:00.000-07:002011-05-25T15:28:15.666-07:00On unit testingWhen ever I am confronted with a problem with a code base, my first reaction is: "<b>wtfutfts</b>" meaning "where's the fucking unit tests for that shit". No unit tests? Now you know your first move in providing resolution.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-73025528147423798782011-05-25T14:39:00.000-07:002011-05-25T19:13:33.979-07:00Alternative s-expression syntax for Soy LispSoy is the name of my lisp dialect (previously called lisphp, and before that phlisp). This is quite a radical departure from standard lisp s-expression but not so far as to, say have infix notation or "sweet expressions". Rather this is a simple inverse of s-expression e.g.:<br /><pre><br />(if (predicate arg1 arg2)<br /> (thenFunc arg2)<br /> (elseFunc arg3))<br /></pre><br />Becomes:<br /><pre><br />if(predicate(arg1 arg2)<br /> thenFunc(arg2)<br /> elseFunc(arg3))<br /></pre><br />Your first question should be, "but wait! what about homoiconicity and the ability to have quasiquote macro definitions!?". I'm glad you asked, I have two proposals. 1) allow for a s-expression macro syntax, 2) allow for quasiquote to start with an atom:<br /><pre><br />defmacro(eat-apple person number-of-apples<br /> `person-eat(,person 'apple ,number-of-apples))<br /></pre><br />You may have noticed the other drawback with this syntax is that it makes having macros which handle sublists a tad more difficult. As any atom followed by (, applies the atom to the arguments inside of the parens. This can be resolved by using rest args for forms which anticipate the last expression to be treated as a lambda. Also Soy allows for [ | ] lambda forms and { } vector/dicts. So it is also possible that would resolve any ambiguities.<br /><br />One of my major ambitions with this is to have a very simple syntax for exploring alternative object models, similar to my previous post about an alternative object model for javascript. <br /><pre><br />;simple keyword selector<br />some-object(set-first-name: 'peter and-last-name: 'jenkum)<br />;chaining <br />some-object(some-keyword: arg1 selector: arg2) <br /> (chained-selector: arg3 takes-closure: [ a | a(message: arg4)])<br /> (another-chained-selector: [_(anon-var)])<br /></pre><br />I really dig how close this comes to snythesizing what I love about smalltalk w/ lisp w/o really forsaking either.<br /><br />One of the real "wins" with this is when you have function returning functions .. n you don't end up w/ a massive amount of nesting. <br /><pre><br />(def adder [x | [y | [z | + x y z]]])<br />(((adder 6) 7) 8)<br /><br />def(adder [x | [y | [z | +(x y z)]]])<br />adder(6)(7)(8)<br /></pre><br />I think the latter reads in a much more obvious "left to right" fashion.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-13654841177289918262011-05-23T15:39:00.000-07:002011-05-23T15:44:05.988-07:00simple keyword message selectors for javascriptAs I have been messing with alternative object systems for php one of the ones I came up with was ab(using) the __invoke magic method to take a keyword argument list and find a given selector based upon it. I realized this could be done in an even more trivial manner in javascript using the object syntax and returning a new object w/ the dispatch mechanism rather than "return this". <br /><br /><pre><br />var Klasses = {<br /> 'Person': {<br /> 'selectorPart1:selectorPart2': function(a, b) {<br /> console.log('called with ' + a + ' and ' + b);<br /> return obj(this);<br /> },<br /> 'subSelector': function(a) {<br /> console.log('subselector with:' + a + ' for ' + this.name);<br /> return obj(this);<br /> },<br /> 'get': function(field) {<br /> console.log(this.name + ' over-ride of get with ' + field); <br /> }<br /> },<br /> '*': {<br /> 'get': function(field) {<br /> if(this[field]) {<br /> return this[field]<br /> } else {<br /> throw new Exception('No field ' + field);<br /> }<br /> }<br /> }<br />};<br /><br />var obj = function(self) {<br /> return function(selector) {<br /> var sel = [];<br /> var args = [];<br /> $.each(selector, function(k, v) {<br /> sel.push(k);<br /> args.push(v);<br /> });<br /> <br /> var selectorName = sel.join(':');<br /> var message = false;<br /> if(Klasses[self.klass][selectorName]) {<br /> message = Klasses[self.klass][selectorName];<br /> }<br /> else if(Klasses['*'][selectorName]) {<br /> message = Klasses['*'][selectorName];<br /> } <br /> <br /> if(message) {<br /> return message.apply(self, args);<br /> }<br /> };<br />}<br /> <br />var guy = obj({klass: 'Person', name: 'peter'});<br />guy({selectorPart1: 'apple', selectorPart2: 'candy'})<br /> ({subSelector: 'rotten'})<br /><br />var otherGuy = obj({klass: 'Person', name: 'samuel'})({subSelector: 'cotton'})<br />console.log(otherGuy({get: 'name'}))<br /></pre><br /><br />To see it live: <a href="http://jsfiddle.net/LG7En/3/">jsfiddle running the above</a>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-32550367004802224802011-05-20T09:26:00.000-07:002011-05-20T10:14:40.448-07:00On PhutilityPhutility is a collection of tools which I utilize in many of my other projects. For example the Node class is utilized by both covenants (sql builder) and also phmop (meta object protocol). Today though I am going to be discussing some of the work that I have been doing in Func to lay the framework for "higher order object oriented programming" with phmop. To give you an idea of what I am driving at, consider:<br /><pre><br />defineClass('Person', <br /> slot('someMethod', function($self) {<br /> $args = func_get_args();<br /> $self = array_shift($args);<br /> $options = appos($args);<br /> if($self->{$options[ifField]} == $options[isValue]) {<br /> $self->call($options[callMethod], $options[withValues]);<br /> }<br /> })->accessor('someMethod'));<br /><br />$person = new Person;<br />$person->someMethod(<br /> ifField, 'age',<br /> isValue, 30, <br /> callMethod, 'someMethod2', <br /> withValues, array(300));<br /></pre><br />There are a few things we can cleanup. Firstly lets create a function "method" which wraps up simple functions which utilize an accessor which is the same as their internal name. Then lets utilize a higher order function "Func\options" which returns a function which has the last param defined as an options hash to clean things up: <br /><pre><br />defineClass('Person', <br /> method('someMethod', Func\options(function($self, $options) {<br /> if($self->{$options[ifField]} == $options[isValue]) {<br /> $self->call($options[callMethod], $options[withValues]);}})));<br /></pre><br />But we can go further by creation an optionsMethod which wraps up the need for the explicit Func\options call. Also we can have it pass a param which gives us back a std class for options rather than an array. <br /><pre><br />defineClass('Person', <br /> optionsMethod('someMethod', function($self, $options) {<br /> if($self->{$options->ifField} == $options->isValue) {<br /> $self->call($options->callMethod, $options->withValues);}}));<br /></pre><br />For the sake of completeness let me show off another way this could be defined more explicitly taking advantage of Func\keyword which evaluates a closure and then returns a function which responds to an appositive style call:<br /><pre><br />$keywordFunc = Func\keyword(function($age, $name, $color) {<br /> return array('age' => $age, 'name' => $name, 'color' => $color);<br />});<br /><br />print_r($keywordFunc(<br /> age, 30, <br /> color, 'blue', <br /> name, 'peter'));<br /><br />array(<br /> 'age' => 30,<br /> 'name' => 'peter',<br /> 'color' => 'blue');<br /></pre><br />As you can see this allows us to pass the params in any order we please (which to be fair our Func\options implementation does as well but packaged into an $options var). So now we will skip to step 3 of what a keywordMethod would look like in use:<br /><pre><br />defineClass('Person', <br /> keywordMethod('someMethod', <br /> function($self, $ifField, $isValue, $callMethod, $withValues) {<br /> if($self->$ifField == $isValue) {<br /> $self->call($callMethod, $withValues);}}));<br /><br />$person = new Person;<br />$person->someMethod(<br /> ifField, 'age',<br /> isValue, 30, <br /> callMethod, 'someMethod2', <br /> withValues, array(300));<br /></pre><br />I think this makes clear why both a meta object protocol and higher order functions are so awesome when they can play together. Rather than having only inheritence, interfaces, or even mixins, as the tool for making things more elegant we can easily add new features to our language with simple function composition.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-30195856419196651782011-05-18T08:09:00.000-07:002011-05-20T08:22:09.350-07:00Considering a MetaObject Protocol for PHPThe purpose of a "MetaObject Protocol" is to allow for defining the very nature of how an object system functions. This means the ability to add traits/mixins, aspects, multiple inheritance, "Generics" etc. w/o requiring compilation or low level tinkering. One major advantage is that this means not having to wait for a revision of your language to express certain design patterns.<br /><br />As usual php is so close, yet so far away. However ab/utiliz/ing the magic methods you can actually get pretty close to something resembling the <a href="http://en.wikipedia.org/wiki/Common_Lisp_Object_System">CLOS</a> system. To accomplish this it requires a few building blocks which I have abstracted into my <a href="https://github.com/shaunxcode/phutility">Phutility</a> library. Primarily the MetaArg and Node which provide the building blocks for composing declarative domain specific languages.<br /><br />The majority of the magic occurs via macro expansion in CLOS. We are going to make up for that via declaring an <a href="http://en.wikipedia.org/wiki/Abstract_syntax_tree">abstract syntax tree (AST)</a> which we will expand and weave into our method dispatch mechanism. <br /><br />Enough talk, let's code. First our goal is be able to declare classes and methods. <br /><br /><pre><br />defineClass('Shape',<br /> slot('width'),<br /> slot('height'),<br /> method('getHeight', function($self) {<br /> return $self->height;<br /> }), <br /> method('getArea', function($self) {<br /> return $self->width * $self->height;<br /> })<br />);<br /><br />$shape = new Shape(<br /> width, 20, <br /> height, 50<br />);<br /><br />echo $shape->getHeight();<br />echo $shape->getArea();<br /><br />defineGeneric('beforeGetArea', <br /> before(getArea),<br /> arity(Shape), <br /> function($self) {<br /> echo "Called before get area!\n";<br /> }<br />);<br /><br />echo $shape->getArea(); //outputs "Called before get area!" and then area<br /></pre><br /><br />Looks like a class and acts like a class. But most definitely not a raw class. How does this work? Principally we keep the meta information about each class in a Registry. Each class defined results in a simple declaration of "class X extends \phmop\StandardClass{}" which allows the usual "new Class" syntax. It writes this to a cache directory and does a require_once on it so as to avoid eval. <br /><br />The base Obj class looks something like:<br /><pre><br />class StandardClass {<br /> public $slots = array();<br /><br /> public function __call($slot, $args) {<br /> return Registry::dispatchMethod($this, $slot, $args);<br /> }<br /><br /> public function &__get($slot) {<br /> return Registry::getSlot($this, $slot);<br /> }<br /><br /> public function __set($slot, $value) {<br /> Registry::setSlot($this, $slot, $value);<br /> }<br /><br /> public function __construct() {<br /> foreach(Appos(func_get_args()) as $key => $val) { <br /> $this->$key = $val;<br /> }<br /> }<br />}<br /></pre><br /><br />So pretty clearly the real magic is taking place in the way that defineClass, defineGeneric, and friends interoperate with the Registry.<br /><br />I am going to end things here for now as I need to make a few posts about some of the inner workings of phutility to really get into the thick of things.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-29854470450879821022011-04-23T21:34:00.000-07:002011-04-23T22:45:27.676-07:00a look at the chet "appositive expression" orm dslChet is a lightweight, minimal, php framework intended to make writing php not feel like a giant pile of array('a' => array('b' => array('c', 'd')). This is accomplished mainly by declaring appropriate constants and using what I am calling "appositive expressions". e.g. Person(name, 'shaun', age, 28); <br /><br />While this started out as a joke it has actually begun to have merit and today I am going to demonstrate the latest portion of the project which is the sql dsl which in a nut shell looks like:<br /><pre><br />TodoItem::with(<br /> creationDate, between($start, $end), <br /> either(<br /> owner, eq(25),<br /> status, in('active', 'pending')));<br /></pre><br />SCS: 26<br /><small>When I am messing with snytax ideas I have come up with a system for scoring their visual complexity which comes down to counting any piece of non-word character i.e. parens, colons, commas, quotes etc. For the above example the SCS (syntactic-complexity-score) is 26.</small><br /><br />The above in a raw php + sql approach would be: <br /><pre><br />safe_mysql_query("select * <br /> from TodoItem<br /> where creationDate between(?, ?)<br /> and (owner = ?<br /> or status in(?, ?))", array($start, $end, 25, 'active', 'pending'));<br /></pre><br />SCS: 33<br /><br />The equivalent in something like cake php would be (from with in a controller abusing the concept of $this to access a model... ugh) <br /><pre><br />$this->TodoItem->find('all', array('conditions' => array(<br /> 'creationDate between ? and ?' => array($start, $end), <br /> 'OR' => array(<br /> 'owner' => 25, <br /> 'status in(?, ?)' => array('active', 'pending')))));<br /></pre><br />SCS: 58<br /><br />I am pretty sure that the above describes what I am trying to get at: if your "abstraction" is messier/harder to read than that which you are meant to be abstracting yourself "from" it is time to go back to the drawing board. While Chet started out as a joke <small>(a reference to Sinatra for ruby but more fitting as Chet Baker was a horrible drug addict yet an incredible musician - thus php is a terrible language yet still capable of doing marvelous things in a non-classy manner even)</small>, I am slowly actually wanting to use it for real projects. <br /><br />One of the design choices was to either bit the bullet and have methods called _and, _or etc. or find alternatives for them. For "and" I have chosen "with". For "or" I have chosen "either". For all of the other sql operators I have chosen the perl approach: "=" is "eq", ">" is "gt", ">=" is "gte" etc. between and in are now both just methods. The beauty is that because all of this is just sugar you can just as easily express the above as:<br /><pre><br />TodoItem::with(array(<br /> 'creationDate' => array('between' => array($start, $end)), <br /> 'or' => array(<br /> 'owner' => array('=', 25),<br /> 'status' => array('in', array('active', 'pending')))));<br /></pre><br />SCS: 56<br /><br />Still not as gnarly as the cake php but clearly noisier than the "real chet" approach. My point is that you can drop the sugar in favor of the array noise if one is so inclined. <br /><br />I am using this same approach for the html/css which yields some very similar gains in readability and "self-similarity" across the entire application. <br /><br />All of the above being said this is purely an exercise in "how far" I can push php to gain something that resembles a non-array/class-abusing DSL with a small amount of code generation and utilization of everything php 5.3 has to offer.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-86718754222919962082010-12-03T08:39:00.000-08:002010-12-12T16:09:19.576-08:00Revision to php TCO trampoline<a href="https://github.com/shaunxcode/php-trampoline">github.com/shaunxcode/php-trampoline</a><br /><br />Sometimes I wake up at 6am with the frightful knowledge that there is an edge case to something I wrote somewhere which is incorrect. The only thing worse than knowing someone on the internet is wrong (<a href="http://xkcd.com/386/">http://xkcd.com/386/</a>) is knowing you were wrong on the internet. <br /><br />I realized in a dream that my trampoline would not work with higher order functions as it currently stands because the while loop is based on is_callable, obviously if your function is meant to return a function you would not want it to be executed immediately (particularly because it probably expects arguments etc.). All of a sudden the whole throwing an exception approach made sense - however thanks to php5.3s __invoke method I can handle this cleanly by wrapping each tail call in an instance of TrampolineBounce and then check "$return instanceof TrampolineBounce". <br /><br />I figured if I am going to need a Trampoline class I may as well leverage __callStatic to allow for easier invocation of trampolined functions. With out further ado: <br /><pre class="prettyprint"><br />namespace Trampoline;<br /><br />class Bounce {<br /> private $func;<br /> public function __construct($func) {<br /> $this->func = $func;<br /> }<br /><br /> function __invoke() {<br /> return call_user_func_array($this->func, array());<br /> }<br />}<br /><br />class Trampoline {<br /> public static function Bounce($func) {<br /> return new Bounce($func);<br /> }<br /><br /> public static function __callStatic($func, $args) {<br /> $return = call_user_func_array($func, $args);<br /> while($return instanceof Bounce) {<br /> $return = $return();<br /> }<br /><br /> return $return;<br /> }<br />}<br /><br />function even($n) {<br /> return $n == 0 ? true : Trampoline::Bounce(function() use($n) { return odd($n - 1);});<br />}<br /><br />function odd($n) {<br /> return $n == 0 ? false : Trampoline::Bounce(function() use($n) { return even($n - 1);});<br />}<br /><br /><br />echo Trampoline::even(1500) ? 'Yep' : 'Nope';<br /></pre>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-8582403812843601208.post-54264874678929788302010-12-01T15:52:00.000-08:002010-12-03T08:49:13.285-08:00tail call optimization in php 5.3<span style="font-weight:bold;">*note this has been revised: (<a href="http://commonphp.blogspot.com/2010/12/revision-to-php-tco-trampoline.html">http://commonphp.blogspot.com/2010/12/revision-to-php-tco-trampoline.html</a>) *<br /></span><br />So I have been holding off on releasing my php lisp for the past couple of months for a few reasons. The first was I wanted to get quasiquotes/macros working correctly and the other was I wanted to get TCO working correctly. I checked quasiquotation and macro expansion off a few weeks ago and have been wrestling with TCO since. There are many work arounds such as only using higher order functions like map etc. which "underneath" use php foreach/while etc. but this always felt like a hack. Today I will show how I am accomplishing TCO. It is actually "pretty" enough that I would even use this technique in "normal" php if the need arose i.e. when implementing an algorithm where mutual recursion is the cleanest way of dealing with a given problem. <br /><br />I had heard "tail" of a magical device called a "trampoline" but had yet to see one implemented in php. First lets set the scene for why such a thing is necessary.<br /><br />Regular mutually recursive functions:<br /><pre class="prettyprint"><br />function even($n) { <br /> return $n == 0 ? true : odd($n - 1);<br />}<br /><br />function odd($n) { <br /> return $n == 0 ? false: even($n - 1);<br />}<br /><br />echo even(15000000) ? 'Yep' : 'Nope'; <br /></pre><br />Result? <br />Fatal error: Allowed memory size of 335544320 bytes exhausted (tried to allocate 261900 bytes)<br /><br />As you can imagine this would blow the stack pretty quickly. The solution is to return closures which close over the next function to be executed by the trampoline:<br /><pre class="prettyprint"><br />function trampoline($func, $args) { <br /> $return = call_user_func_array($func, $args);<br /> while(is_callable($return)) {<br /> $return = $return();<br /> }<br /> return $return;<br />}<br /></pre><br />Ready for trampoline version of functions, notice the closures being returned:<br /><pre class="prettyprint"><br />function even($n) { <br /> return $n == 0 ? true : function() use($n) { return odd($n - 1); };<br />}<br /><br />function odd($n) { <br /> return $n == 0 ? false : function() use($n) { return even($n - 1); };<br />}<br /><br />echo trampoline('even', array(15000000)) ? 'Yep' : 'Nope';<br /></pre><br />No more blowing the stack!<br /><br />For another example here's a tail call version of factorial:<br /><pre class="prettyprint"><br />function fact($n) {<br /> $product = function($min, $max) use($n, &$product) { <br /> return $min == $n ? <br /> $max : <br /> function() use(&$product, $min, $max) {<br /> return $product(bcadd($min, 1), bcmul($min, $max));<br /> };<br /> };<br /> return $product(1, $n);<br />}<br /><br />echo trampoline('fact', array(5050));<br /></pre><br />Result?<br />48424893876442346868149955534231630311152316586879831768663469768542209496025625521335165836597244986775871359731653944863304801472363186993340977071916545094098397335673370033050095301846673812319519184368943848940208083286841710046697935471447379350531958006231636490517114754236601801475241025829782559539886360417931118962453657701734674670226076209971728976092053885317753609072349073735823517244188123450674446015646408575152721563438720528695200000950089076496921144117143216432888312645825602479454709341393797835166357888263581672355452853869124012486552503932416596193377435247315025743200744002301394585228500268455104719958806656594340592125681513366273489623936710098205543483870278942617415439360839831622538877203695372405175195820803498878614463408507559071691975942963055323977023975579962356660525443011519924497192606177308403911494610046020125224506026847906849303464211582255596044461695809931170735992902861015153948973924760246045409447614027579732580231716105662183345892311470363765765619375852044952237375592745997667408717094345683918382540158972633294341661753292428842087402347931146455707829443369742629955406096433554614672210416043568566713315721328986242009438119615170550083892805000693555351590233960368408610557369747063249171375287938142229602269575258888078115537273321885243640816116313033760859934727694805934472650247398590268106132632051710011975357435411073973400518009267243861234604382480972044372345571394804592046625365990943593155118312928026092849203020843936635478814979107242813498013904401479307173527813374488927846868388843629960975688405420793866362748625568963809336537735196635482323869611480442317616213118017936592673957745954854119905353862738630397953491904711075413351939493239103430359228666385849114266162025489907918597974910444817388693635468572983832473160429177887474388606642754513228627277514269543214565586593047881932646980529181738704933529003783674019143726269073240250043051103116046759758599556492457493255007087656022414606222859948126380313826837273496022239059049197478197470692778763575742323649675518937743998020384086092356484327490394949151360442183700474945898304321062066355143800014715430781407860862440970162255814321544952627961522489938257459652704978558232366966762259286202122152536234765500508950380085381022529769410646695981455932497274422145956223070614311782386939393660505071468075609946312266961804079078124972138751570252042991908322662337184841517195535818561858155408260788044667462475133702274158651640293943156955741104099223849587202623924618562507307459878359309762592535803298228381823574279830281362305301068394370171580376856819398786773168976316243886413852653396055497826310062540076987247412010421678922894559582379982459315131410353844686814801084428315428961052501286596189198414322404856396902069457672137306587727429077418184644185029820725981473585275798688633870939149445242039540994144936753560145261967645980942905198036844963065770953567278843744815101688364553167567561127581285151504170857163788476814410430818485342212602471021116122232536945437600193563274350613219002205135771961855617387540043201574995936521078443417355808962389471634447182986942493573328833479027327904592963332830948909056606878114712587614797355021919494836468834407159424831076501904939674180550971070685974826026825572462514492992573071756584471446779582538562871889253777491503761958039422843336111308820256625552392100765429247159402181090198939792727134760027285582388085697688779314720476653237742989447851866203189262136310101262960823946227816914290542573075004229606139191853412465867836753036378462157616915664130017838805074756547841197620513628237540972492294319305705734478032962949128924196666202655682340324787692515590130551301935088659828410687356942341790891345277677382788831208787364801395111486357308798265828146035781558094135067110230536901270297547242166950881473214274739101845553727897045531119954725288893493225046788944932736466460550726919323972814208640861318915500012832311138673106892856437774916302054198800992266101283582978474791023088963610289841529824634218025633260218581419143652195694797922926870213605428040752771533690783079334348985159752481190866305108822791928707079718613729022578623134006395058165941963834815415900225540158365512962747234118295298072392721688699472738462833608355509413369430747483292086244416854740060940250464784905488189338770945263660330117646978971876652613557429461658723058825915031208985735140615497693407650641375234834094995218262573623896322220855235269130698919365626682758374382064483940359436309642974808514506879129036989811006578121058886468214783989745311306991381502598738427646690529026148074559926029141700545754931041231062108024348298640252182862477489626497023992723452959298403206063199087668949604632790351230972033067574210447769545912131839504735995523543564698434762288745349217285435045940022877153117114056361355516568793184887831573689961226955484323093901470710251285589637162339096014594590326637778408537708815313301255641448290264509028416236493070536691277554818638552346553208672413541183818357851114016689498361821413896379525442756652690413158967535936279206144987763854912793024225936394940295690129089121120860291418297457810220526616453514910819323035995322046111221378283408895173068084562318564077990588079762019973028279392634257796579680892376694383912153702257752771918240121360797334802574067759177201695444664008395141273331798769390706217716413944484444589448011804566872918201233352031246374079426621952856774873141762004642963805009605909976650733467147648933063451549234764105261843727285344810237038664893806272788248667617929188856335934471876757762121375528223069436338430636550586312429972101590001624825194534053466559441823558494047711923047787608650174147236583346033499704371720807668032648824335082469921765890357796559326553963051584575314243179379693972869819018936515241128344111174643025352717517264399068943122072909168503889489366305203869840440783566583673872512529607991028619785374051069353232798743538451929474739656942294561359950279354484262386048300967384230958861468226703153885216880543889184923233329346644468452757677009730774828691754812914301660783522852525684240191438648246859144805142924667100195015093959485990061146668886559862309819174810279322086728633972728154606053167707643070032164693171224226003284900895506902979108769760815523818908193387378633599962338329201288673602475038425763910335696933668401126584246627457560953522086694366649469599705521441640238122010959682479428036962415722220141394447107341640961359500927881874233095198411689068446492958685137714321262410692218565491447540332778657853504520774359829079460075117251206648241533763048331739049003142679483361884913145518268997761626564746070642120683866637049087132932456214339322178452985208536699837965699909310064620090804584888468392476355755579270080782553655541381943286896134122662506174668761696780529727626382334663869879097172088652428923258543798284963386781746966688860322027736021528016691959550581844902683467447609407368122794497183432069868909471472548153656903880703201644425109624545859826873667750937614607390493457875484953740767611339667576173714034455565432308153130757930008532562324042807264432614157844822751708067421611502291046796482944889562484299548170367202226348895651943485134487355907416500408163581341989736946778288899701341554759826542507320619287649549795982662920054235475749643320872348617927891303979446402061121132359483927554056402873178493913139966737132973717633632447161627037227392256332565547672908601616412069162541656417328547927835344764379080034006834438837309816235465641185216815046674945763955984713681604516254849662824411306353793798089671114185280187265849418052077836852589180241592723184733541106406465656205948358954948142025233991272905001141355021729920577475479281289486806270011744359500435621608814756150544407957227147107089215203809044297767994104686800618204519792063487603872622575538947815450619856182485749773834231277713725309254568969583149073554526597574846552798676406462337998787339779028992439990039978154586413759133622409437489561164656777421746380264056302768613928564315667912312952457404411495519474886250396579083072312029449210229223734219955586387114225996549617982828293430386694221527613218115999731321291884322073808127116054283436992190175171603278221120478118125795564192351195970923490210310910133096302509169175624548014311817872937471722034686941769631047594296358186560148127293467958839434265463971529119297056567953684958757245231554391244311041212876924918500484087772698403206170331941238143489798776660534676579046337828982028986348099030601184836047434217855703008400176583770452171688793096489449429186708279868381214066539707277515059120768682704305155217982248302399219666594196559372775947145581773723282195411033360764337292946984272715506254608876131243628417716054202032599008485959729298011133493587411121592451061195248871236550219323568794733198388938084043968597320732717910476328327887684545483577062955807065986440317073048021931109482880059179684697878805723379654977018391836443141027833877379573134035147846614695719532905855359486154082235216657769559165736626734582602844219961653790266071295624034376769110677927593476162673165371870321528606055962225071216770606048742973083597090117219781320947530791012971233612874212167300949205392589536160373120473483108568345225335577313624485693021092445510802644688591070751584819925697407862940653022261735482476498456623897965943740384974312102003102503405987723190936549669116934059002978404863724434029651459171929972224636473192846126682034744452198659199773243765555394223202793062777093231567712374298614503785227247044790390230170449231471025389935822203023342537851424261619334224815629159006888149337227458290876318073016643545061947091539741062518116457909282507014366747914604640619597780018271697736234489832448906487488167612194968404133122879631080718761096322629749242740599130685431498769606882708624503872378150881580793787863737584355460103940532518865937858605854476146840248883570271489049276259843458637015215238959285845598888570553863679704803156416650276687716857598414761640580833928733883633835389587148355501394145096219531824605734660342901357825854824533481203519236387571460898657123958733206267932786756738047919023782881760608578646872699523991805943926237776113774398337350917182253520526962590153838195949497674171793321672290838421458771652486169401420831215559578522629914795737160039551091814038045014141164820901176264063896563274263280283566146499227688707995123583313957488864707245532192866843267707404232105194327568792851170384048440120280104065824324895974735080562974987135989427850162706790819742752770673925751139449717572950066181333226422371275841399550790779378722199692256665985844973214290417466996736463656592057379723789125163155964154625281239640046452862780603936207280664294883639688906345127219732436103075763451035422059632678374543779848932298460317103368289281025974169671272176383711807597354081467243350813365849007681882657888031711208469929623879339045017847389034606030926200455252399928369014924250363511229402299134698234156077158889592184980239170939501976466282865503049323617696621577333707865541600453933439576081214463590926434086384747290541542956399056962056229808127723780675081863094174281718420041021021738137563902621285687570410794757069128146157463269445164824140810572619018262027820086204868288731570345974912460949952968977302815206146117344553964793868988818958005020677285939064713111149888845649122939779041112120103892513478651508392217974531543376928740368199784888020421470272916459151814416040117267729799572111113430173741099889364201242246402948747813374749761369635840602934801976235850307624671917843079911771168517196726786713522653929181501433771224598604194250156742985226389799732276295274717715643984526333979308207645737104136935577625600314512836136271115660651134990573752618766965589069059676285129729732662084619078729162659020226938244891662847624627441891710694949940459227354409221979111240807882850754281611301674487520083457930422881455622420345738892296143055690156793547311809505543345371574649444927866169060334457904017188783393683115952245289189776402314456755190701764830857379904237952920537930837101480626961392776308489600995416134740406176538114983299708968857197075820121096069101496947802184680898394989647393102640058875148772532514830965041682985531620318115129285743608361213991132382738975462458617395619613187809486070987315723381903277922746195238195752905633836870278707046586517135634008007513341884363874996422486463455702922902696818946264796634960559597402803593333400937678136319820912555570462398816832063531837359091080659789751716369244408831250173156802380436362475465330236414904123853473096649894512457823362094181780912054103436914017980019455398250827412935061689262299438765336556444028008141319555636511769936177287956920799719676548107901075208403027364066932296087105441105308233895669378571717426107611042700373090786826809357637920085583810333407384012506569539408308629504276425574350546071484766182755577320350092810707004993335303482992232100488260296389559828488542505497559354326474562253495080783576216166234312096015413769488264005498554434439616686883956930058964562125319715331423528773748137428825047204186186648001874137240953303168262859474441442364125373820078661928648678801874400337808524510807407292324770665786283834004819341447708739358303385371869359181326822754638005614357757088482815667552921043175589353835686768950622306463118223568914503995372430337670295660500254935570486455145607978192630525671852896912768541854734619733961476555472299897874821466782568991296975858100442257963419860785714800116609046347135848549904523395244469912871910348036936480662845699967192884116216409488999878911387634858801666284371199007716171100287801287860307049091775742741060304901180847344576070072887286804106341674016729286661940027108142630650637758836422914341360842554401092646434224130444935704965676057926041362775924786583432602640154170852804440351577248486945824032635382183769612125595063262772078130555155831993351782956959696608968709544714953809612896233163746852346991803469348501042522744077168092081901244451643409807267936512732460961147488822964796202735500820301014818688579847672354195891890954086978215346979986227790640259391170662626734729071467762656777507642626883458780766449547354683123105196509097488881829974869470326766694168376589702024017800739547467144404227676674351665468804862538125870908797276169065403720549323187458536526037850524207766074436322836938304257311371902601828532364832899357200542907831617866643720527906804105301881269555094766929366731873712163397632411978533831454925115992025126122572575243124108927948251985272760453761040442900279614459490962271192658777575358243219372257723044331255857441455137934349703756882320673204480286229329737047565154624737913976783040219275254317566702293970510158407465954045780946698855495818114759070368009209396367465717100483910841200597758805510253615045052314093062210028464178602712271706991877647528837796369531833743493455280297995225803903076950864895339985477756937031227397457811500378614493909582380416368640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000<br /><br /><br />So in other words PHLISP should be officially released by christmas eve.Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-8582403812843601208.post-12006567334777780382010-11-18T19:01:00.000-08:002010-11-18T19:42:14.513-08:00ruby gem like system for phpWith the advent of phar I feel the time is nigh for a ruby gem/perl cpan-esque system for distributing packages/sharing code. I know pear exists but honestly how does one distribute a package via pear? proposals? cvs? I am imagining something where you point it to a github project and it generates a phar, increments the version number, when ever you tell it a release is viable.<br /><br />It would also be slick to be able to do something like:<br /><pre class="prettyprint"><br /> rock::require('somePackage');<br /> namespace somePackage;<br /><br /> funcDefinedInSomePackage(); <br /> $x = new ClassFromSomePackage;<br /></pre><br /><br />Where rock::require at dev time would check for the latest version of "somePackage" on the server, if it is newer than what is there (or it is not there at all) it will download the phar, put it in the library and then load the phar.Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-8582403812843601208.post-54725611135364902112010-09-30T11:48:00.000-07:002010-10-01T17:54:54.038-07:00mock table dataI got tired for looking at multidimensional arrays when writing lots of mock table data. <br /><br /><pre class="prettyprint"><br />$orders = array(<br /> array('email' => 'shaunxcode@gmail.com', 'firstname' => 'shaun', 'lastname' => 'gilchrist', 'street' => '555 north 800 east', 'city' => 'Orem', 'state' => 'UT', 'zip' => '84097', 'country' => 'USA', 'phone' => '801-222-5555'),<br /> array('email' => 'peter@gmail.com', 'firstname' => 'peter', 'lastname' => 'jensen', 'street' => '5555 windsor street', 'city' => 'Salt Lake City', 'state' => 'UT', 'zip' => '84105', 'country' => 'USA', 'phone' => '801-555-0445'));<br /><br />$orders = Voltron_Test::MockTable("<br /> email | firstname | lastname | street | city | state | zip | country | phone<br /> shaunxcode@gmail.com | shaun | gilchrist | 555 north 800 east | Orem | UT | 84097 | USA | 801-222-5555<br /> peter@gmail.com | peter | jensen | 5555 windsor street | Salt Lake City | UT | 84105 | USA | 801-555-0445<br />");<br /><br />#relevant Volron_Test::MockTable method<br /><br />public static function MockTable($data) <br />{<br /> $rows = array();<br /> foreach(explode("\n", $data) as $row) {<br /> if(empty($row)) {<br /> continue;<br /> }<br /> $row = array_map('trim', explode('|', $row));<br /> if(!isset($cols)) {<br /> $cols = $row; <br /> continue;<br /> }<br /> $rows[] = array_combine($cols, $row);<br /> }<br /> return $rows;<br />}<br /></pre>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-88780073215405817452010-09-23T20:46:00.000-07:002010-09-23T20:50:40.756-07:00instantiating object inline via __callStaticThis is a nice hack for instantiating objects inline via php 5.3 __callStatic. What I dig about it v.s. the newObject('ClassName') is that a) You can pass in arbitrary constructors, w/ newObject it only supported the case of there being a single param to constructor i.e. newObject('ClassName', $x)->otherFunc($y) can now be N::ClassName($x)->otherFunc($y). I think I like the fact the class can be a "bare word" and that any number of constructors can be passed thus legacy classes etc. which may require arbitrary arguments can also be used. <br /><br /><pre class="prettyprint"><br />class Test {<br /> public function __construct($name, $age)<br /> {<br /> echo "Name: $name and Age: $age passed to constructor\n";<br /> }<br />}<br /><br />class N {<br /> public static function __callStatic($className, $args)<br /> {<br /> $class = new ReflectionClass($className);<br /> return $class->newInstanceArgs($args);<br /> }<br />}<br /><br />N::Test('some name', 300);<br /></pre>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-7463997726854971312010-08-24T09:11:00.000-07:002010-08-24T22:12:54.189-07:00transmogrified function compositionUsing the transmogrifier something like function composition such as: <br /><br /><pre class="prettyprint"><br />(define (compose . fs)<br /> (if (null? fs) (lambda (x) x)<br /> (lambda (x) ((car fs) ((apply compose (cdr fs)) x)))))<br /></pre><br /><br />Becomes possible like:<br /><pre class="prettyprint"><br />$compose = [$funcs | <br /> empty($funcs) ? [$_] : <br /> [car($funcs)($compose(cdr($funcs))($_))]];<br /></pre><br /><br />Which becomes: <br /><pre class="prettyprint"><br />$compose = function($funcs) use(&$compose) {<br /> return empty($funcs) ? function($x) { return $x; } : <br /> function($_ = false) use(&$funcs, &$compose) { <br /> return apply(car($funcs), array(apply($compose(cdr($funcs)), array($_))));};};<br /></pre><br /><br />I am not doing an exact copy here because I am not using ". rest" args as I would probably call this function like: <br /><br /><pre class="prettyprint"><br />echo $compose({square negate cube})(500); <br />//shorter than<br />echo $compose('square', 'negate', 'cube')(500);<br /></pre><br /><br />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.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-38567467771153236372010-08-13T22:58:00.001-07:002010-08-13T23:00:28.444-07:00php quasiquote preprocessorSometimes itches must be scratched. Last nights musing leads to tonights finished quasiquote for php: <a href="http://github.com/shaunxcode/php-quasiquote-preprocessor">http://github.com/shaunxcode/php-quasiquote-preprocessor</a>. 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.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-22451815765959708862010-08-12T18:05:00.000-07:002010-08-12T22:06:27.283-07:00Refactoring Voltron query DSLThis 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: <br /><br /><pre class="prettyprint"><br />$UserModel->getWhere(array('type' => 'and', value' => array(<br /> array('type' => '=', 'field' => 'name', 'value' => 'shaun'), <br /> array('type' => 'or', 'value' => array(<br /> array('type' => 'between', 'field' => 'age', 'value' => array(69, 100)), <br /> array('type' => 'like', 'field' => 'l_name', 'value' => '%name%')))))); <br /></pre><br /><br />So I went with a half baked dsl<br /><br /><pre class="prettyprint"><br />$UserModel->getWhere(Q::andWhere(<br /> Q::is('name', 'shaun'), <br /> Q::orWhere(<br /> Q::is('age', Q::between(array(69, 100))), <br /> Q::is('l_name', Q::like('%name%')))));<br /></pre><br /><br />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. <br /><br />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:<br /><br /><pre class="prettyprint"><br />$UserModel->getWhere(W('and', <br /> W('=', 'name', 'shaun'),<br /> W('or', <br /> W('between', 'age', 69, 100), <br /> W('like', 'l_name', '%name%'))));<br /></pre><br /><br />I pine for quasiquote:<br /><pre><br />$userModel->getWhere(<br /> `(and<br /> (= name shaun)<br /> (or<br /> (between age 69 100)<br /> (like l_name %name%))));<br /></pre>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-32220115878946379082010-07-27T13:54:00.000-07:002010-07-27T14:19:47.063-07:00quasiquote for primitive lispI 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: <a href="http://www.google.com/url?sa=t&source=web&cd=3&ved=0CCEQFjAC&url=http%3A%2F%2Fpeople.csail.mit.edu%2Falan%2Fftp%2Fquasiquote-v59.ps.gz&ei=OU1PTIDvB4q8sQPDrfyYBw&usg=AFQjCNGOzmDytfGGyjX1-dTD4lrFxcdLwA">here</a>.<br /><br /><pre class="prettyprint"><br />(define (qq-expand x)<br /> (cond ((tag-comma? x)<br /> (tag-data x))<br /> ((tag-comma-atsign? x)<br /> (error "Illegal"))<br /> ((tag-backquote? x)<br /> (qq-expand (qq-expand (tag-data x))))<br /> ((pair? x)<br /> `(append ,(qq-expand-list (car x))<br /> ,(qq-expand (cdr x))))<br /> (else '', x)))<br /><br />(define (qq-expand-list x)<br /> (cond ((tag-comma? x)<br /> `(list ,(tag-data x)))<br /> ((tag-comma-atsign? x)<br /> (tag-data x))<br /> ((tag-backquote? x)<br /> (qq-expand-list (qq-expand (tag-data x))))<br /> ((pair? x)<br /> `(list (append ,(qq-expand-list (car x))<br /> ,(qq-expand (cdr x)))))<br /> (else ''(,x))))<br /></pre>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-46542748015587383592010-07-19T17:48:00.001-07:002010-07-20T11:26:57.274-07:00PHP to javascript fluent expressionsThere 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: <br /><br /><pre class="prettyprint"><br /> UI::JSCall('object')->method(array('a' => 'b')); <br /> // renders to object.method({'a': 'b'}); <br /><br /> UI::JSReady('object')->method(array('a' => 'b')); <br /> //renders to <\script type="text/javascript">$(document).ready(function(){ object.method({'a', 'b'}) });<\/script> <br />*/note the escaped script tags for bloggers sake? */<br /></pre><br /><br />The source to make this happens is relatively trivial: <a href="http://code.google.com/p/phpviewadapter/source/browse/trunk/UI/JSCallBuilder.php">http://code.google.com/p/phpviewadapter/source/browse/trunk/UI/JSCallBuilder.php</a>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-16695493734250775372010-07-17T21:18:00.001-07:002010-07-18T00:19:46.978-07:00solving the word cube in voltronIn response to: http://programmingpraxis.com/2010/07/13/word-cube/<br /><pre class="prettyprint"><br /><br />/* returns array of words found in the 9 letters provided */<br />function solveCube($letters) {<br /> $words = newType(TFile, 'words.txt')->splitBy("\n");<br /><br /> return newType(TString, $letters)<br /> ->asArray<br /> ->powerSet('contains', $letters[4])<br /> ->map('permutations', I('asString')->in($words))<br /> ->map('join');<br />}<br /><br />echo solveCube('ncbciune');<br /></pre><br /><br />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. <br /><br />Next post I will delve into iterative permutation and powerset internals on Array as that is obviously where the meat of that solution lies.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-40747057367332024282010-07-05T14:47:00.000-07:002010-07-05T14:51:29.771-07:00voltron model calculated fieldsIn 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:<br /><br /><pre class="prettyprint"><br />class TimeRecord extends Voltron_Model<br />{<br /> protected $table = 'time_record';<br /><br /> protected $fields = array(<br /> 'id' => Type::Primary,<br /> 'created' => Type::Timestamp,<br /> 'created_hour' => Type::Calculated('created')->asDateTime->formatAs('H'));<br />}<br /></pre><br /><br />The magic lies with in the created_hour type definition which can also be written as Type::Calculated(I()->created->asDateTime->formatAs('H')). <br /><br />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.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-26570922247363635222010-06-30T16:07:00.001-07:002010-07-02T15:56:54.161-07:00voltron 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)<br /><br />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:<br /><br />Idiomatic php:<br /><pre class="prettyprint"><br />$names = array('peter', 'paul', 'mary');<br />foreach($names as $key => $val) {<br /> $names[$key] = strtoupper(strrev($val));<br />}<br />echo join('&', $names); <br /></pre><br /><br />Functional php:<br /><pre class="prettyprint"><br />$names = array('peter', 'paul', 'mary');<br />echo join('&', array_map(create_function('$val', 'return strtoupper(strrev($val));'), $names)); <br /></pre><br /><br />Functional php 5.3:<br /><pre class="prettyprint"><br />$names = array('peter', 'paul', 'mary');<br />echo join('&', array_map(function($val) { return strtoupper(strrev($val)); }, $names)) <br /></pre><br /><br />Voltron:<br /><pre class="prettyprint"><br />$names = newArray(VString, 'peter', 'paul', 'mary');<br />echo $names->map(L(val)->reverse->upper)->join('&');<br /></pre><br /><br />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.<br /><br />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. <br /><br />functional php:<br /><pre class="prettyprint"><br />$listOfList = array(<br /> array('a', 'b', 'c'), <br /> array('d', 'e', 'f'));<br /><br />echo join('<hr>', array_map(create_function('$val', 'return join(\',\', array_map(create_function(\'$val\', \'return strtoupper($val);\'), $val));'), $listOfList));<br /></pre><br /><br />Voltron:<br /><pre class="prettyprint"><br />$listOfList = newArray(VArray, <br /> newArray(VString, 'a', 'b', 'c'), <br /> newArray(VString, 'd', 'e', 'f'));<br /><br />echo $listOfList->map(L(val)->map(L(val)->upper)->join(','))->join('<hr>');<br /></pre><br /><br />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.<br /><br />Basically I am trying to get at the idea that oneliners don't have to be made of gnarleston heston.<br /><br />perl:<br /><pre class="prettyprint"><br />@p=(0,1);until($#p>20){print"$p[-2]\n";push @p,$p[-2]+$p[-1]}<br /></pre><br /><br />Voltron: <br /><pre class="prettyprint"><br />echo newRange(0, 1)->expand(L(x)->add(y), 20)->join("\n");<br /></pre><br /><br />Here is an example of finding primes.<br /><br />Python:<br /><pre class="prettyprint"><br />noprimes = [j for i in range(2, 8) for j in range(i*2, 50, i)]<br />primes = [x for x in range(2, 50) if x not in noprimes]<br /></pre><br /><br />Voltron:<br /><pre class="prettyprint"><br />$noprimes = newRange(2, 8)->map(N(VRange, L(y)->times(2), 50, y))->flatten;<br />$primes = newRange(2, 50)->diff($noprimes);<br /></pre>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-1947166903659163752009-09-30T21:35:00.001-07:002009-09-30T21:41:12.031-07:00APL jot dot in scheme and phpSo 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:<br /><br />PHP:<br /><pre><br />function jotDot($fun, $v1, $v2) {<br /> $result = array();<br /> foreach($v1 as $x) {<br /> $result[$x] = array();<br /> foreach($v2 as $y) $result[$x][$y] = $fun($x, $y);<br /> }<br /> return $result;<br />}<br /></pre><br /><br />Scheme:<br /><pre><br />(define (mapcar func list)<br /> (if (null? list) '()<br /> (cons (func (car list)) (mapcar func (cdr list)))))<br /><br />(define (jotDot func v1 v2)<br /> (mapcar (lambda (x) (mapcar (lambda (y) (func x y)) v2)) v1))<br /></pre><br /><br />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:<br /><br />The eventual "fluent expression" APL->php interpreted code to do something like calculating a times table up to 12 would look like:<br /><pre><br />$V = APL()->indexGenerator(12);<br />$V->jotDot('times', $V);<br /></pre>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-73141908370989820422009-07-27T15:45:00.001-07:002009-07-27T15:48:45.064-07:00more php5 musingWhilst in normal php-land another livable library for me is going to be my phparrayplus leveraging anon functions. <br /><br /><pre><br />$results = StaticClass::methodReturnsArray($arg)->eachPair(function($i, $x){<br /> return $x->lookMaNoTmpVars($i + 1);<br />});<br /></pre><br /><br />In my opinion so much prettier than<br /><pre><br />$results = StaticClass::methodReturnsArray($arg);<br />foreach($results as $i=>$x){<br /> $results[$i] = $x->lookMoCrappTastic($i + 1);<br />}<br /></pre>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8582403812843601208.post-56052561275382510692009-07-20T12:45:00.000-07:002009-07-20T12:46:27.058-07:00curry in php 5.3Here is curry in php 5.3<br /><pre><br /> function curry(){<br /> $args = func_get_args();<br /> $fn = array_shift($args);<br /> return function() use(&$fn, &$args) {<br /> $nargs = func_get_args();<br /> foreach($nargs as $narg) $args[] = $narg;<br /> return call_user_func_array($fn, $args);<br /> };<br /> }<br /><br /> $add20 = curry(function($a, $b){return $a + $b;}, 20);<br /> echo $add20(5); #25<br /></pre>Unknownnoreply@blogger.com0