Holstege, Mary. “Invisible Fish: API Experimentation with InvisibleXML.” Presented at Balisage: The Markup Conference 2024, Washington, DC, July 29 - August 2, 2024. In Proceedings of Balisage: The Markup Conference 2024. Balisage Series on Markup Technologies, vol. 29 (2024). https://doi.org/10.4242/BalisageVol29.Holstege01.
Balisage: The Markup Conference 2024 July 29 - August 2, 2024
Balisage Paper: Invisible Fish: API Experimentation with InvisibleXML
Mary Holstege spent decades developing software in Silicon Valley, in and around markup
technologies and information extraction. She has most recently been pursuing artistic
endeavours. She holds a Ph.D. from Stanford University in Computer Science, and has
a long history of throwing concepts from linguistics at computing problems.
Developing a simplifying textual interface over a complex set of components can be
tricky. Here I use Invisible XML to isolate changes in the input language from the
implementation, allowing for experimentation with wildly different approaches to the
interface. An intentional, conlang approach to the experimentation is highlighted.
This paper is about using Invisible XML (IXML) to experiment with a textual interface over a complex underlying set of capabilities.
The output from IXML is an XML document representing the parse tree, which an XQuery program interprets to make the appropriate API calls and produce the desired behaviour.
The IXML grammar specifies which non-terminals will be represented in the tree and
whether they will be represented by elements or attributes. IXML rules can further
modify the output to suppress or introduce various terminals as well. Because of this
flexibility in determining what the XML parse tree will look like, processing of the
parse tree can be decoupled from the details of the textual interface. This makes
experimentation with those details easier.
Given a framework for experimenting with the formulation of a textual interface, how
should we proceed with that experimentation? I suggest an approach based on the idea
of treating the API as a conlang (constructed language). That means we think about
the total space of syntactic, morphological, and symbolic possibilities, and choose
which to apply with intention. It also means acknowledging that we are dealing with
a language that, despite being interpreted by computer programs, is also and primarily
for human beings to communicate with each other. The choices we make in designing
our conlang will make things more or less difficult for them.
My context here is making a textual interface to use to create complex drawings using
high-level artistic components. The idea is that to create a scene of five small silver
fish covering a black canvas, instead of writing a pile of code and organizing the
appropriate API calls, the driver does all that and you just give it an input string
like "5 small silver fish cover black canvas". I will show something of my experimentation
and discuss how the analysis of the conlang for that interface has also helped with
my own understanding and rationalization of the underlying components and how they
function.
Normalizing with IXML
An IXML grammar consists of a series of rules relating a non-terminal symbol to a
series of non-terminal symbols and terminal strings along with various combining operators.
The grammar is used to parse an input string to produce an XML document. Each non-terminal
can be marked to indicate whether it should be included as an element with the same
name (the default), included as an attribute with that name, or excluded entirely.
Terminals are included as content unless marked to be excluded. In addition, terminals
can also be introduced de novo without having to be present in the string being parsed.
The current working draft of the IXML specification also supports the ability to provide
new names for the elements and attributes instead of using the non-terminal names
directly. These features can be used to normalize the output.
IXML can normalize output in four ways:
By using the inclusion and exclusion symbols on terminals, IXML can normalize element
and attribute contents
By using inclusion and exclusion symbols on non-terminals, IXML can normalize the
nesting structure
By using attribute markers on non-terminals, IXML can normalize the output ordering
By using rename markers on non-terminals, IXML (working draft) can normalize the names
of elements and attributes
Let's look at a concrete example.
The grammar fragment in Figure 1 gives a simple infix grammar for addition and subtraction expressions. Read from
top to bottom it says that an expression (expr) is a left operand (left) followed by an operator (op) followed by a right operand (right). Each of these operands is a number (number) and should be exposed as attributes (the @ symbol). A number is a sequence of digits from 0 to 9 but there should be no number element in the output (the - symbol). The operator is either addition (plus) or subtraction (minus) and should also be exposed as an attribute. Finally the plus and minus operators
are represented with the appropriate symbol and those elements are also removed from
the output.
Given this grammar, parsing of the string "4+5" gives the XML document
<expr left="4" op="+" right="5"/>.
The expr non-terminal is included as an element name, the left, right, and op non-terminals are included as attributes (using the @ symbols), and the plus, minus, and number non-terminals are represented only by their content in the parsed XML (due to the
- symbols).
A different representation for simple arithmetic operators is a functional notation.
Figure 2 gives a grammar for that. It says an expression is an operator followed by the left
and right operands in parentheses and separated by a comma. Here the operator is represented
by a name ("plus" or "minus") but that name should be removed from the output and replaced with a corresponding
symbol.
In this grammar the operator is given in functional form with a name instead of using
an infix operator symbol. The rules for the plus and minus non-terminals normalize the result by removing the operator name (with the - symbol) and introducing the operator symbol (with the + symbol).
Parsing of the string "plus(4,5)" therefore also gives
<expr left="4" op="+" right="5"/>.
IXML has normalized the operator names to the operator symbols, and since both the
operator and the operands are attributes, the order doesn't matter, so that has been
normalized as well. By removing extraneous terminal and non-terminals, the expression
has been boiled down to its essence, so the nesting structure has been normalized
as well.
Attribute order might be different, but that doesn't matter. If the interpreter of
the XML doesn't care about element order either, that gives us even more flexibility.
While IXML 1.0 doesn't currently let you change the names of elements and attributes,
the working draft does. If all else fails, adding a simple XSLT transform can make the adjustment. We can then experiment with different ways of
expressing the operations of the API without having to touch the underlying interpreter.
IXML normalization insulates the interpreter from experimentation in the textual interface.
In my case I am experimenting with a textual interface to various drawing components.
The textual interface describes the desired scene. The code that interprets the interface
and makes the appropriate calls to the drawing components is fairly complex. Being
able to experiment with how to express the desired outcome without having to perform
major rewrites of that code is very helpful.
Conlangs
After being exposed to the wonderful variety of morphological and syntactic features
present in the world's languages, students of linguistics are often tempted to try
their own hand at pulling together a few of their favourites to make their own language.
Long before Hollywood science fiction and fantasy epics turned this into a cottage
industry, many a linguist filled little notebooks with syntax diagrams and morpheme
lists for their own constructed language (conlang for short).
I am no exception.
Rather than invent Dothraki, I went on to a career in software. My experiences with conlangs stuck with me, however.
When I analyze computer languages and APIs, or design them, I try to take a conlang
stance:
Be intentional in your choices
Enlarge the range of choices you consider
Remember this is a language for humans: be systematic, but allow for appropriate exceptions
The actionable implementation of this approach is to determine what the important
entities and grammatical classes and relationships are in your domain and then decide
how to indicate them with vocabulary and syntactic devices. Here are some of the things
to think about.
Consistency sets expectations
Even where function syntax is determined by the programming language, the syntax of
the function and parameter names is not. The ordering of parameters is a choice. Even
in cases where parameters are identified by name rather than ordering, the order used
in examples and API documentation is still a choice. The set of parameters and how
to expose them is a choice. Each of these choices creates expectations that make the
API easier or more difficult to grasp.
Let's look at a simple example.
Figure 3 shows a set of function names. In this case the names are all consistently a target
(a noun) followed by an action (a verb). Almost. The morpheme "properties" is not
an action at all: it names the result of the action. Still, the symmetric force of
the API encourages us to think of it as an action, perhaps an elided form of "get-properties".
What other inferences does this API encourage us to make? We probably assume that
"document", "collection", and "link" are all alike in some way, and that "get", "delete",
and "properties" are all alike in some way. The API is also telling us that the most
important thing is the target. What the functions actually produce is less important.
We probably further assume that if we encounter a new relevant target "thingy" and
a new relevant action "reckon", that we would find functions named thingy-reckon() and collection-reckon().
In this example we're looking at pure function names, but this is the same approach
taken when we have classes named for targets in object oriented programming and methods
named for simple actions. This is the same approach taken when creating fine-grained
namespaces that name targets and simple action names for functions in that namespace.
What happens if we turn things around: action first?
First, the non-action "properties" starts looking ever more problematic. It really
isn't an action, and this formulation makes that gratingly obvious. "get-properties"
really would work much better here.
We're still going to assume that "document", "collection", and "link" are all alike
in some way, and that "get", "delete", and "properties" (urgh) are all alike in some
way, although the mismatch between what we expect to be an action and its English
name might make us start to assume that there is something really quite different
about the "properties" series of functions. We're going to expect get-thingy() and reckon-collection(). Furthermore, this leads us to think of the actions as the main thing. We may even
expect to see generic get(), delete(), and properties() methods that dispatch to these specific ones. This is not at all an expectation from
the target-first approach, and indeed with that approach it can be harder to imagine
such methods or how to name them.
This is the approach taken when we use namespaces for specific kinds of actions, where
the actions operate over different targets.
From a conlang perspective we are collecting vocabulary lists here, and inventing
grammatical categories and syntax rules for combining items in these categories. Users
of these APIs can use these rules and implicit categories to navigate the API and
make predictions about it. Developers of the API can use the rules to make decisions
about how to extend it.
Where there are no rules, APIs become less predictable and therefore less usable.
People being pattern-finding creatures, they will try to make sense of the mess in
front of them, finding faces in the random rocks of Mars.
Folks might assume from the collection of names in Figure 7 that "document", "collection", and "link" are all different. They might assume that
"get" is an operation that doesn't apply to "link", that there is something fundamentally
different about "properties-of" applied to "document" and "properties" applied to
"collection". Perhaps getting properties is a more complex operation for "collection"
and "link". What should we predict about "thingy" and "reckon"? thingy-reckon() or reckon-thingy()? Hard to know. As folklore has taught us for millennia: names matter.
People will invent reasons for differences and similarities, regardless of intention.
Best make them intentional.
Word order
Let's take another example. Consider what it looks like when you are applying a series
of operations one after another. Programming languages tend to conceive of operations
as being done by some anonymous and ubiquitous agent (the computer) hence the imperative
style. The functional language viewpoint, however, takes the "thing being operated
on" (in the classic view) as the agent, the one performing the action. So let's run
with that viewpoint and deem that agent as the subject (S), the action as the verb
(V), and the operands as the object (O). When we combine a series, one whole operation
(e.g. S+V+O) embeds in the next as its subject. What does embedding look like with
different word orderings?
Consider this representation in XQuery arrow syntax of a series of operations: first
a negative translation, then a scaling by half, and then a positive translation.
This kind of embedding is like English syntax, and for English speakers reads naturally
from left to right, telling us what happens in temporal order. It isn't the only way
to go about things, however. The conlang perspective tells us to consider the range
of possibilities, to look outside our familiar tongue.
Here is the same set of operations, but represented as the value of an SVG transform
attribute. The operations apply from the right to the left.[1]
SVG appears to use this ordering to be consistent with the ordering you'd see if the
transforms were partitioned into child groups instead. You can add more operations
by prepending them to this string or by wrapping the transformed element in a new
group with a new transform attribute with the new operator. The transforms apply from
the inside out: in text order, from right to left. You can think of this as the "but
first" approach: "translate by 800, but first scale by half, but first translate by
negative 800". You're saying the most important thing is what happened last, but straining
the memory of your readers on figuring out the order of events.
XQuery functional syntax uses center embedding with VSO order.
The English gloss of this is something like "translate the scaling of the translation
of the item by -800 by half by 800". In natural language, center embedding often leads
to comprehension difficulties, stressing human short-term memory as you build up a
stack of pending items before getting to the center where you can start to unroll
things.
There are other logical possibilities.
SOV ordering is actually more common in languages of the world than English's SVO.
With left embedding this is the order of certain kinds of calculators.
This ordering tends to emphasize the operands over the operators and creates a sense
of closure for each operation. This is also an "about-that, and-then" kind of formulation,
something like "take minus 800 and translate by that, and then take half and scale
by that, and then take 800 and translate by that".
OVS and OSV orders are quite rare in natural languages, but they do occur.
This ordering reminds me a little COBOL data and procedure sections: first all the
data you need, then the series of operations. For that matter, a lot of GPU programs
look like this too. An English gloss of is the "respectively" construction, something
like "take 800, and half, and -800: now translate, scale, and translate by those respectively".
Finally, for completeness' sake, here's what OSV with right embedding looks like,
a combination of "but first" and operand-forward:
"Take 800 and translate by it, but first take half and scale by it, but first take
-800 and scale by it."
Grammatical gender
English is relatively impoverished when it comes to markings for case, mood, tense,
person, gender, and so on. Our computer languages need not be similarly impoverished.
One way to think about Hungarian notation is that it constructing grammatical gender
classes for variables and marking them with gender prefixes. For example the variables
named szName, pName, and nName are marked with the affixes sz, p, and n to indicate that they are, respectively, a zero-terminated string, a pointer, and
a count. Number (1 versus many) is also marked: aszName for an array of strings as opposed to szName for a singleton.
Other gender and number systems are possible. Sometimes APIs will use their own markers
for some idiosyncratic purpose. For example, the Windows' API uses m_ to mark a data member of a class, g_ for a global, cr for a colour reference value, and so forth. These combine, so that m_psz is a pointer (p) to a zero-terminated string (sz) that is a member of the class (m_).[2]
This kind of thing gets criticized for getting in the way of readability (when the
markers are small abbreviations), writability (when they are longer words), and for
introducing the possibility of a mismatch between what a name claims and what it actually
is and thus impeding refactoring. It is most useful when the naming convention tells
you something the compiler or interpreter couldn't already tell you, such as whether
a string is safe or unsafe, that is, an internal string or a string coming from input
that needs to be sanitized.
Mood
Computer languages tend to operate in an imperative mood, giving commands to the computer,
as it were. But let's consider, in the conlang way, the full range of possibilities.
Is imperative really the best for a particular situation? Is there really only one
mood in play? And if not, how should we indicate the variation?
First, let's look at some of the possibilities (by no means an exhaustive list):
Mood
Paradigm
Usage
imperative
(you) do that
commands
interrogative
does he do that?
queries
indicative
he does that
statements
subjunctive
he would do that if he were able
counterfactuals
conditional
he would do that if he were able
conditionals
presumptive
he must be doing that
probable truths
potential
he likely did that
likely truths
hypothetical
he might have done that
possible truths
inferential
I conclude that he did that
inferences
Natural languages don't generally distinguish all these situations in a systematic
way, and English typically just uses modal verbs of some sort for most of these. We
can analyze what some computer languages do in these terms as well.
Consider SPARQL CONSTRUCT versus ASK. It is in the usual imperative style, but we
could also analyze it as "CONSTRUCT" being a modal verb to mark the indicative and
"ASK" as marking the interrogative. Given that analysis, the distinction could have
been marked in some other way.
If we consider a language such as RDF for representing knowledge, wouldn't it be interesting
to consider the range of moods to express state of certainty (inferential, presumptive,
hypothetical, et al) and finding a way to express that in the knowledge representation
language?
At the very least, it is worth considering whether a particular part of the API is
operating in imperative, interrogative, or indicative mood. This does get reflected
even in names. For example, get-properties() is imperative ("(computer) get me the properties of ..."), and properties() is indicative ("the properties of ... are ..."). I find my libraries in functional
languages are drawn towards the indicative because it describes an outcome: this and
that will happen (somehow). The imperative tends to suggest an order of operations:
do this then do that.
The language is for humans
The conlang approach suggests that we consider that the language is for use by human
beings. Software developers often forget this, assuming that the purpose of a computer
language is to be interpreted by some bit of software. While that is true, it is also
a misleading and limited view. Software APIs need to communicate with human beings
as well: humans who use them, humans who debug them, humans who refactor and extend
them.
The argument for Hungarian notation markers is exactly this: knowing at a glance that
something is a counter versus an index helps humans better understand what the software
does. Software libraries are full of all kinds of little conventions like this, even
if they are not systematically defined. Personally, I will use ix to mark an index that may be unordered rather than strictly sequential. Thus $jix is a positive integer in some range, but $j will be a positive integer running from 1 to the upper limit.
A consequence of keeping humans in mind is that it becomes important to maintain fairly
consistent rules even for aspects of the API that aren't important to the computer.
We've seen a few examples of this already: a standard ordering for targets and actions
in the naming of functions, or the use of markers to indicate classifications of variables
that aren't important to the compiler.
Another example to consider is the robust, cross-linguistic observation that higher
frequency (Zipf36), more predictable (Levshina22), or simpler (Lewis16) terms tend to be shorter. Thus: "a", "it", "cat" versus "jaguar", "caterpillar",
"transcendentalism". Default case, gender, tense, etc. markers tend to disappear
entirely. Thus we have "il entend" (present) but "il entendra" (future) and "il entendit" (past).
The application to APIs is direct: simple common functions should have short names.
Markers for default or common situations should be shorter than those for non-default
and less common situations, or absent entirely. This is a plausible rationale for
the use of "properties" as an action in the prior examples: keeping common operations
shorter in their expression.
Another thing humans expect to see is short and consistent case frames. Here's one
analysis of case roles for English clauses (Winograd83, Figure B-13):
The idea is that there are a few central roles for each kind of process. Given a set
of API functions it is useful to think through the types of processes they represent,
the central participants for a collection of similar operations, and be consistent
in how they are ordered and indicated. The types of processes and their participants
are not necessarily identical to those of English. It is also useful to distinguish
central participants (such as Medium in Material Process) from secondary participants
(such as Manner in Circumstances). There is always a temptation in software to start
larding up functions with an endless array of parameters and options. The conlang
lesson here is that less is more and that a little analysis and organization can prevent
a complicated and unusable mess down the road.
Invisible Fish
Experiments with textual art interface
The textual art shell operates in the following fashion:
Text is input to a command prompt, for example:
art> 5 small silver fish cover deepsea canvas at random
IXML parses and normalizes the input. A small XSLT stylesheet provides a bit of contextual
defaulting. The result is an XML description of the desired scene:
The art shell driver looks up, dynamically loads, and calls the appropriate components
and performs the appropriate scene adjustments, generating the result as an SVG file.
What I'm going to focus on here is using IXML normalization to experiment with different
kinds of expression of the desired outcome, and some possible variations driven from
a conlang perspective.
The initial goal of this project was straightforward enough: drastically reduce the
effort in combining some of the drawing components into a new work. Creating a new
work already involved starting with a template and filling in the gaps with the appropriate
combination of function calls. I was interested to see how much of this effort could
be abstracted away and still be able to produce a variety of interesting results.
The driving conlang principle here is: this is a language for humans (specifically:
me). I wanted to keep things simple enough to remember, while retaining as much power
as possible.
The first question was one of mood: imperative or indicative? Since for me the only
possible command was "draw", adding it seemed redundant. Operating in an indicative
mood made it possible to use verbs to represent scene relationships in a way that
worked out well.
A quick note on ambiguity
A number of the grammar fragments in this section are ambiguous, allowing multiple
possible parses. While it is prudent to avoid ambiguity as much as possible, I don't
tend to worry about it much. I use a system (CoffeeSacks) that lets me define a function to resolve ambiguity appropriately.
The function I use takes a greedy approach and always picks the option with the longest
matching prefix. This resolution mechanism is easy to implement, easy to understand,
and gives me good control over the results. It is conformant with how many regular
expression processors resolve ambiguity, so it also has the advantage of familiarity.
I recommend it.
Representing things: characteristics of things
When something is drawn, it will have various characteristics: size, colour, and so
on. How should these be expressed? There are a number of options: as a single function
with a set of parameters, as a specific function with named parameters, in a more
natural English style where the parameters become attributes modifying a noun, or
expanding our horizons in the conlang way, through using attributive affixes. Each
of these variations uses a different IXML grammar to produce the same XML for the
driver to process. Let's look at a few of them, all to express the idea of a group
of five small fish rendered in red with a speckled stylistic effect.
Function-style syntax with ordered arguments
First let's use a functional representation, resembling a light gloss on an actual
underlying function call. The expression would be:
thing(fish, 5, 0.25, red, speckled)
The relevant part of the IXML grammar turns the arguments into attributes on the thing element:
This is straightforward, but we are already veering into fairly long parameter lists,
and that's before we get into things like brush strokes, pen angles, and texture variations.
Which parameter was colour, again? Let's see if we can do better.
Functions with named arguments
The expression in this case shifts the kind of thing to the function name, and uses
name equals value pairs for the drawing characteristics, allowing them in any order:
The relevant fragment of the IXML grammar drops the intermediate options and option elements and uses the argument names to identify which attribute to create, dropping
the extra bits of syntax:
This formulation keeps us from having to remember the proper parameter order. It also
lets us leave out some parameters, so defaults will need to be supplied. A more sophisticated
grammar could do so, or we could rely on the driver to supply them.
Adjectives before noun
What about using a more natural English style with attributes modifying a noun? Something
like:
5 wee red speckled fish
We could allow the adjectives in any order, but the truth is that English speakers
expect certain classes of adjectives to come before other classes of adjectives, so
that "five small red fish" is fine, but "five red small fish" is
weird.[3] So this grammar fixes the order, which helps manage the ambiguity caused by colour being an open class.
The IXML grammar normalizes size adjectives to numeric values and suppresses the intermediate
options element:
It is time to push the envelope a bit in the conlang fashion. Let's use diminutive
and augmentative suffixes for the size and an attributive prefix for the styling.
An example of that looks a little like a snippet of Moby Dick:
5 red bespeck-fishies
The IXML grammar normalizes away a number of non-terminals, and turns the affixes
back into the standard values we had before:
The basic drawing unit is not just some things (such as 5 wee red fish) but a thing
at a particular location within some space. More generally it is a group of things
within a set of spaces arranged in some particular way. For example, we might say
"5 fish fill small box in spiral". The fish are the things, the box is the space,
and the spiral is the arrangement. Each of these is constructed and managed in its
own way. Arrangements are not rendered directly: only the locations associated with
them matter. Groups may or may not be rendered, depending on their characteristics.
A blue box would need to be rendered, but a box with no colour or other drawing characteristics
would not. A thing is always rendered. The arrangement is relative to the space. Here's
the thing: the same kind of item may operate as a thing, a space, or an arrangement.
How can we indicate and distinguish these roles?
Another way to think about this is: the major participants in a Drawing process are
a Group (of things), a Division (of spaces), and an Arrangement (of locations). Given
that case frame, how should we mark the case roles?
Word order
The first thing to try is basic word order: Group fill Division in Arrangement. For example:
5 wee circles fill large circle in standard circle
The IXML grammar fragment does the usual kinds of normalization we've seen before,
dropping extraneous information from the output:
This approach gives us something more like a natural English sentence. The normalization
of plural forms helps here as well. The input text reads easily and could be used
as the basis for the alternative text of the image directly. The presence of a verb
offers us the opportunity for different verbs to express important aspects of the
scene relationships. In the full grammar I use "fill" to indicate that the things
should be scaled to fit the space, whereas "cover" means to leave them be, and "inhabit"
means they should be clipped to the space.
On the other hand, this formulation is "natural" with strong limits on what can be
expressed, making it tricky to know where the boundaries are until we trip over them.
So it is naturalistic, but not really natural. Depending on the specific spaces and
arrangements used, the particular verb "fill" and the preposition "in" may be more
or less apt. For example "in" reads OK in "in standard circle", but "at" reads better
for "at top-left". Furthermore, once we get into more complex scenarios where we are
constructing divisions of spaces created from the points of the boundaries of things
and so on, "in" because an interesting preposition to use for other purposes.
Using case markers
In the conlang fashion, we consider some alternatives. Many languages indicate case
roles not with word order but with particles or inflections. Thus Latin "equus" (nominative)
versus "equum" (accusative) versus "equo" (dative) or Russian "работа" versus "работу"
versus "работе". Here I use no marking for the Group and prepositions as particles
for the Division and Arrangement.
5 wee circles large in-circle standard at-circle
The IXML grammar uses the particles to determine which participant is which and removes
the extraneous item element.
Notice that the grammar does not impose an order on the participants any more. We
could give them in any order. For example:
large in-circle 5 wee circles standard at-circle
The XML output is different, in that the participants are given in a different order,
but the interpreter doesn't care about that, so this amounts to the same thing:
The pluses and minuses of this formulation are the inverse of the word order approach.
It reads less naturally, even using English prepositions rather than Latin case endings
or something made up, but its artificiality makes it less likely to confuse. Free
word order seems like a boon, but in natural language it mainly helps by allowing
us to emphasize new or important information in conversation. That may apply to image
descriptions for a series of related works: this image is about using a large circle
as the space, and that image is about using a large square instead. It seems a fairly
tenuous benefit, especially given the artificiality of the expression.
Let's get agglutinative
Finally, let's just have a little fun pushing the envelope and taking a non-English
approach entirely, where both size and case role are represented in suffixes. Here's
the example:
5 circley circleadontown circledot
The suffix "y" means wee, "adon" means large, "town" marks the Division, and "dot"
marks the Arrangement. Standard size and Group are unmarked.
In this grammar the order is still free, and we normalize the suffixes:
This is playful and allows for the free order, but requires extra knowledge to understand,
even using suffixes that attempt to resonate with English speakers.
Open classes, closed classes
So far the grammar fragments have enumerated the allowable things, spaces, and so
on. The real more complex grammar leaves some of these open. A thing can be anything.
This is an open class. A simple space is also an open class, but started as a closed
class, with an enumeration of valid members:
The driver looked up all the kind attributes to load the appropriate components. As the language developed, and the
component bindings for more spaces were created, I shifted space to an open class.
The grammar uses the rename marker (working draft) to turn the space-kind attribute to kind. The lookup code stayed the same and any rules in the grammar using space-kind stayed the same:
I could have just changed space-kind to kind throughout the grammar, but I liked having the grammar rules be more specific in
this way.
Locations created a stronger challenge. Location is a closed class, complicated by
the presence of additional information for some kinds of locations and not others.
Specific locations such as "center" are unique. There can be only one. But random
locations could be multiple and accept a count. I wanted to simplify the representation
for the processing so that there would always be a location-kind attribute along with a count attribute where relevant. Renaming solves this:
Both the location kind and the count will appear as attributes on the location element,
and the naming of the kind will be consistent regardless of whether it is a countable
kind or a unique kind. The grammar properly constrains the use of the count.
A similar problem arises with drawing options. Rather than having a special attribute
for colour, and another for size, and then separate elements for other various options,
I wanted it all unified and consistently represented using the element option with a name attribute and then a type-based attribute as appropriate.
The grammar uses renaming extensively to normalize the names:
Although the size option is defined in terms of strings, it ends up being represented as an option
with a numerical value. A string like "wee red fish(styling="speckled")" produces a consistent set of options:
This simplifies passing drawing options to the maker functions.
Grammatical gender
Some things cover area. We could use them as clipping bounds or as spaces. We could
decorate the area they cover in various ways (crosshatching or speckling, for example).
Some things consist of strokes. They can be used as paths. We could draw them with
different pens (tapered strokes or dashed strokes, for example). They have no interior
to decorate. Some things may be able to be viewed in either light. Is a red circle
a circular red line or a circular red area?
It is the kind of distinction that grammatical gender can help with.
should produce a dashed red circular line, and that:
5 wee red dashed circles-zone
should produce a red circular area filled with dashes.
Conclusions and Summary
IXML can be used to normalize the parse tree for textual languages. That is, we can
get the same parse tree from radically different textual interfaces by using the normalization
capabilities of IXML, applying different grammars to produce the same results. That
enables evolution of the rules of textual interface language without having to modify
the underlying interpretation of what the textual interface is describing.
IXML accomplishes this normalization by providing controls for which non-terminals
to include as elements or attributes, and which non-terminals to exclude entirely.
Terminals can be elided or introduced. Finally, in the working draft at least, non-terminals
can be renamed for further normalization.
One approach to how to develop a textual API is to treat it as a constructed language,
or conlang, which means being mindful of the choices, looking further afield for the
true range of options, and remembering that even a computer language is a language
for humans. Computer languages and APIs may not be as complex and subtle as natural
languages, but applying a linguistic analysis can help us understand the moving parts
nevertheless.
We've seen various examples drawn from a textual interface created for exposing complex
drawing operations in a simplified way.
Applying the conlang approach helped me realize what the different kinds of participants
in the drawing were, which led to construction of maker functions that took this role
into account. It also led me to an understanding of the different gender classes of
things, which also led to augmentations of the construction functions.
[XQuery]
W3C: Jonathan Robie, Michael Dyck, Josh Spiegel, editors.
XQuery 3.1: An XML Query Language
Recommendation. W3C, 21 March 2017.
http://www.w3.org/TR/xquery-31/.
[1] Although the SVG specification says the operations are applied in the order provided,
that is not the case at all. The specification says:
<g transform="translate(-10,-20) scale(2) rotate(45) translate(5,10)">
<!-- graphics elements go here -->
</g>
is functionally equivalent to:
<g transform="translate(-10,-20)">
<g transform="scale(2)">
<g transform="rotate(45)">
<g transform="translate(5,10)">
<!-- graphics elements go here -->
</g>
</g>
</g>
</g>
But that means the first operation applied to the graphics elements is translate(5,10), nottranslate(-10,-20). So: right to left.
[2] I am tickled by the idea that there is an unwritten ordering rule here. m_pszThing is OK, but *pszm_Thing and *m_szpThing are not.
[3] The standard ordering is (at least by some accounts): opinion size age shape color
origin material purpose noun. Thre is good evidence this is even a linguistic universal,
see Scontras23.
W3C: Jonathan Robie, Michael Dyck, Josh Spiegel, editors.
XQuery 3.1: An XML Query Language
Recommendation. W3C, 21 March 2017.
http://www.w3.org/TR/xquery-31/.