nw-fp-clojure
This Claude Code skill provides Clojure-specific patterns for functional programming, emphasizing data-centric domain modeling with spec for runtime validation, REPL-driven development workflows, and practical patterns like threading macros and generators. Use this skill when building data-intensive applications, prototyping with evolving schemas, or exploring solutions interactively, and avoid it for teams requiring compile-time type safety or those unfamiliar with Lisp syntax.
git clone --depth 1 https://github.com/nWave-ai/nWave /tmp/nw-fp-clojure && cp -r /tmp/nw-fp-clojure/nWave/skills/nw-fp-clojure ~/.claude/skills/nw-fp-clojureSKILL.md
# FP in Clojure -- Functional Software Crafter Skill
Cross-references: [fp-principles](../nw-fp-principles/SKILL.md) | [fp-domain-modeling](../nw-fp-domain-modeling/SKILL.md)
## When to Choose Clojure
- Best for: data-centric domains | REPL-driven exploration | rapid prototyping | frequently evolving data shapes
- Not ideal for: teams wanting compile-time type safety | Android | teams unfamiliar with Lisp syntax
## [STARTER] Quick Setup
```bash
# Install Clojure CLI
brew install clojure/tools/clojure # macOS
# Create project
mkdir -p order-service/src/order_service order-service/test/order_service
# Run REPL: clj | Run tests: clj -X:test
```
**Namespace caveat**: Clojure namespaces use `-` but filenames use `_` (JVM requirement). `order-service.core` lives in `order_service/core.clj`.
## [STARTER] Domain Modeling with Spec
Clojure is dynamically typed. Domain modeling uses maps with qualified keywords, validated at runtime.
```clojure
(require '[clojure.spec.alpha :as s])
;; Domain wrappers as specs
(s/def ::order-id pos-int?)
(s/def ::email (s/and string? #(clojure.string/includes? % "@")))
(s/def ::customer-name (s/and string? #(> (count %) 0)))
;; Record types as spec'd maps
(s/def ::customer (s/keys :req [::customer-name ::email] :opt [::phone]))
;; Choice types as spec alternatives
(s/def ::payment-method
(s/or :credit-card (s/keys :req [::card-number ::expiry-date])
:bank-transfer (s/keys :req [::account-number])
:cash #{:cash}))
```
### [STARTER] Validated Construction
```clojure
(defn make-email [raw-email]
(if (s/valid? ::email raw-email)
{:ok raw-email}
{:error (str "Invalid email: " raw-email)}))
```
### [INTERMEDIATE] Auto-Generated Test Data from Specs
```clojure
(require '[clojure.spec.gen.alpha :as gen]
'[clojure.spec.test.alpha :as stest])
(gen/sample (s/gen ::customer) 5) ;; random valid customers
;; Auto-test functions against their specs
(s/fdef validate-order
:args (s/cat :raw-order ::raw-order)
:ret (s/or :ok ::validated-order :error ::validation-error))
(stest/check `validate-order)
```
Define specs, get generators and function tests for free.
## [INTERMEDIATE] Composition Style
### Threading Macros
```clojure
;; Thread-first (data through first arg) / Thread-last (for collections)
(-> raw-order validate-order price-order confirm-order)
(->> orders (filter active?) (map :customer-name) (sort))
;; comp composes right-to-left; partial for partial application
(def process-order (comp confirm-order price-order validate-order))
```
### Error-Track Pipeline (Convention-Based)
```clojure
(defn bind-result [result f]
(if (:ok result) (f (:ok result)) result))
(defn place-order [raw-order]
(-> {:ok raw-order}
(bind-result validate-order)
(bind-result price-order)
(bind-result confirm-order)))
```
No stdlib Result/Either -- by convention using `{:ok v}` / `{:error r}` maps.
## [INTERMEDIATE] Effect Management
Side effects managed by convention and architecture, not the type system.
### Pure Core / Imperative Shell
```clojure
;; Pure domain logic (no I/O, no state)
(defn calculate-discount [order]
(if (> (count (:lines order)) 10) {:rate 0.1} {:rate 0.0}))
;; Imperative shell (I/O at edges)
(defn place-order-handler! [deps raw-order]
(let [result (-> raw-order
validate-order
(bind-result (partial price-order (:get-price deps)))
(bind-result confirm-order))]
(when (:ok result)
((:save-order! deps) (:ok result)))
result))
```
### [ADVANCED] Hexagonal Architecture with Functions
```clojure
;; Functions as dependencies (idiomatic Clojure)
(defn make-place-order-handler [find-order-fn save-order-fn!]
(fn [raw-order]
(let [result (validate-and-price raw-order)]
(when (:ok result) (save-order-fn! (:ok result)))
result)))
;; Composition root
(def handler
(make-place-order-handler
(partial find-order-in-db datasource)
(partial save-order-in-db! datasource)))
```
For lifecycle management, use Component, Integrant, or Mount:
```clojure
(require '[integrant.core :as ig])
(defmethod ig/init-key ::order-repo [_ {:keys [datasource]}]
(->PostgresOrderRepo datasource))
```
## [INTERMEDIATE] Testing
**Frameworks**: clojure.test (built-in) | test.check (PBT) | Kaocha (test runner).
### Property Test with test.check
```clojure
(require '[clojure.test.check.clojure-test :refer [defspec]]
'[clojure.test.check.generators :as gen]
'[clojure.test.check.properties :as prop])
(defspec serialization-round-trips 100
(prop/for-all [order (s/gen ::order)]
(= order (deserialize (serialize order)))))
```
### Custom Generator
```clojure
(def gen-valid-email
(gen/fmap (fn [[user domain]] (str user "@" domain ".com"))
(gen/tuple
(gen/such-that not-empty (gen/string-alphanumeric))
(gen/such-that not-empty (gen/string-alphanumeric)))))
```
## [ADVANCED] Idiomatic Patterns
### Data-First Domain Modeling
```clojure
(def order
{::order-id 42
::customer {::customer-name "Alice" ::email "alice@example.com"}
::lines [{::product-code "W-1234" ::quantity 10 ::price 25.0}]
::status :validated})
```
"Data is better than types." Domain models are maps. Validation happens at boundaries.
### Multimethods for State Machines
```clojure
(defmulti handle-command (fn [state _command] (:status state)))
(defmethod handle-command :empty [_state {:keys [item]}]
{:status :active :lines [item]})
(defmethod handle-command :active [state {:keys [action] :as command}]
(case action
:add-item (update state :lines conj (:item command))
:pay (assoc state :status :paid)
state))
(defmethod handle-command :paid [state _command]
state)
```
## Maturity and Adoption
- **Dynamic typing trade-offs**: No compile-time type checking; typos in keywords are silent until runtime. Spec instrumentation helps but is opt-in.
- **Spec limitations**: Powerful for data valReview dimensions for validating agent quality - template compliance, safety, testing, and priority validation
Review dimensions for validating agent quality - template compliance, safety, testing, and priority validation
Review dimensions for acceptance test quality - happy path bias, GWT compliance, business language purity, coverage completeness, walking skeleton user-centricity, priority validation, observable behavior assertions, traceability coverage, and walking skeleton boundary proof
Detailed 5-phase workflow for creating agents - from requirements analysis through validation and iterative refinement
5-layer testing approach for agent validation including adversarial testing, security validation, and prompt injection resistance
Architectural style selection decision matrices, trade-off analysis, structural enforcement rules, and combination patterns. Load when choosing or evaluating architecture styles.
Comprehensive architecture patterns, methodologies, quality frameworks, and evaluation methods for solution architects. Load when designing system architecture or selecting patterns.
Canonical AT completeness gate — research-anchored 7-category taxonomy (C1-C7) + 15-item mechanical checklist. Paradigm-neutral. Drives acceptance-designer reviewer verdict deterministically.