Skip to content
Back to skills

Nw Fp Clojure

ASecurity

Clojure language-specific patterns, data-first modeling, REPL-driven development, and spec

  • 615 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 27, 2026
data-aigojavakotlinshellbashexpresstestingapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add nWave-ai/nWave --skill nw-fp-clojure --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Nw Fp Clojure?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Nw Fp Clojure
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/nwave-ai-nw-fp-clojure/badge)](https://www.skillsdirectory.com/skills/nwave-ai-nw-fp-clojure)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: nw-fp-clojure
agent: nw-functional-software-crafter
description: Clojure language-specific patterns, data-first modeling, REPL-driven development, and spec
user-invocable: false
disable-model-invocation: true
---

# 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 validation but lacks expressiveness of static type systems for complex invariants. Spec 2 in development for years.
- **Discipline needed for test coverage**: Without compiler catching type errors, comprehensive tests are essential.
- **Smaller ecosystem**: Fewer libraries than Java/Kotlin. Good JVM interop compensates but Java interop code is verbose.

## Common Pitfalls

1. **Silent nil propagation**: Missing key returns `nil`, propagates silently. Use `some->` and explicit nil checks at boundaries.
2. **Learning curve for non-Lispers**: Parenthesis-heavy syntax is a barrier. Invest in Paredit/Parinfer and REPL workflow first.
3. **Spec verbosity**: Large spec definitions become unwieldy. Extract into focused namespaces and use `s/merge`.
4. **Convention-based error handling**: Without stdlib Result, teams use inconsistent patterns. Standardize on `{:ok v}` / `{:error r}` early.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.