A look at pooQuery internals

Tags:

I have received some questions regarding pooQuery (pQ from now on)…

  • Does this really work?
  • How does it do all this?

Short answer: Yes, it does work, and what makes it tick is magic… or not, so let’s look at the tricks behind fluent languages.

The core concept

The core of pQ is what I’m calling the intermediate object – basically a class which allows you to chain commands.

pQ chains begin with a function call: take(), assign(), get() or call(). Each of these functions return whatever was passed to it inside an intermediate object. For example in the cases of take and assign, what you get is the And intermediate, which is a basic object which only has one purprose: to let you use ->and-> to chain something else.

How does the chain work

The chain is basically just a bunch of classes. Let’s look at something simple:

take('foo')->and->display();

First, the take method takes ‘foo’ and returns a new AndObject. The AndObject’s constructor is given a new IntermediateObject, which in turn is given the string ‘foo’ as a parameter. When you get the and property, the AndObject returns whatever was assigned to it – in this case, the IntermediateObject. This way you can then call whatever methods/properties the returned object contains.

The IntermediateObject has a method called display, and calling it will simply display the value assigned to the object. Most other methods it has, however, involve passing the value along. This is again simply achieved by creating a new object and passing the value as a parameter and then returning the new object. This way the original value is passed along the chain.

“Self-referencing” with it() and its()

pQ has two functions with a more special meaning: it() and its().

it() is a very simple function. It just returns an instance of ItObject, which in turn is simply something that is used to determine that the user wants to refer to the current value. We could also have used something like a string ‘self’, but what if the user wanted to assign the string ‘self’ and not refer to the current value in the chain? This is why the ItObject exists – its sole purprose is to be the absolute identification of the current value.

its() is also quite simple: It has just a method called method(). This is used to identify class methods when chaining with call(). If you simply pass a string to call, it will use the string as a function name to call, but if the result of its()->method( … ) is passed, the call method looks at the value and parses it as a method of the currently active value, which is expected to be a class instance.

What about conditionals and looping?

pQ does not have conditionals or looping, at least not yet. I’ve been thinking of several possible ways to implement them, but they are quite complex, especially if you want more fine-grained control.

For example, doing something like if(‘hello’)->equals(‘something’)->call( … ) … isn’t as simple as one might think. At first, it may seem simple: Just pass hello and something to a method which compares them. But what if they aren’t equal? What do we do then?

We can’t simply return null: the next chaining command would fail with an error. The chain could also be longer than just what the above example shows, so we still need to return a fully functional chainable object. Something that could be done is “sterilizing” the chain: Add a method to the IntermediateObject, which when set, will make the object “ignore” everything – just return new chainable objects, but do nothing else.

One might also want to do the above in reverse: call( … )->if(‘foo’)->equals(‘bar’) – in other words, put the condition after the chain. How do we do something like this? In theory, we could reverse the operations done in the chain, but in practice this would require a lot of things: We’d need to store all the previous steps in the chain, we’d need to implement a mechanism for undo’ing all the previous steps…

As you can see, conditionals aren’t very easy. What about looping then? Similar principles apply to them, but it’s even more complex than conditionals. We could collect the chain in an array and then call each step in the array in the correct order for the specified counts… but that’s a lot more complicated than it sounds.

In closing

The basic features of a fluent language aren’t very complicated, but they do require some thought. Otherwise it’s very easy to end up having difficult to maintain code. However, when stepping forward into more intricate features of programming languages like conditionals and looping, you may face a huge challenge.

And I like challenges like this, so I intend to continue working on pQ :D