I recently re-read through Michael Arntzenius’ excellent list of Aphorisms on Programming Language Design, and a specific point caught my eye:
21. “Declarative” means you can use it without knowing what it’s doing.
All too often, it means you can’t tell what it’s doing, either.
It is, in hindsight, quite obvious – any knowledge of how a declaration is processed implies knowledge of the context that declaration will be used in, be it a configuration file, a SQL query, a programming language REPL, and so on. That is to say, context matters, but more importantly, knowledge of the context matters even more.
It seems to me that this maxim can be applied beyond what we think of as strictly “declarative programming languages”.
Though programming languages differ in their modes of expression, the act of writing in a programming language is, itself, always declarative. This, too, seems obvious in hindsight; regardless of whether you’re writing in C or SQL, you’re not telling the computer what to do, but rather how it should be done, or what the outcome should be. Furthermore, both of these exercises requires fore-knowledge of the context: in the former case, knowledge of the C language and libraries, in the latter, knowledge of SQL and, for some use-cases, how the query planner works.
Of course, things are made easier by the fact that the behaviour of both C and SQL is covered by specifications, and both operate in fairly standard ways regardless of which compiler or database engine is used. On the other hand, though one might readily understand the formatting rules of any INI, JSON, or YAML file, the same cannot be said about how these are used once parsed – it heavily depends on the context. When it comes to DSLs, all bets are off.
How are we, then, to build systems that minimize this sort of “external” knowledge, or that otherwise make the least amount of assumptions in defining what is explicit and what is implicit?
A few things come to mind:
Use prior art or knowledge. One can leap-frog years of experience-building by utilizing pre-existing patterns, especially where these don’t form core competencies or differentiators.
Don’t override the meaning of existing conventions, especially in isolation. Different approaches to design should look (and feel) different.
Make things that look similar also behave in similar ways; a symbol used to mean one thing in a specific context should ideally not be re-used for different semantics in a different context.
This only seems to confirm another of Michael’s Aphorisms:
19. Syntax is a pain in the ass.