Now that we’ve got a feel for the ingredients of a Racket-implemented language, let’s make a more useful one: jsonic. It looks like JSON. But it lets us insert Racket S-expressions wherever we like, which will be compiled to JSON.
JSON is a subset of JavaScript used for encoding structured data in plain text:
1 2 3 4 5 6 7 8 9 10 11 | [ null, 42, true, ["array", "of", "strings"], { "key-1": null, "key-2": false, "key-3": {"subkey": 21} } ] |
A JSON file contains one of six values: a null, a Boolean, a number, a string, an array, or an object. An array is a sequence of things, like a Racket list, that can contain other JSON values. An object is a set of key–value pairs, like a Racket hash table, where each key is a string, and each value is any JSON value. JSON structures can be nested to any depth (i.e., they are recursive).
Though JSON is derived from JavaScript, JSON itself is not a programming language. It’s just a way of encoding data. But if we have to write a lot of JSON files with repetitive features, we might start dreaming of a way to automate the drudgery. + Spoiler: JSON is essentially a nicer way of writing XML, which is a nastier way of writing S-expressions, so we’re traveling full circle.
One option: we could make a JSON-generator library using our favorite programming language. That wouldn’t be wrong. But it would also send us on a detour better avoided. After all, the major reason to use JSON is that it lets simple things be simple. Once we make our JSON library, then we have to write all our JSON from within a programming language, thereby losing the notational economy of JSON itself.
What’s the other option? We can go the opposite direction, and bring automation features into JSON. This hybrid would no longer be JSON—it would be a domain-specific language based on JSON. This is a great use case for a DSL: injecting new capability into a file type that doesn’t otherwise support it. + This is also one of the animating principles behind Pollen: to be able to add programmable features to any text file. This DSL is jsonic.
Though some think of DSLs as a heavy abstraction—isn’t it always more work to make a new language than to use an existing one?—that’s not necessarily true. (As we’ll see, jsonic requires even fewer lines of code than bf.) Compared to a code library, a DSL can provide a simpler, more focused interface, giving us access to the capabilities we need, and nothing else. In turn, the DSL can be easier to teach and share than a full-fledged library, because there’s less to describe and learn.
That’s where we’ll start: by describing the behavior of jsonic.