A very insightful article, although I have frequently thought of more restrictive languages as more powerful: they allow you to say more about what’s happening, rather than just pulling the trigger and hoping. They’re more expressive of intent, with restrictions on what the intent is. There are always valid programs that aren’t expressible, but it seems more sensible to me to drop down into a less restrictive subset when necessary than just to write everything by the seat of your pants.
I tend to think power (to produce things) versus power (to analyze/consume) things as being in tension.
That’s true. The problem is that producing, in the grand scheme of things, is a tiny part of what we do. When it’s time to fix a bug, extend an existing feature, or integrate with an API, the ease and speed of those tasks depends on the power to analyze, rather than the power to produce.
There’s also a direct “multiple interpretations” angle. Something which is highly analyzable is reusable. For instance, not every partial function from Int to Int can be written as a (finite) table, so a function representation is more liberal for creators while the table implementation is more liberal for consumers since it enables both an interpretation as a function and, e.g., the ability to print the function out.
Hmm, but not nuanced enough I think.
It is almost obvious that sometimes more powerful is better, and sometimes less is better… I don’t think he looked closely enough at where the boundary is.
For example CSS is a classic example of a non-powerful W3 style data language… but do much with it for awhile and you start to reach for http://sass-lang.com
So there is some point, probably between classic CSS and SASS that is the optimum for a data language.
I tried doing “data driven” for awhile… and then realised what the data means tends to be loosely defined, and require (possibly) a parsing step, and certainly an interpretation step before you can really understand it.
If I had just don’t it in raw, say Ruby, creating concrete frozen instances of the classes would it be better? Yes.
If I had done it as a well normalized SQLite database would it have been better? Yes, I could query and manipulated it better.
In between? Hmm. I think it just adds “more stuff” to understand and get wrong.
There’s also the in between case, of a very simple language that embeds a powerful language. I thought the manifest example might be going there, but perhaps Makefiles are the canonical example (maybe django templates too, less certain). 90% or more of what make does is expressed in a simple declarative form. Turn these twenty C files into objects. I could build a replacement that parses the majority of OpenBSD Makefiles. But there’s always that one makefile that’s got 30 lines of shell embedded in it. This makes migrating very hard.
The rc start up scripts did make this transition. rc.conf was a script sourced by the shell and could contain arbitrary shell fragments. This is hard for tools to parse however, so it was announced that only var=value statements would be allowed. It worked out because not many people had taken advantage of the power presented.
I’ve experienced this in three places, two of them have worked well for me and one of them has been a disaster.
Makefiles (as you said) and daemontools both call out to something arbitrarily powerful. IME, this has not been a problem, because the semantics around when and how they have been called is well understood. The tool that calls these arbitrarily powerful scripts is, itself, not very overridable.
Chef, the configuration management tool, uses Ruby as it’s “DSL”. But in reality, this is just some classes that are imported into your chef code by default, so you are really just writing Ruby. This has been nothing but problems for me. People end up doing all sorts of things they shouldn’t because “it’s just Ruby” but at the same time Chef is overriding the evaluation of things (it’s thunking some things that look like they are executing now).
The lesson I’ve taken from this, is if someone wants to provide a more powerful language in a weaker one, it has to be able to provide a clear boundary of when that more powerful langauge can be executed and what context. Otherwise it’s just a lie.
[Comment removed by author]
So thinking in terms of DSLs allows me to refine my point. If one of the bullet points on the brochure for your DSL is “plus write arbitrary ruby/Python/Java code” then your DSL has failed to fully capture/express the domain.