Previous Entry Share Next Entry
QL Stuff, part 4: Expressions, Phrases and Stages
device
jducoeur wrote in querki_project
[Picking up from where we left off, in "The Beginnings of QL".]

Last time, I introduced the notion of a QL Expression -- a thing in double-square-brackets like this:
[[My Property]]
As mentioned there, an Expression takes an incoming Context (initially, the Thing that defines the Expression), defines a transformation, and spits out an outgoing Context. Now let's get into a few more nuts and bolts. This is getting *very* technical, and is mainly aimed at programmers; feel free to skip to the example at the bottom to get the important gist.

(I confess, I'm writing this series mostly in order to make sure *I* understand what I'm talking about -- there's nothing like trying to explain something to demonstrate where your own thinking is muddled. And QL is an example of a subtle but important principle in programming: in order to build something that *looks* simple in use, you often have to get pretty complicated under the hood, with a lot of very clear and consistent abstractions so that the user can say, "Well, this *ought* to work" and have it actually do so. In the end, I don't expect more than 1% of users to read anything nearly as detailed as this explanation -- instead, I'm going to focus on examples and recipes, and try to have a system that is consistent enough that people can extrapolate from there.)

Phrases

An Expression, technically, consists of any number of Phrases -- if you think of a Phrase as similar to a statement in an ordinary language, you're not too far off. A Phrase is ended with a newline, or the end of the Expression. (Or, now, a semicolon -- I have been finding use cases where I want multiple Phrases on a line, so added semicolon as an option yesterday.) A Phrase always receives the initial Context (usually the Thing that defines the Expression) at the beginning of the Phrase, and ends by producing a new Context. Normally, the Context at the end of the Phrase will be rendered as Wikitext and displayed.

The truth is, Phrases are only of modest utility, and I don't expect to see many multi-Phrase Expressions in practice. They are mostly going to be useful in relatively rare but complex cases where you want to define a value and use it in multiple ways. I do not actually have any serious use cases for them yet, but can see them coming down the pike, so I included them in the architecture.

Stages

A Phrase is made up of any number of Stages, and these are front-and-center when you are doing anything serious.

As mentioned before, QL is basically a simple data-processing language. So what you are doing is starting with Thing A as the Context, and passing that from Stage to Stage, transforming it at each step. At the end of the Phrase, you turn the result into Wikitext. I expect single-Stage Expressions to be the most common, but I expect two-Stage ones to be almost as common, because they are how you do fancy formatting. There is no limit to how many Stages you can pass through (and this, in turn, should allow you to perform any normal database operation), but in practice I expect complex Phrases to be unusual.

Methods

Each Stage is essentially calling a Method. That's not always explicit, but as mentioned last time, if you just name a Thing, it will call the _apply() Method on that Thing. So when I say:
[[My Property]]
I am passing the current Context -- the Thing we started with -- to my-property.apply(), which returns a new Context containing the value of that Property on the incoming Thing.

A Stage can name a Method other than _apply() with a fairly ordinary dot syntax:
[[My Utilities.doSomething]]
means that we call the doSomething Method that is defined as a Property of my-utilities. Methods aren't implemented yet, but will simply be named QL Expressions. You will use Methods in Querki for the same reason you do in any other programming environment: to factor out common operations.

(There will eventually be an ability to add additional parameters to Method calls, using parentheses. Those are in the design, but I haven't gotten to any need for them yet. They will mainly be used to better define the transformation -- for example, [[My List -> _columns(3)]] will mean "render my-list as three columns in the resulting HTML". Every Method implicitly has the incoming Context as a parameter, and that's enough in most cases.)

Multi-Stage Phrases

That last parenthetical scratched the surface of a two-Stage Expression, so let's play with that. Continuing my CD-collection example from last time, we've already established that an Album has a Recorded By property, and that points to an Artist. Say that the Artist has a Website property, that I want to include in my display of the Album. My Display Text for the Album now becomes:
#### [[Display Name]]

By: [[Recorded By]] ([[Recorded By -> Website]])

#### Notes
[[Notes]]
That displays as:

Verdant

By: Emerald Rose (http://emeraldrose.com)

Notes

I haven't listened to this yet, but I really liked them in concert, so I'm looking forward to it.
The Website reference there is a classic two-Stage Phrase. It starts with the current Thing (the Album), fetches the Recorded By property (which is a pointer to the Artist named Emerald Rose), fetches the Website property from *that* (giving a URL), and then reaches the end of the Phrase so it renders that URL.

The key is actually the "->" operator in the middle there. That operator is pronounced "map" if you're a programmer, and means "feed all elements of the incoming Context into this Method". So (usually) if you're feeding it one Thing, you get one result; if you're feeding it a List, you get a List; if you're feeding it an Optional Property, you get an Optional result. In programming terms, that's kind of modern and esoteric, but by and large it means that you will usually get the most obvious results based on what you tell it to do. (And this, in turn, is why all Properties in Querki are technically Collections -- that way, the map operator is very consistent and easy to reason about.)

All of which is a really complex way of explaining an expression that I hope is pretty horribly obvious when you actually *look* at it. Again, the point of the exercise is to get a language that is about as concise and clear as I can make it.

Next: Text Stages, and making things pretty
Tags:

  • 1
1. This may be something you'd already planned on covering in Text Stages, but JIC not: In the above example, if an Artist has no Webpage, you'd get a pair of contentless parens after their name. Any way to make those contingent on the presence of the Webpage property?

(This ability is something that exists in most systems I've used for this sort of thing, but which I can't recall ever ending up particularly pretty / easily legible in the markup.)

2. Can you give an example of a multi-Phrase Expression?

3. If you're ever turning this post into user-friendly documentation, I'd suggest leading with (a) the example and (b) the simplest concepts (Methods / Stages), then building up to Phrases and Expressions from there, showing how they're built up from the smaller pieces.

(If turning into man-page style reference materials, that's a different story. :)

1. Precisely correct -- your point is why you would actually want to render this example with a Text Stage. (Indeed, I suspect that three times out of four, multi-Stage Phrases will end with a Text.) But those are a complex enough topic unto themselves that I wanted to pull them out into the next article. Good catch, though, and I was thinking about that as I wrote the article.

2. This is all still rather nascent: it's designed in my head conceptually, but since I don't have a good use case, I haven't tried to reify it. But the high concept runs like this.

Querki doesn't have variables per se (since it's a pure functional language), but it will eventually have named local values; these will tentatively always start with dollar sign, so $foo. These are the equivalent of let clauses in traditional functional languages, and I've never seen one that could get away without them, so I assume they'll be needed.

I can see wanting to be able to do things like this (again, I don't have real examples yet, but I'm guessing from experience):
methodA -> methodB -> methodC -> $myList
$myList -> ""[[_collectiveHeader]] ([[_count]])""
_splitList($myList) -> ""...render a part of the List""
That's all vague and hypothetical, but I suspect stuff along those lines will be necessary. Note that the first line is the exception to "Phrases always become Wikitext" -- when a Phrase ends in a name assignment, the end result is effectively Unit. (The empty value.)

Basically, we will probably need ways to build relatively complex functions in the long run, for more powerful apps. That's likely to require the ability to define values, reuse them, put them in parameters, and so on. For now, I'm just intuiting that that's likely to be necessary; we'll see if I am correct.

3. Probably correct. These posts are relatively stream of consciousness, but I know that real documentation needs to be very different. Like I said, my main focus is likely to be examples and tutorials; I haven't given a lot of thought to the long-term design of the reference materials yet. I will probably ask the community to help me refine and clarify those, as we get to it...

  • 1
?

Log in

No account? Create an account