Blog › ICP guides
Kitten developer on retainer: stack effects, type system, concatenative programming, combinators, and Kitten stack-based functional programming on monthly retainer
September 26, 2026 · ~15 min read
A Kitten text processing pipeline was built around a series of word definitions that transformed input records. One word, process, had been declared with a stack effect of text -- text — consuming one text value and producing one text value. Inside the definition, the developer computed an intermediate length value using Kitten’s string length operation and pushed it onto the stack for potential reuse in a later step. That intermediate int was never consumed within the definition body; it remained on the stack after process completed. Callers of process expected a single text value on the stack after the call, but the stack contained both the intermediate int and the output text. Kitten’s type checker tracks stack effects statically: the declared effect text -- text required exactly one value to be on the stack after the word ran, but the implementation left two. Stack imbalance: 1 extra value per call. The developer restructured the definition in two ways: for cases where the length value was needed, a drop was added after the length computation to consume the intermediate before the final push; for cases where the length was not actually needed, the length computation was removed entirely. Stack imbalance: 1 extra value per call → 0. The Kitten developer on retainer diagnosed the stack effect mismatch: in a concatenative language, every word’s definition must produce exactly the stack effect declared in its type; pushing an intermediate value without consuming it violates the declared effect and accumulates phantom values that corrupt downstream word applications.
The work log entry read “fixed text processing pipeline stack errors, 7h.” It names the result and duration. It cannot explain why stack imbalance is particularly insidious in Kitten programs — in most programming languages, an unused intermediate computation is harmless (a dead variable that gets garbage collected); in a concatenative language, an unused intermediate is an unconsumed stack value that shifts the position of every value below it in the stack, causing every subsequent word to operate on the wrong inputs without any obvious error at the call site of the erroneous word. It cannot explain how to choose the right combinator for a given stack manipulation — dip applies a quotation to the second stack element while temporarily removing the top, which is correct when the computation needs to reach below the top value and then restore it; both applies the same quotation to a pair of values on the stack, which is correct when performing the same transformation on two values simultaneously; compose builds a new quotation by sequentially combining two quotations without executing them, which is correct when constructing a computation to be passed or stored; choosing the wrong combinator applies the computation to the wrong stack position, which is as invisible in source as a wrong-receiver call in an object-oriented language. It cannot explain Kitten’s IO effect type — functions that perform input or output must declare the +IO effect in their type signature; a pure function that calls print or read without +IO is a type error; the boundary between pure computation and effectful computation is statically enforced, which is the mechanism that makes Kitten programs testable without mocking. The 7 hours of stack effect audit, combinator selection, and effect annotation are invisible in the diff.
Kitten stack effects: type notation, composition rules, polymorphic +r, and higher-order function types
Kitten’s type system tracks the stack effect of every word as a first-class type. The notation a b -- c means the word expects a and b on the stack (with b on top) and leaves c on the stack. Stack effect composition is the rule that governs word sequencing: if word f has effect a -- b and word g has effect b -- c, their concatenation has effect a -- c. This composition rule is the type-checking engine for the whole program: the type checker verifies that each word in a sequence produces what the next word expects. A mismatch — where f produces b but g expects d — is a type error. The polymorphic stack effect variable +r represents “the rest of the stack.” A word with effect +r a -- +r b consumes an a value and leaves a b value, leaving everything below a on the stack unchanged. This is the standard form for most Kitten words that transform one value: the +r variable makes the word composable with any context, regardless of what other values happen to be on the stack below.
Higher-order words in Kitten take quotations as stack values. A quotation in Kitten is a deferred sequence of words enclosed in { } braces. The type of a higher-order word that takes a transformation quotation and applies it is something like +r a (a -- b) -- +r b: it expects a value of type a and a quotation that transforms a to b, and it produces a value of type b while leaving the rest of the stack unchanged. The call combinator invokes a quotation: { 1 + } call pushes the quotation and then executes it, equivalent to inline 1 +. The dip combinator takes a quotation and a value, temporarily removes the value, executes the quotation, and then restores the value: 10 20 { + } dip adds nothing to the second element while restoring the top 20. The compose combinator builds a new quotation by sequentially combining two quotations: { 1 + } { 2 * } compose produces a quotation equivalent to { 1 + 2 * }. Kitten is a statically typed concatenative language developed by Jon Purdy. Its closest retainer neighbors are Factor developer retainers (both are concatenative languages with stack effects) and Forth developer retainers (shared stack-based programming model), but Kitten’s static type system with declared stack effect types, algebraic data types with match/case, and +IO effect annotation make the retainer work distinct in type-driven stack correctness, algebraic type design, and effect boundary management.
Kitten algebraic types, IO effects, and combinator selection for correct stack access
Kitten algebraic types are defined with define type and variant constructors. A define type Shape { Circle (Float) | Rectangle (Float, Float) } defines a type with two variants, each carrying data. Pattern matching uses match and case: match { case Circle(r) { r r * pi * } case Rectangle(w, h) { w h * } }. The Kitten type checker requires exhaustive coverage: every variant must be handled; a missing case is a type error at the match expression. This exhaustiveness requirement is the mechanism that makes algebraic type design valuable in Kitten: adding a new variant to a type propagates type errors to every match expression that handles the type, making it impossible to accidentally add a case without updating all handlers. Algebraic type design in Kitten retainer work involves structuring data so that invariants are encoded in the type (using match to handle all cases) rather than in runtime conditional logic that could miss cases.
The IO effect type in Kitten separates pure computation from side-effectful operations. A word that calls print, println, say, or read must declare +IO in its type signature: define greet (text +IO -- +IO). A pure word that does not declare +IO cannot call IO-effectful words; attempting to do so is a type error. This effect tracking makes the boundary between pure and IO code explicit and statically verified. Retainer work involving +IO typically covers: adding missing effect annotations to words that call print or read, restructuring programs to move IO operations to the top level while keeping inner logic pure, and designing the data flow so that pure transformation words produce values that IO words consume separately. The combinator selection audit — verifying that dip is used to reach below the top stack value, both to apply uniformly to a pair, and dup/drop/swap for basic stack reordering — is the structural component of a Kitten retainer that directly parallels loop and conditional structure audits in conventional languages.
How HourTab tracks Kitten developer retainer hours
Kitten retainer work carries the invisible-hours problem specific to concatenative languages: the type error is often reported at the call site of the erroneous word rather than at the word definition itself, because the type checker detects the stack mismatch when the word is used in a composition context, not when it is defined in isolation. The stack imbalance pattern described above — a definition that pushes an intermediate value without consuming it — is the most common correctness issue in Kitten programs written by developers coming from conventional languages: they think of pushing a value and not using it as a dead-variable pattern that is harmless, when in a concatenative language it is a stack pollution pattern that corrupts all subsequent operations. Diagnosing this requires understanding Kitten’s stack effect type system, how the type checker computes the effect of a word composition, and how to read the type error to identify which word in the sequence produced a stack with the wrong shape. A retainer engagement typically involves stack effect audit (every word definition verified to match its declared effect), combinator audit (every dip/both/compose/call verified to select the correct stack position), and effect annotation audit (+IO verified on every word that calls print, read, or any other IO operation).
HourTab gives Kitten developers a public retainer-hours URL they send to clients — typically teams building data transformation pipelines in a concatenative style, language researchers exploring statically typed concatenative programming, and developers embedding Kitten as a scripting layer in applications where stack-based composition provides a composable query or configuration language. For Kitten retainers, each work log entry should name the mechanism (stack effect: a b -- c type notation, imbalance correction, +r polymorphic rest; combinator: call quotation invocation, dip second-element application, both pair application, compose quotation building, dup/drop/swap reorder; algebraic type: define type, variant constructor, match/case exhaustive coverage; IO effect: +IO annotation, print/read boundary), the specific word name, and the before/after stack imbalance count. Kitten retainers are often compared to Factor developer retainers for the shared concatenative stack-effect model, but Kitten’s static stack effect type system with declared types, algebraic data types with exhaustive match/case, and +IO effect type boundary make the retainer work distinct in type-driven stack correctness enforcement, algebraic type design, and pure-IO separation. HourTab’s work log makes the stack effect audit, combinator selection, and effect annotation work visible to clients who would otherwise see only the symptom — stack-related type errors at call sites — and not understand why the fix required knowing that in a concatenative language, pushing an intermediate value without consuming it is not a dead variable but a stack shape violation that shifts every downstream word’s inputs, and why the +r polymorphic variable is the mechanism that makes individual words composable regardless of what other values happen to be on the stack at the point of composition.
Track Kitten developer retainer hours without the status emails
HourTab gives Kitten developers a public URL per client retainer. One link, no login, live burn-down. Your clients stop asking “how many hours do I have left?” and your stack effect audit log — imbalance diagnosis, combinator selection, IO effect annotation — becomes the proof of value that gets the retainer renewed.
See HourTab pricing →FAQ: Kitten developer retainers
What does a Kitten developer on retainer typically do?
A Kitten developer on monthly retainer covers Kitten stack effects (function type a b -- c notation; stack effect composition; polymorphic +r rest variable; higher-order function types (a -- b); stack imbalance detection), Kitten types (int, float, bool, text primitives; optional ?T; list [T]; unit {}; algebraic types with define type and variants; match/case exhaustive pattern matching), and Kitten I/O and combinators (+IO effect type; print, println, say output; read input; call quotation invocation; dip second-element application; both pair application; dup/drop/swap stack reorder; compose quotation concatenation).
What Kitten work is most commonly underlogged in a retainer?
Stack imbalance diagnosis (word declared text -- text but definition pushed intermediate int without consuming it; stack imbalance: 1 extra value per call; added drop or restructured to eliminate intermediate; imbalance: 1 extra/call → 0; 6–10 hrs invisible); higher-order combinator selection (call vs dip vs both vs compose; dip for second-element application; both for pair transformation; compose for quotation building; wrong combinator applies to wrong stack position; 4–8 hrs invisible); algebraic type design (define type with variants; match/case exhaustive coverage; avoiding unnecessary nesting; 4–7 hrs invisible); +IO effect annotation (missing +IO on words that call print or read; pure-IO boundary restructuring; 3–6 hrs invisible).
What are typical Kitten developer retainer rates?
Entry-level Kitten developers (1–2 years, basic stack effect notation, primitive types, kitten workflow) bill at $55–$100/hr. Mid-level Kitten concatenative programmers (2–4 years, stack effect composition, algebraic type design with match/case, higher-order combinators, +IO effect management) bill at $90–$165/hr. Senior Kitten stack-based functional developers (4–8 years, polymorphic stack effects with +r, advanced quotation composition, large-scale concatenative architecture) bill at $130–$240/hr. Monthly retainer ranges: $2,000–$4,000/mo advisory (15–25 hrs), $5,500–$14,000/mo for full Kitten concatenative systems engineering.
What should a Kitten developer retainer agreement include?
A Kitten developer retainer agreement should specify: stack effect scope (a b -- c type notation; composition rules; +r polymorphic rest; higher-order (a -- b) types; imbalance detection and correction); type system scope (int/float/bool/text; optional ?T; list [T]; unit {}; define type algebraic types; match/case exhaustive); combinator scope (call, dip, both, dup, drop, swap, compose; correct combinator selection); IO effect scope (+IO annotation; print/println/say/read; pure-IO boundary); and hour logging format (stack category: effect type, imbalance correction, combinator selection; specific word name and before/after imbalance count).
How should Kitten developer retainer hours be logged?
Log each Kitten retainer session with: stack category (stack effect: a b -- c notation, imbalance diagnosis, +r polymorphic rest; combinator: call invocation, dip second-element, both pair, compose building, dup/drop/swap reorder; algebraic type: define type, variant constructor, match/case coverage; IO: +IO annotation, print/read boundary); the specific word name and before/after imbalance count (word: process; declared: text -- text; implementation pushed intermediate int without consuming; imbalance: 1 extra int/call; fix: added drop after length computation; imbalance: 1 extra/call → 0); and the before/after metric. Include whether fix required drop insertion, definition restructuring, combinator substitution, or +IO effect annotation addition.