Liking cljdoc? Tell your friends :D

05 - Composability and Builders

Core requirement

Everything should be properly decomposed so the end result is composable.

defnz is the user-friendly surface, but it must not be a monolith. It should be syntax over smaller reusable pieces.

form
  ↓
signature data
  ↓
normalized type data
  ↓
boundary spec
  ↓
generated source
  ↓
compiled artifact
  ↓
callable function

Why data matters

The signature is ordinary Clojure data:

[xs [:slice :const :f64]
 :ret :f64]

Because it is data, users can compose it with ordinary Clojure functions.

Type builders

Users should be able to write builders like:

(defn slice-of [t]
  [:slice t])

(defn const-slice-of [t]
  [:slice :const t])

(def bytes
  (const-slice-of :u8))

Then use them:

(defnz count-byte
  [input bytes
   needle :u8
   :ret :usize]
  "...")

The core API should accept this because it accepts data, not opaque type objects.

Macro generation

Users should be able to generate functions:

(defmacro defbinaryz [name op t]
  `(defnz ~name
     [x ~t
      y ~t
      :ret ~t]
     ~(str "return x " op " y;")))

Usage:

(defbinaryz add-f64 "+" :f64)
(defbinaryz mul-i64 "*" :i64)

Lower-level functions

The macro layer sits over data-level functions:

(zig/normalize-type [:slice :const :u8])
(zig/normalize-signature '[xs [:slice :const :u8] :ret :usize])
(zig/build-spec {...})
(zig/generate-source spec body)
(zig/compile! spec body)
(zig/load! artifact)
(zig/fn spec body)

Most users do not need these; library authors and macro authors do.

Destructuring as composition

Destructuring is a way to compose Clojure-shaped inputs with Zig-friendly native arguments.

(defnz distance
  [{x1 :x y1 :y} {:x :f64 :y :f64}
   {x2 :x y2 :y} {:x :f64 :y :f64}
   :ret :f64]
  "...")

This composes three ideas:

  • Clojure map-shaped inputs;
  • signature data describing field types;
  • generated native scalar arguments.

Public data should stay public

Avoid making users construct internal classes or opaque type objects for public contracts.

Good:

[:slice :const :u8]

Less good as the only public representation:

(zig.types/slice (zig.types/const zig.types/u8))

Helpers may exist, but the canonical representation should remain data.

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close