It would be interesting to see a DSL parser in Go and Rust as open source as these languages are becoming popular for distributed computing trends in cloud computing, especially with microservices. If I had the skills of compiler designer, I may be bold to implement it.
Could HashiCorp HCL (https://github.com/hashicorp/hcl) be used to represent promises? They use it for their Terraform product (https://www.terraform.io/), which is a CM but strictly for RESTful API, like AWS or GCE.
The theory is explained such that these actors make their promises and coordinate amongst themselves. It is differentiated from obligation in that the actors supposedly can't be 'told' what to do. However, even though such systems are written in a declarative style these are still effectively 'instructions' and not 'requests'. The actor really doesn't have a choice of whether to execute them or not. Unless, of course, the particular node doesn't have a relevant operation to support the request.
Furthermore, it seems to me, that the distinction between imperative and declarative is fuzzy. Yes, we write our configuration in declarative language but ultimately the agent on the node implements our declarative requests using predefined imperative routines. The actors have no true intelligence other than being written specifically for a particular platform and executing routines that are relevant to those platforms. The declarative style is, therefore, simply an abstraction that prevents us from having to write instructions toward a specific architecture.
Probably the use of agent-based solutions lends itself to better scaling but I feel that this trying to fit the operation of it all into the fancy formalized box with specialized dialect just over-complicates things for the average administrator.
Everything is imperative ultimately because it runs as machine language, which are byte opcodes running step-by-step imperatively. Declarative is well suited to change config as it abstracts complexity system specific implementation, also it clearly communicates desired behavior to a broader audience. That is the concept.
All popular change configurations use declarative with exception of Chef, which uses a pseudo-DSL in Ruby. I like declaritive as it creates more readible/portable code, where pure ruby for example, invites complexity, as the change config becomes optional at this point, e.g. file resource from change config API vs file API in ruby.
As for CFEngine, while the concept is sound, the implementation may be another story. In otherwords, maybe it is not the most intuitive implementation of declarative.
In practice (reality), a little of both is needed. Puppet added a lot of imperative pieces to their DSL making more complex and specialized, defeating original design of puppet IMHO. They may have had no choice. Chef maybe has and advantage in this regard as they started with imperative, and promote community style guides and best practice to encourage readibility and portability.
Obligation vs Promise
================
This is a broad topic, that applies to other systems beyond change config, such as service discovery or JavaScript API mechanism. Typically it is implemented with an agent on the client.
For, traditional change config, where change is applied at runtime, agent system is optimal to have client meet its promise. The agent can converge to a desired state, and thus have a limited self-healing capability.
For change applied at build or deploy time, such as immutable production with Docker, complex centralized change config become less necessary. Push configuration system (obligation) become more optimal, e.g. Ansible.
In immutable production scenarios with Docker, obligation is very popular, either rolling your own custom one with Ansible or other tool, or using an orchestrator like Mesos/Marathon (DC/OS), Kubernetes, or Swarm. However, there is a point where promise theory may become more optimal, likely around 10,000+ system. Habitat is one solution that uses promise theory for containers; they call it choreography vs orchestration (obligation).
So, promise is optimal for runtime config changes, obligation is optimal for < 10K for immutable prod (build time or deploy time config changes) , promise optimal for > 10K+ w. Immutable.
There has been the real world self-healing and auto-scaling archectures using custom or orchestration (obligation) system like Kubernetes. So this is not far fetched. Other tools like service discovery (zookeeper, etcd, consul) along may play a role to coordinate live active state. This would then be a mix or promise and obligation.
True self-healing cannot happen easily with current batch of centralized change config tools, as they may not have the current source of truth, for it is stored or queried from a intemediary source.
How all of this turns out in practice, is in a state of flux, only community interests and marketplace demands will determine how this plays out.