The Ansible playbooks are YAML lists. There's no particular reason that:
---
# This playbook deploys the whole application stack in this site
- name: apply common configuration to all nodes
hosts: all
remote_user: root
roles:
- common
…
is more readable than:
(playbook
"This playbook deploys the whole application stack in this site"
(play
"apply common configuration to all nodes"
(hosts all)
(remote-user root)
(roles common)
…)
or:
(playbook '((all '(common) :user root :comment "apply common configuration to all nodes") …)
:comment "This playbook deploys the whole application stack in this site")
In fact, I'd argue that both Lispy representations are much more readable.
Oh, I didn't mean to say that S-expressions can't be used for configuration. It's that in general you don't want a Turing-complete language to do configuration management, because you want to be able to reason about things like rollbacks, dependencies and diffs.
Yeah, except that inevitably one does end up wanting some element of Turing-compleness, hence the Jinja templates used in Saltstack & Ansible.
In a S-expression-based configuration language, one would either embed an S-expression-based programming language, or generate the S-expressions with a programming language which can manipulate S-expressions.
I don't think that it's that easy to get away from needing Turing-completeness in general. No reason you can't still support rollbacks, dependencies and diffs anyway.
I think this is something along the lines of what a friend once told me: "Compare the grammar of Java and C++. C++ has a very complex definition, whereas Java is brain-dead simple. And that fact enables all the powerful transformations IDE can do."
Also there's this school of thought that attributes most security problems to people accidentally using Turing-complete languages where they meant to use something less powerful. Consider vulnerability to arbitrary code execution through user input injection, which could be interpreted by your program being a "parser" (so-called "shotgun parser" - it's implicitly distributed throughout your code base) for a Turing-complete superset of what was supposed to be a list of accepted inputs. There are pretty good talks about this line of thought and I personally find it pretty interesting.
But none of this affects the fact that in complicated enough programs, you need "configuration" to be more code than data, which leads people not knowing of Lisp to reinvent a subset of it in XML or JSON or something similar.
Probably because nobody called it 'configuration management' then. It's a very good tool from the job - because eventually all configuration formats end up getting Turing-complete and totally unreadable. Why not just start with something that supports code = data out of the box?
It can work but it works best when the language is kept dumb.
A dumb declarative language is easier to understand and easier to maintain. It can be used to help maintain a strict separation of concerns.
The smarter you try to make the language, the worse it becomes. Ant & MSBuild AFAIK are basically full programming languages in their own right so there was really no point to them actually being their own language.
The real lost art here is scripting languages. Even if you parse things yourself, configuration files still have a natural tendency to evolve into a crappy programming language over time. So instead of writing your own config file format you should just make a couple a quick bindings for a scripting language like Lua, Python or Ruby.