|May. 17th, 2013 @ 09:50 am What's in a Call?|
[This one's for the programmers.]|
In the wake of our discussion about the new _edit() command the other day (and now that I've implemented that and am rapidly adding more commands), I think it's time to talk a bit about Calls and how they work in the QL language.
If you recall, QL is mainly a pipeline language, passing a "context" from hand to hand:
Each of those arrows represents a context being passed from one Stage to the next; each one transforms the context in some fashion and moves on. This example starts by specifying a Thing, fetching a Property from it, and embedding that value inside a text output.
[[My Thing -> My Property -> ""The property's value is ____""]]
That works well for the 90% case, where we're doing simple transformations -- the result is a language that is extremely light and easy for the most common cases. But how do we deal with less-common ones? That language is still evolving (indeed, I was up until 1am last night sketching out the syntax for closures), but in many cases the answer will be Calls.
While most Stages *look* they are simply naming a Thing, in fact each one is a method call. Simply naming a Thing calls the "apply" method on that Thing (terminology taken from Scala, on the theory of "steal from the best"). Thing.apply() simply returns a pointer to that Thing. Property.apply() expects the incoming context to be a Thing; it fetches this Property's value from that Thing, and passes it into the outgoing context.
Sometimes, though, you need to do something fancier -- for now, that means Internal Methods. These are built-in functions that are available throughout Querki. For the moment, they're all implemented in raw Scala code -- eventually I'll make it more possible to build these things in QL yourself, but that'll require more language refinement. I added two this week, and they illustrate the language in different ways.
The first is _edit(), which we discussed before:
(Tangent: since Querki has a flattish namespace, I am promulgating a convention that system names start with an underscore, and that user names shouldn't do so. That's a crude but reasonably clear way to reduce accidental name conflicts.)
That "._edit" is, again, a method call. The really important part, though, is the ".". This is how you identify the method to apply to the named Thing, if you don't want to use apply(). So in this case, we are calling _edit() on My Property, and passing in the root context. (That is, the Thing that we are displaying, which is usually the start of the pipeline.) _edit spits out the necessary HTML to edit that Property on this Thing, with the current or default value filled in, and the AJAX hooks needed for it to update the server when it changes.
That latter bit was my favorite enhancement for the week. For the Wedding RSVPs, I *really* didn't want invitees to have to click a "Done" button -- that just feels clunky. Nowadays, folks expect to be able to click a button or fill in a field online, and have it just *work*. There's no excuse for Querki to not follow suit, so I've now implemented that: the _edit() command is now live-updating. So you can basically make any display of any Thing dynamic, simply by using _edit.
Of course, there's nothing special about _edit from a language point of view -- for instance, you can just use it in a list context and it will Just Work. To take an example that I'm planning for the Wedding App, say that an Invitee can have a list of Children. On the Invitee's page, we can say:
That is, for each child, we show a bullet item with their name, and an edit control for their RSVP. That way, parents can handle their children's RSVPs easily. (I'll probably do the same thing for spouses -- expecting both members of a couple to RSVP separately is unrealistic.)
[[Children -> ""* [[Name]] -- [[RSVP._edit]]""]]
Then there are parameters, which are best illustrated by the _section() command:
This deals with what I've found to be an *enormously* common usage in Querki: displaying a List as a section of the page. The three parameters are sort of what you would expect, but there are a bunch of interesting nuances:
[[My List -> _section(HEADER, DETAILS, EMPTY)]]
As usual, the objective is to DWIM -- the system should work as easily and intuitively as possible. But that has all sorts of interesting implications.
- HEADER is printed first, as the header of the section -- its Context is the list itself, as a whole
- DETAILS comes next, repeated once for each element in the incoming List as its Context
- EMPTY is printed instead, if and only if the incoming List is empty
One of the most important and subtle is that the parameters to _section are (essentially) passed by name, not by value. In a typical programming language, parameters are passed by value -- the language evaluates the parameters *first*, and then calls the outer function call only once all of the parameters have been evaluated, passing in the resulting values of evaluating the parameters.
But that doesn't make sense in Querki, since frequently the point of the call is to be evaluating the parameters with a context constructed by the call. For example:
That is, the whole point of _section is that it is transforming the context, and then evaluating the parameters in the resulting context. So we can't evaluate the parameters *before* making the call -- that would be silly and pointless.
[[Children -> _section(
""## Your Children ([[_count]])"",
""* ____ -- [[RSVP._edit]]"",
""You don't have any children that I know of"")]]
This stuff is still evolving, and may yet become more flexible. When you look at it closely, it becomes clear that _section is more like a macro (in the modern hygenic-macros sense) than a function call -- it's a consistent way of adding a new syntactic structure. I expect that'll be somewhat common in Querki, but maybe not overwhelmingly so. It is fairly straightforward to take a call-by-name and treat it like a call-by-value (just start by evaluating the parameters in the outer context), but if it turns out to also be common to want call-by-value, we may add the ability for a method to declare how to treat its parameters. (Wouldn't be a shocking thing to want, but I'm not going to add it unless it turns out to be needed.)
*Whew* -- okay, enough for today. Congratulations to anybody who followed all of that -- it's the result of weeks of thinking, design and implementation, to figure out the underlying principles of method calls in Querki. It's not done yet, but it's starting to feel and work right. Comments, as always, are invited...