Introduction to QL

QL is the programming language built into Querki. Its primary purpose is to take the data in your Space, and transform that into whatever form you want to show on-screen. It doesn't pretend to be a general-purpose language, and is focused mainly on making the most common operations as easy as possible, but you'll find that it is surprisingly powerful when you need it to be.

What is QL?

QL is a pure functional pipeline language. That means:
  • "Pure" -- QL expressions are free from "side-effects". There are no variables, and there is no mutable state. This means that QL expressions look quite different from programs in many traditional programming languages, but they tend to be easier to assemble and reason about.
  • "Functional" -- each QL expression is essentially a function: it receives an input, transforms it, and produces an output.
  • "Pipeline" -- in practical terms, QL expressions are constructed as pipelines, with the data flowing from stage to stage, getting transformed by each.
The guiding philosophy of QL is that Simple Problems Should Have Simple Solutions. The more common an action is, the easier it should be. This is shown in the two most common uses of QL. This is how you link to a Thing from Text: [[My Thing]] and this is how you include the value of a Property that is defined on the current Thing: [[My Property]] Why these don't contradict each other is discussed later; suffice it to say, QL is designed to follow the DWIM Principle ("Do What I Mean") as often as possible.

Context

A key concept in QL is the current "Context". This basically represents the current state of the expression -- a value, plus some metadata. The purpose of a QL Expression is to transform a Context into another Context. We say that any given expression or stage receives a Context, transforms it, and produces a new Context.
In general, a QL expression starts off with the Thing that you are looking at. For instance, in this example: [[My Property]] If you are looking at the display of My Thing, this will receive My Thing as the initial Context. It passes that to "My Property", which knows that, since it is a property, it should fetch the value of My Property on My Thing, and produces that value as the new Context.

Expressions, Phrases and Stages

A QL Expression is everything inside double-square brackets, as shown above. It runs through everything in it to get a new Context; that result is then usually rendered as QText. (There will be some exceptions eventually, but at the moment that's how you use it.)
An Expression is made of any number of Phrases; these are separated by newlines or semicolons. Right now, there isn't much use for multi-Phrase Expressions, so don't worry about them. (They will become useful once we implement named values, but that's a little ways off.)
A Phrase is made up of a number of Stages. Each Stage describes a single transformation, and the Stages are separated by arrows. For instance, here is a fairly normal QL Expression of three Stages: [[Children -> _filter(Is Active) -> _bulleted]] Assume that "Children" is a Link Set Property, and "Is Active" is an Exactly One Yes/No Property. The above means, "Fetch the set of Children on the current Thing; filter them to keep only the ones where "Is Active" is True; and display the results as a bullet list". Programming in QL is generally like this: a series of steps, each one transforming the input a bit.

QLText Stages

As mentioned earlier, QLText (or simply "Text") is QText with QL embedded in it. You can define a Stage as further QLText, by surrounding it with "" (double double-quotes) on the start and end. (And that can have deeper QL embedded inside it.)
This is still a Stage, just like any other. It receives a incoming Context, and transforms it into QText. It is extremely common to have a QLText Stage at the end of your QL Expression, to get the output to look exactly as you like.
For example, the "Next" link at the bottom of this page actually looks like this (this is a pretty complex example, but shows what you can do in just two Stages): [[nextInList(Subsections.refs -> Subsections) -> ""| Next: [[Display Name]] ->""]] What's going on here?
  • The Expression initially receives this page as its incoming Context.
  • _nextInList() looks up whether there is another page which list this one in the "Subsections" Property (that's what _refs does); if so, it fetches the next page from the Subsections list.
  • It passes that next page (if there is one) to the QLText Property that is delimited by "".
  • Inside the QLText is another QL Expression, [[Display Name]]. This is receiving the next page, so it fetches the "Display Name" Property from that next page.
  • Inside the QLText, everything surrounded by __ becomes a link to the received Context. So it renders "Next: Display Name ->" as a link to the next page.
There's a lot there, and it'll be easier to understand when there is IDE support for QL. (Which is planned for 2014.) But the key thing to keep in mind is that you're just building up transformations, step by step. This makes it easy to write QL one piece at a time, trying it out and seeing if you get the expected results, and then adding the next Stage. Once you get the hang of it, it is usually easier to write bug-free QL than most languages.