Iron ChIF: Pilot Episode (Pacian vs. Draconis, using Dialog)

hello all! happy to be here. dialog is cool and fun to talk about and below you’ll find more-than-you-ever-wanted-to-know about it. hopefully this will be helpful in appreciating what the iron chefs are doing.

(a swell of intro music here…)

Dialog is a domain-specific language for writing IF. It, along with its infrastructure, were created by a very smart Swede named Linus Akesson (imagine a tiny ring over the ‘A’) and first released in 2018. One advantage of Dialog is, in fact, it’s newness. It hasn’t collected any cruft or barnacles as yet and offers programming conveniences that older languages don’t have (or don’t offer natively). Linus named Inform 7 and Prolog among his inspirations. Cosmetically, it does bear some resemblance to Inform 7 if you squint hard enough. Linus was big into making code readable with a clean syntax. Dialog is a good balance between readability and efficiency.

This…

(room #kitchen)
(name #kitchen)  kitchen
(look #kitchen) This is a clean white-tiled kitchen.
(from #kitchen go #south to #diningRoom)
(before [leave #kitchen #south]) Momma pleads "Please don't go, Bubba!"

…is pretty close to natural english if you ignore the parentheses and “scaffolding”.

Of course, you can’t ignore the parentheses because Dialog is a “logical language” (that’s the “inspired by Prolog part”). Dialog code is, at heart, built up from rules and conditions (also called queries) placed on those rules. Rules of the same logical form are called a predicate. When Dialog encounters a rule, it evaluates the conditions (if any) that apply and if all the rules are “true” then the rule “succeeds”.

(prevent [give #rareValuableBook to #jimmy])
	(#jimmy is #in #bathtub)

If the second line is true, then the rule succeeds, preventing the player from ruining the book. If he’s not in the tub, then the query fails meaning that the rule fails and is ignored, allowing the action.

Dialog uses $ as a wildcard or with a variable. This is extremely convenient. For example. if we want to know where Jimmy is we can query:

(#jimmy is #in $Container)

And afterward, $Container will be “bound” to the #bathtub (or wherever else Jimmy might be). But, more importantly, maybe we don’t care where Jimmy is - he could be in the library or standing on a table or sitting in a chair which is, itself, hanging from a hot-air balloon. But, wherever he is, maybe we want to put the apple in the exact same spot.

(#jimmy is $Relation $Container)
(now) (#apple is $Relation $Container)

This makes traversing the object tree trivial. The most powerful use, though, comes when we use these in a multi-query. If we precede a condition/query with a *, then Dialog will continue to call each matching query until it finds one that succeeds or until they all fail.

(find the frenchman)
	*(french $NPC)
	(male $NPC)
	"Sacre bleu, (name $NPC) is a frenchman."

(german #klaus)
(french #marie)
(female #marie)
(french #henri)
(male #henri)
(spanish #jose)
(french #helene)
(female #helene)

When this is called, Dialog sets up a “choice point” and looks for any rule that fits the first pattern (french x). Klaus and Jose are here but they’re not french so they’re not even queried. Marie matches, causing the first query to succeed. But then Marie isn’t male so the following query fails. But because it’s a multi-query, Dialog goes back to the choice point. This time it finds Henri who makes both queries succeed and successfully triggers our message.

This use of binding and multi-queries is the most powerful part of Dialog. It’s also the part of Dialog that is the hardest to grasp and to wrap ones head around since it’s very VERY different than, for example, C or Inform.

Also very different - Dialog “objects” aren’t objects in the usual sense. That is, they’re not self-contained collections of data and methods but are, instead, simply labels that rules and predicates operate on. So, there’s no such thing as programmatic “scope” or siloing of data. A rule is a rule is a rule no matter where it is. Each line of the #kitchen definition above could have been put in a separate “include” file, it wouldn’t matter. This can cause trouble if you’re not paying attention:

(touch #cactus)
	Ouch!
(touch $)
	It doesn't feel like anything in particular.
(touch #pudding)
	Ooh, gooey.

Because rules are queried in sequence, where a rule is placed is important. Touching the cactus will give the expected response. But touching the pudding will give the middle default response, not the gooey one we want (and so will touching anything else that happens to come after the generic (touch $) in our code).

Some other cool things:
1 - The standard library included with Dialog is excellent and very complete. And it is, itself, written in very clear Dialog code which means customizing the library for your own purposes is easy and even expected (generally, it’s not a good idea in most other languages to go mucking around in the library itself).

2 - The Dialog infrastructure is impressive. It compiles to z-code and the compiler is quite good. But Linus also created something called the A-machine (again with a tiny ring over the ‘A’) which is, sort of, the “Glulx of Dialog”. There’s a javascript interpreter for this which will let you use links and do interesting things with the status bar. There’s also a 6502 assembly/Commodore-64 interpreter that is surprisingly speedy.

3 - It has an awesome dynamic debugger that shows changes in your code on the fly as you make them without having to recompile and restart. This on its own is nearly worth the price of admission.

Some downsides:
1 - There’s no access to z-machine assembly and, if not compiling to the A-machine, support for windowing, statusbar tricks, links, etc. doesn’t exist. Even using the javascript interpreter, it’s difficult. Daniel Stelzer does amazing things with the status bars in his games that I don’t think can be fully appreciated if you haven’t tried it.

2 - Similarly, if you want all the bells and whistles of the A-machine in a stand-alone executable you’re out of luck.

3 - Debugging can sometimes be a pain. If a rule fails, it does so quietly and is simply ignored. So if you have a typo…

(if) (#bruce is #in #kitchenn) (then) "Howdy, Bruce!" (endif)

…it can be tough sometimes to track down. Even though #kitchenn doesn’t exist, this is a completely logical and valid rule that will simply fail every time. The compiler won’t complain and you’ll scratch your head a while wondering where Bruce is.

4 - Like most other languages that aren’t Adventuron, Inform/Punyinform, or ZIL, Dialog isn’t optimized for retro or 8-bit machines. Although smaller games will run surprisingly well using the C64 A-machine interpreter.

5 - There’s no dynamic object creation. Which can be an issue sometimes under very specific circumstances.

6 - I really wish it had a built-in parse_name() equivalent like Inform 6.

So, Dialog is awesome. Personally, I use it about half the time. If a game is small enough that I think it’ll run well on an 8-bit machine then I’ll happily use Inform/Punyinform (and could write an encomium to these as well). But a larger more complex project will almost always be easier and more efficient to put together with Dialog.

24 Likes