Apologies to those who were hoping for longer, more boring tutorials at this point. But wires helps us finish debunking two objections to making programming languages, and domain-specific languages in particular:
“It’s cumbersome and difficult.” Not at all. Racket has the capacity to make complex programming languages. But as we’ve seen in wires and the other tutorials so far, it can also make very simple DSLs. Because of this, Racket makes DSL-building a technique that’s practical for a wide range of programming problems—even little ones.
“Few problems are naturally suited to DSLs.” This objection mostly arises from a lack of creative perspective. Sure, not every problem demands a DSL. But with wires, we approached the problem by asking a simple but revealing question: what if we treated our puzzle input as code for a not-yet-existing language? It turns out that we can ask this question about a lot of possible inputs. (In the jsonic tutorial, we asked it about JSON.)
Moreover, have a look at some of the other approaches to solving this puzzle. Essentially they’re all doing the same work as wires, but with brutal inefficiency: first they break the input data into pieces (like our reader) and then assign meaning to those pieces (like our expander).
Why not use the right tool for the job? The pleasure of building languages with Racket is that we always have access to the deepest abstractions of the Racket language—including macros, lexical contexts, identifiers, and bindings. Therefore, we can often make tricky problems much simpler by mapping our problem directly onto these deep abstractions.
And if our programs end up shorter than everyone else’s—well, that’s a burden we can learn to live with.