Turmeric Style Guide

Status: Living document -- updated as idioms are established. Last Updated: 2026-05-27


Function Arity

Hard parameter limit

MAX_FN_ARITY is 16. Functions with more than ~5 positional parameters are a code smell; 16 is an emergency escape hatch, not a target.

More than 5 params -- reach for defstruct

When a function needs many named, independent inputs, pack them into a struct and pass a single options value:

(defstruct CsvOpts
  [delim       :int   ;; field separator (e.g. 44 = ',')
   quote       :int   ;; quote char (e.g. 34 = '"')
   has-header  :int   ;; 1 = first row is header
   infer-rows  :int   ;; rows to sample for type inference
   null-str    :cstr  ;; string that represents NULL (e.g. "")
  ])

(defn read-csv [src :cstr opts :CsvOpts] :int
  ...)

Default values via partial application (Haskell-style idiom)

With currying, locking in a default options value is a one-liner:

(def default-csv-opts (CsvOpts 44 34 1 100 ""))

;; read-csv-fast is a closure with opts baked in.
;; Call it with just the filename.
(def read-csv-fast (read-csv default-csv-opts))

(read-csv-fast "data.csv")

(read-csv default-csv-opts) returns a closure (fn [src :cstr] :int ...). No macro magic, no keyword arguments -- just currying.

Genuine variadic interfaces -- use & rest :type

When a function takes an unknown number of values of the same type (println, format, aggregation column lists, etc.), use a variadic rest parameter:

(defn println-all [first :cstr & rest :cstr] :void
  (println first)
  ;; rest is a cons-list of :cstr; walk it with head/tail helpers
  ...)

(println-all "hello")              ;; rest = nil (0)
(println-all "a" "b" "c")         ;; rest = cons("b", cons("c", 0))

Rules for & rest

Cons-list manipulation in #{Unsafe} code

The rest parameter is an int64_t holding a pointer to a linked list of __tur_cons_cell { int64_t head; int64_t tail; } cells, or 0 (nil). Inline-C helpers that walk it:

(defn cons-list-sum [lst :int] #{Unsafe} :int
  ```c
  typedef struct { int64_t head; int64_t tail; } __tur_cons_cell;
  int64_t acc = 0;
  __tur_cons_cell *p = (__tur_cons_cell *)(intptr_t)lst;
  while (p) { acc += p->head; p = (__tur_cons_cell *)(intptr_t)p->tail; }
  return acc;
  ```)

Or use a pure tail-recursive helper:

(defn cons-head [lst :int] #{Unsafe} :int
  ```c
  typedef struct { int64_t head; int64_t tail; } __tur_cons_cell;
  __tur_cons_cell *p = (__tur_cons_cell *)(intptr_t)lst;
  return p ? p->head : 0;
  ```)

(defn cons-tail [lst :int] #{Unsafe} :int
  ```c
  typedef struct { int64_t head; int64_t tail; } __tur_cons_cell;
  __tur_cons_cell *p = (__tur_cons_cell *)(intptr_t)lst;
  return p ? p->tail : 0;
  ```)

(defn list-sum-acc [lst :int acc :int] #{Unsafe} :int
  (if (= lst 0)
    acc
    (list-sum-acc (cons-tail lst) (+ acc (cons-head lst)))))

Performance note

Each variadic call with k rest args allocates k cons cells on the heap. Variadics are for convenience calls, not hot paths. If you're calling a variadic function in a tight loop, consider refactoring to a fixed-arity helper.

Quick decision guide

Situation Reach for
>5 named, independent params defstruct options value
Default values + currying defstruct + (def fast (f defaults))
Unknown number of same-type values & rest :type variadic
Recursive accumulator threading context closure-capture for context; fixed-arity for changing args
Genuinely >16 params Something is wrong -- split the function

Naming Conventions


Docstring Standard (;;;)

See the full standard in CLAUDE.md.

Quick reference:

;;; fn-name -- brief one-line summary.
;;;
;;; Parameters:
;;;   param -- description
;;;
;;; Returns:
;;;   description of return value
;;;
;;; Example:
;;;   (fn-name arg)  ; => expected result
;;;
;;; Since: Phase B1
(defn fn-name [param :int] :int
  ...)

Indentation

Follow Clojure-style indentation -- see CLAUDE.md for the full rules.

Key points:


Inline-C Style

;; Good:
(defn file-size [f] :int
  ```c
  return (int)ftell((FILE*)f);
  ```)

;; Bad (closing ``` on its own line breaks Markdown fences):
(defn file-size [f] :int
  ```c
  return (int)ftell((FILE*)f);
  ```
)