This post is about the language I am working on as a part of this environment. For the sake of this post, let’s call the language ITCH. Hopefully I can expose some of the assumptions underlying the system and open them up for discussion.
The language is intended to support these two goals:
For the model of complexity, I am imagining certain types of games.
As genre examples, you could take interactive fiction (see also), roguelikes, card games like Magic, or puzzle games like jelly no puzzle, SSR.
These games tend to be interesting because of their interacting parts: many local actors and places whose combinations produce surprising game events.
a language that lets us build up a game world piece by piece
an interpreter that lets us analyze a moment in time by working with the chains of events that led up to it
When reasoning about an artificial situation, I tend to “start from somewhere.” I start with an empty model and extend it by recognizing consequences.
Recognizing a consequence first requires recognizing basic facts: objects that can be true of, or present in, the situation.
A consequence is of the form “when some condition (defined in terms of basic facts) is true, or present, then make true, or make present, certain new facts.”
When a set of consequences fail to predict the behavior of the system, we extend it. We may need to change an existing rule or add new rules to make the model more accurate. This may require recognizing new classes of facts.
Programming languages based on Datalog build systems in this way. We interpret consequences (now called rules) operationally; they denote atomic behaviors. A program accepts input facts, computes applications of rules, and produces output facts. This is the basic model used with ITCH.
When the system itself is defined using facts and consequences, then the models constructed by our informal style of reasoning actually become immediately formalizable.
We often call facts tuples and consequences rules. We divide tuples into sets called relations.
For our game applications, we might normally use an object oriented language. This paradigm divides functionality up into objects, places them together in a world, and lets them interact by message passing or procedure calls.
I consider ITCH to be basically object oriented, by this definition.
For our classes, we write sets of rules.
Instead of sending a message to an object (to be processed by its most specific superclass) we assert a tuple (to be processed by the most specific applicable rule).
Many object-oriented systems make these two operational assumptions:
Rule programming deprioritizes this view of things; our object is more like a hook which causes the rules of the game to change in some local, temporary way. Objects allow rules to trigger. Messages can refer to specific objects, but don’t have to.
An object has no canonical boundary with the world. If there is one, then the world can carry on even if the object is excised.
Rule sets can define individual object behaviors, but they can also flexibly define interactions between objects.
A rule of a game frequently refers to multiple objects.
For instance, the implementation of a single card’s behavior tends to look like a consequence, as defined earlier: when a certain formation of game conditions is present (“a player has an empty hand”, “a piece lands on a square owned by another player”), draw some conclusion (“the game is over”, “the player attempts to pay the other a certain amount”).
Puzzle games usually have very simple physics: some rules that describe how a configuration of elements move after the player performs an action. Puzzles are constructed from configurations of token objects that follow these rules.
Localizing these behaviors at a query, rather than a particular object, is natural.
This style has a more flexible notion of identity.
Rather than judging that an instance is nominally a member of some class, we can only say that it matches some query.
The most basic queries are single predicates; for instance
This pattern might form the basis for all our banana behaviors.
We can recognize a familiar subtype by extending the query:
banana x, overripe x
A formation of tuples, viewed as attributes, thus may form an instance of some “query class”, while a different collection of tuples referring to the same values may be recognized as a different instance by some other query.
The system can be extended with new recognizers, unconstrained by a class hierarchy. When extended with an effect, a recognizer becomes a rule, and it can interpose at any level of the system’s behavior.
Rules allow us to abstract:
banana x, overripe x => overripe-banana x overripe-banana _ => 1:ripe-banana-count
and add new behavior orthogonally to the rest of the application:
n:ripe-banana-count, n > 2 => make-banana-bread?
Our hope is to write applications that treat their users as aspiring programmers. Our model of a programmer is that they
Our view of the average programming situation, as it relates to the three activities above:
In other words, the processes of building models and relating observed or modelled behavior to source code is mostly intuitive, and not supported by tools.
Providing source code is a very coarse way of supporting a student. Applications are nominally defined by their source, but compiled programs do not generally reflect the structure of their source. Reading the source is like reading a manual.
Debuggers are traditionally used to fix bugs. Their usefulness to people learning the relation between some behavior and its source code is not emphasized.
Our model of the future of the programming situation:
Treat every application like a domain specific language.
Expert users have developed a command of the grammar of their application. They have sophisticated mental models of the objects at hand. These models may go beyond the application designer’s understanding.
This expertise should form the basis of a language for asking. The knowledge kept by an interpreter is about how the definitions in the program relate to computed values. A responsible interpreter can answer certain questions about this knowledge:
Their answers must be local: an answer to “who computed this value?” is a program that computes that value and nothing more. The source can be less like a book and more like a tutor. A tutor teaches by providing mind-sized answers and nudges in the right direction.
Answers to questions such as these should help the user leverage their expertise in the domain to develop expertise in the inner logic of the application. Although the application-DSL may not be extensible, its implementation in ITCH is extensible. The language is a substrate for constructing new experiences that prioritize learning, trust, and modification.