-
Notifications
You must be signed in to change notification settings - Fork 2
Differences with Integrant
-
Distinction between system and component maps
In Integrant a system map is a flat, single level, map with prototype keys as entries:
;; integrant config {:ns/key {:a 1} :ns2/key2 {:b (ig/ref :ns/key)}}
In Commix system and component configurations follow same rules. System config maps can be freely reused within other systems.
-
Integrant confounds prototype keys with names of instantiated components
The entry
:ns/key
above specifies both the prototype key (providing the behavior) and the name of the "instantiated" component (providing a way to name and reference components within the system map).When you have several components inheriting from same prototype keys you need to recur to composite keys.
;; integrant config {[:ns/key :com1] {:a 1} [:ns/key :com2] {:b (ig/ref :com1)}}
In Commix you would write
{:com1 (cx/com :nskey {:a 1}) :com2 (cx/com :nskey {:b (cx/ref :com1)})}
-
Provision for grouped components
The following is not possible in integrant:
{:coms {:A (cx/com :ns/key) :B (cx/com :ns/key)} :com1 (cx/com :ns2/key2 {:coms (cx/ref :coms)})}
-
Provision for nested components
Integrant enforces flat systems. Even with small systems this often makes visual inspection and understanding dependencies hard.
Often a component
:A
depends on another component:B
but no other component depends on:B
. In integrant you have to write{:ns-a/A {,,,} :ns-b/B {:a (ig/ref :ns-a/A)}}
In Commix you can write it directly as a nested component:
{:B (cx/com :ns-b/B {:a (cx/com :ns-a/A)})}
Besides being a bit more parsimonious Commix syntax emphasizes dependency relationship and makes it clear that component
[:B :a]
cannot be accessed by other components in the system map except of:B
. -
Provision for modules or plugable configurations
Currently there is no way in Integrant to plug in an external configuration. In Commix it is as natural as adding a component to the map.
(def sys {:par 0 :A (cx/com :ns/key {:a (cx/ref :par)}) :B (cx/com :ns2/key2)}) (def system {:sub-sys1 (cx/com sys {:par 1}) :sub-sys2 (cx/com sys {:par 2})})
-
Distinction between return and no-return methods
In integrant some methods are named with
!
(halt-key!
,suspend-key!
). Bang!
is there to emphasize that the return value of a method is not used to update the system map. Arguably this is a somewhat confusing and non-idiomatic semantics.In Commix values returned from all methods are automatically assoced into system map allowing for idiomatic clojure pipelines which allow but don't enforce side-efectfull components.
-
Provision for side-efectufull systems
Integrant was designed for side efectfull components only. All Integrant's methods are suposed to operate on original system map. For instance return values of
halt!
orsuspend!
never end up into the system map.In Commix each action returns a modified system map which can be passed to the next action in the pipeline:
(-> config (init) (suspend [[:some :path] [:other :path]]) (resume) (halt))
-
Uniform life-cycle system
Integrant's life-cycle system doesn't quite follow a coherent pattern. For instance
init
takes a config and returns a system by running a build process which at some stage involves runninginit-key
on components.suspend!
andhalt!
take in systems and their return values are suposed to be discarded.resume!
takes in two system maps!In Commix config is part of the life-cycle - "config" == "uninitialized system" and
init
is no more special than any other life-cycle action. Every life-cycle action takes in a system and returns a system. Every life-cycle action maps directly to life-cycle key method and writing custom life-cycle methods is close to trivial. -
Supervision of the life-cycle order
In Integrant it's up to the user to ensure that methods are either fully idempotent or what they run in correct order. For instance, it's up to you to run
(halt! sys [:key1 :key2])
and then ensure that nextinit
runs on keys[:key1 :key2]
and their dependencies but no other keys.In Commix there is an enforced order of actions. For instance, you can run init on uninitialized or halted component but not on suspended one.
-
IDE Completion of Parameters
Because Integrant's config are plain maps, it would be hard to design completion systems for IDEs. In Commix, as components are declared with cx/com markers, it is straightforward to add support for completion which would pick argument names from
init-key
method. -
Derived keys
By design Integrant relies on derived composite keys to create unique names within the system. In Commix this is not necessary because of the clear separation between component's names within the system and component's prototype key.
Still, derived keywords are often useful to inherit behavior from a set of keys. Commix has no provision for inline specification of composite or derived keys. Such derivation has a global scope and doesn't belong to system's configuration. You will have to do that outside the system.
-
Life-cycle methods parameters TODO:
-
Distributed vs centralized configuration TODO: