Skip to content

UX: StatusMap as IDeref container #13

Description

@SerhiiRI

Problem

execute returns a full status-map with internal structures, errors, stats, etc. But 99% of the time users only need the resolved instruction:

;; Current — verbose
(:instruction (commando/execute reg instruction))

;; Every single call site has this pattern

The status-map is valuable as a container (errors, patching, debug) but the default UX forces everyone to unwrap it.

Solution: defrecord + IDeref

Make status-map a record type that implements IDeref. @ returns :instruction on success, throws ExceptionInfo (with full status-map as ex-data) on failure.

;; Happy path — clean, 99% of use cases
@(execute reg instruction)
;; => {"1" 1, "2" 1, "3" 1}

;; Explicit error handling — full control
(let [r (execute reg instruction)]
  (if (ok? r)
    @r
    (handle-errors (:errors r))))

;; try/catch style
(try
  @(execute reg instruction)
  (catch #?(:clj ExceptionInfo :cljs ExceptionInfo) e
    (let [{:keys [errors status]} (ex-data e)]
      ...)))

;; Patching — pass the container, deref the final result
(let [r (execute reg instruction)]
  @(execute reg changed {:previous r}))

Implementation sketch

(defrecord StatusMap [status instruction errors warnings successes stats uuid]
  #?(:clj  clojure.lang.IDeref
     :cljs IDeref)
  (#?(:clj deref :cljs -deref) [this]
    (if (= status :failed)
      (throw (ex-info "Commando execution failed" (into {} this)))
      instruction)))

Why defrecord works here

  • assoc/update/get/destructuring — all work, extra keys (:internal/*) stored in overflow map
  • No dissoc on status-map fields anywhere in the pipeline (verified)
  • Record field access (:status, :instruction) is direct Java/JS field lookup — faster than hash-map
  • CLJS has IDeref protocol — cross-platform
  • (= record plain-map) breaks, but tests compare individual keys, not entire status-maps

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions