| name | toolsmith |
| description | Patterns for implementing Toolsmith library functions. Covers pragmatic internal approach using raw operators with exception documentation, monadic behavior patterns (Result/Validation/plain value), predicate special rules, and the distinction between building Toolsmith vs consuming it. Use when implementing functions FOR the Toolsmith library itself. |
Toolsmith Library Implementation
Core Principle
The Toolsmith library provides semantic functions for consuming code, but uses pragmatic implementation internally.
Toolsmith exports functions like not(), isEmpty(), and isEqual() that consuming code MUST use instead of raw operators. But internally, Toolsmith implementations CAN use raw operators (!, &&, ===, .length, etc.) with documented exceptions. This is pragmatic: we provide the abstraction boundary, we don't re-abstract ourselves internally.
The Key Distinction:
- Consuming code (applications using Toolsmith): MUST use semantic functions, NO raw operators
- Toolsmith internals (building Toolsmith itself): CAN use raw operators with exception documentation
When to Use This Skill
Use this skill when:
- Implementing functions FOR the Toolsmith library (in
libraries/toolsmith/src/) - Writing predicates (type guards, boolean checks)
- Creating array/object/string/math utilities
- Building monadic functions (Result, Validation)
- Understanding Toolsmith's internal architecture
- Distinguishing internal implementation from external API
DO NOT use this skill when:
- Writing application code that CONSUMES Toolsmith
- Implementing functions in OTHER libraries (warden, steward, sentinel, etc.)
- For consuming code, use the
function-implementationskill instead
This skill is for library authors building Toolsmith. If you're consuming Toolsmith, use function-implementation skill.
Philosophical Context
The Pragmatic Boundary
External API (Consuming Code Perspective):
- MUST use Toolsmith functions (
not,isEmpty,isEqual,and,or) - MUST NOT use raw operators directly (
!,===,&&,||,.length) - Follows strict constitutional rules
- Benefits from monadic error handling
- Gets semantic, readable code
Internal Implementation (Toolsmith Library Perspective):
- CAN use raw operators (
!,===,&&,||,.length, etc.) - CAN use native methods (
.map,.filter,.reduce, etc.) - MUST document exceptions with
//++ [EXCEPTION]comments - MUST provide semantic public API
- MUST remain pure (no mutations, no side effects except IO boundaries)
Why This Approach:
Performance: Native operators are heavily optimized by JavaScript engines
===is a single CPU instructionisEqual(a)(b)requires function call overhead (2 calls due to currying), stack frames, parameter passing- For code executed millions of times, this matters
Readability: Operators are universally understood
Array.isArray(value) && value.length === 0is immediately clear to any JavaScript developerand(isArray(value))(isEqual(0)(length(value)))requires mental parsing of nested curried calls- Lower cognitive load for library maintainers
Simplicity: Toolsmith IMPLEMENTS the abstractions; it doesn't consume them
- We're the abstraction layer, not the consumers
- No need to wrap what we're wrapping
Encapsulation: The consumer never sees internal implementation
- Internal use of
&&is encapsulated behindand()function - Consumers see:
and(condition1)(condition2) - They don't care if internally we use
&& - The abstraction boundary is the function interface, not the implementation
- Internal use of
Type Safety: Predicates need raw access for TypeScript type narrowing
value._tag === "Ok"enables discriminated union narrowing- Type guards require access to internals
Performance vs Abstraction Trade-off
For Toolsmith internals, prioritize:
- Performance (use native operators)
- Readability (use familiar syntax)
- Maintainability (clear, simple code)
For Toolsmith PUBLIC API, provide:
- Semantic functions (not, and, or, isEmpty, isEqual)
- Monadic behavior (Result/Validation handling)
- Type safety (branded types, type guards)
The balance: Use pragmatic implementation internally to provide powerful abstractions externally.
Constitutional Rules That STILL Apply
Even in Toolsmith internals, these rules are MANDATORY and NON-NEGOTIABLE:
1. No Classes ✅ MANDATORY
// ❌ FORBIDDEN - even in Toolsmith
class Validator {
validate() { }
}
// ✅ CORRECT - use modules with functions
export default function validate(value: unknown) {
// ...
}
2. No Mutations ✅ MANDATORY
// ❌ FORBIDDEN - mutations not allowed
const result = []
array.forEach(item => result.push(item))
let count = 0
count++
// ✅ CORRECT - immutable data
const result = array.map(transform)
const count = array.length
Exception: IO boundaries (state management, localStorage) with //++ [IO] comment
3. No Loops ✅ MANDATORY (with generator exception)
// ❌ FORBIDDEN - no loops
for (let i = 0; i < array.length; i++) {
process(array[i])
}
while (condition) {
doSomething()
}
// ✅ CORRECT - use map/filter/reduce
array.map(process)
// ✅ EXCEPTION - generators may use loops
export default function* range(start: number) {
return function* rangeFrom(end: number): Generator<number> {
//++ [EXCEPTION] Using let and for loop in generator for performance
//++ Generators are allowed to use imperative iteration internally
//++ They produce immutable sequences externally
for (let i = start; i <= end; i++) {
yield i
}
}
}
4. Named Functions Only ✅ MANDATORY
// ❌ FORBIDDEN - arrow functions
const add = (a: number, b: number) => a + b
// ✅ CORRECT - named function declarations
export default function add(augend: number) {
return function addToAugend(addend: number): number {
return augend + addend
}
}
// ✅ EXCEPTION - arrow syntax OK in type signatures only
type Mapper<T, U> = (value: T) => U
5. One Function Per File ✅ MANDATORY
// ❌ FORBIDDEN - multiple exports
export function add(a, b) { }
export function subtract(a, b) { }
// ✅ CORRECT - one export per file
// File: add/index.ts
export default function add(augend: number) { }
// File: subtract/index.ts
export default function subtract(minuend: number) { }
6. Curried When Multi-Parameter ✅ MANDATORY
// ❌ FORBIDDEN - multiple parameters (unless uncurried callback for native method)
export default function add(a: number, b: number): number {
return a + b
}
// ✅ CORRECT - curried
export default function add(augend: number) {
return function addToAugend(addend: number): number {
return augend + addend
}
}
// ✅ EXCEPTION - uncurried callbacks passed to native methods
// When delegating to .map, .filter, .reduce, we accept uncurried callbacks
// to avoid curry/uncurry overhead
export default function map<T, U>(f: (arg: T, index?: number) => U) {
// Accept uncurried callback because we delegate to native .map(f)
return function mapWithFunction(array: ReadonlyArray<T>): ReadonlyArray<U> {
return array.map(f) // Native method expects uncurried callback
}
}
7. Pure Functions ✅ MANDATORY (except IO boundaries)
// ❌ FORBIDDEN - side effects
let cache = {}
export default function memoize(value: number) {
cache[value] = compute(value) // Mutating external state
return cache[value]
}
// ✅ CORRECT - pure function
export default function compute(value: number): number {
return value * 2
}
// ✅ EXCEPTION - IO boundaries with //++ [IO] comment
export default function persistToLocalStorage(key: string) {
return function persistWithKey(value: string): Result<Error, void> {
//++ [IO] This function performs localStorage side effects
try {
localStorage.setItem(key, value)
return ok(undefined)
} catch (error) {
return error(error as Error)
}
}
}
These rules apply everywhere in Toolsmith, always, with documented exceptions only.
Constitutional Rules That DON'T Apply Internally
These rules apply to CONSUMING code but NOT to Toolsmith internals:
1. Raw Operators Permitted ✅ ALLOWED
CAN use:
!for negation&&,||for logic===,!==for equality>,<,>=,<=for comparison.lengthfor length checkstypeoffor type checkinginstanceoffor instance checkingArray.isArray()for array checkingObject.is()for SameValue comparisonObject.keys(),Object.values(),Object.entries()for object operations
MUST document:
//++ [EXCEPTION] reason for using raw operator
//++ What external code should use instead
implementation
2. Native Methods Permitted ✅ ALLOWED
CAN delegate to:
.map(),.filter(),.reduce(),.find(),.some(),.every().slice(),.concat(),.join(),.split().keys(),.values(),.entries().toUpperCase(),.toLowerCase(),.trim()
MUST document:
//++ [EXCEPTION] Using native .map for internal implementation
//++ External code uses our map() function from Toolsmith
return array.map(fn)
3. Object Property Access Permitted ✅ ALLOWED
CAN access properties when implementing predicates or type guards:
export default function isOk<T>(value: unknown): value is Ok<T> {
//++ [EXCEPTION] Accessing ._tag for discriminated union narrowing
//++ This is the standard pattern for type guards
return isObject(value) && "_tag" in value && value._tag === "Ok"
}
Exception Documentation Pattern
Required Format
Every use of raw operators or native methods MUST be documented:
//++ [EXCEPTION] What rule is being violated and why
//++ What consuming code should use instead (if applicable)
//++ Any additional context (performance, type narrowing, etc.)
implementation using raw operator/method
Examples by Category
Logical Operators
//++ [EXCEPTION] This is the ONLY permitted use of the ! operator
//++ Everywhere else, use this `not` function instead
return !value
//++ [EXCEPTION] Using && for internal logic
//++ External code uses and() or allPass()
return Boolean(left) && Boolean(right)
//++ [EXCEPTION] Using || for internal fallback
//++ External code uses or() or anyPass()
return value || defaultValue
Comparison Operators
//++ [EXCEPTION] Using === for internal comparison
//++ External code uses isEqual() or is()
return a === b
//++ [EXCEPTION] Using > for numeric comparison
//++ External code uses isGreaterThan()
return a > b
Property Access
//++ [EXCEPTION] Using .length for array length check
//++ External code uses isEmpty() or isNotEmpty()
return array.length === 0
//++ [EXCEPTION] Accessing ._tag for type narrowing
//++ This is the standard discriminated union pattern
return value._tag === "Ok"
//++ [EXCEPTION] Using typeof for primitive type check
//++ External code uses isString(), isNumber(), etc.
return typeof value === "string"
Native Methods
//++ [EXCEPTION] Using native .filter() to implement our filter() function
//++ External code uses our filter() function from Toolsmith
return array.filter(predicate)
//++ [EXCEPTION] Using Array.isArray for type guard
//++ External code uses our isArray() function
return Array.isArray(value)
//++ [EXCEPTION] Using native .map() for internal helper
//++ External code uses our map() function, not .map
//++ This is a private helper implementing the public API
return array.map(f)
Type Checking
//++ [EXCEPTION] Using typeof for primitive type checking
//++ This is a primitive type guard with no higher-level abstraction
return typeof value === "string"
//++ [EXCEPTION] Using instanceof for class checking
//++ External code uses appropriate type guards
return value instanceof Error
Patterns
Pattern 1: Predicates (Type Guards and Boolean Functions)
Purpose: Predicates have special status - they can use raw operators internally
When: Creating any function returning boolean or performing TypeScript type narrowing
Characteristics:
- Use raw operators for checks
- Document exceptions
- Type predicate syntax for narrowing (
value is Type) - No need to use Toolsmith functions internally (we ARE the Toolsmith functions)
Structure:
export default function isArray<T = unknown>(value: unknown): value is ReadonlyArray<T> {
//++ [EXCEPTION] Using native Array.isArray for type narrowing
//++ External code uses this isArray() function instead
return Array.isArray(value)
}
Examples:
// Simple type guard
export default function isString(value: unknown): value is string {
//++ [EXCEPTION] Using typeof for primitive type check
//++ External code uses this isString() function
return typeof value === "string"
}
// Discriminated union type guard
export default function isOk<T>(value: unknown): value is Ok<T> {
//++ [EXCEPTION] Accessing ._tag for discriminated union narrowing
//++ Standard pattern for type guards
return isObject(value) && "_tag" in value && value._tag === "Ok"
}
// Boolean check (not a type guard)
export default function isEmpty<T>(array: ReadonlyArray<T>): boolean {
//++ [EXCEPTION] Using .length for emptiness check
//++ External code uses this isEmpty() function instead
return isArray(array) && array.length === 0
}
Key Points:
- Predicates return plain
boolean(not wrapped in Result/Validation) - Can use
===,typeof,.length, native methods freely - Must still document exceptions
- Used extensively even within Toolsmith for type narrowing
Pattern 2: Monadic Behavior (Overloaded Signatures)
Purpose: Functions that handle plain values AND monads
When: Creating utilities that work with Result/Validation/plain values
Characteristics:
- Multiple overloaded signatures for each input/output combination
- Implementation handles all cases with type guards
- Fallthrough behavior: pass errors unchanged
- Use Toolsmith predicates for type narrowing even internally
Structure:
export default function map<T, U>(f: (arg: T, index?: number) => U) {
//++ [OVERLOAD] Plain array → plain array
function mapWithFunction(array: ReadonlyArray<T>): ReadonlyArray<U>
//++ [OVERLOAD] Result monad → Result monad (fail-fast)
function mapWithFunction(
array: Result<ValidationError, ReadonlyArray<T>>
): Result<ValidationError, ReadonlyArray<U>>
//++ [OVERLOAD] Validation monad → Validation monad (error accumulation)
function mapWithFunction(
array: Validation<ValidationError, ReadonlyArray<T>>
): Validation<ValidationError, ReadonlyArray<U>>
//++ Implementation handles all overload cases
function mapWithFunction(
array:
| ReadonlyArray<T>
| Result<ValidationError, ReadonlyArray<T>>
| Validation<ValidationError, ReadonlyArray<T>>
):
| ReadonlyArray<U>
| Result<ValidationError, ReadonlyArray<U>>
| Validation<ValidationError, ReadonlyArray<U>> {
// Use Toolsmith predicates for type narrowing even internally
if (isArray<T>(array)) {
return _mapArray(f)(array)
}
if (isOk<ReadonlyArray<T>>(array)) {
return chainResults(_mapToResult(f))(array)
}
if (isSuccess<ReadonlyArray<T>>(array)) {
return chainValidations(_mapToValidation(f))(array)
}
// Fallback: pass through unchanged (handles error/failure states)
return array
}
return mapWithFunction
}
Key Points:
- Overload signatures declare all supported input/output combinations
- Implementation signature is most general (union of all)
- Use Toolsmith type guards (
isArray,isOk,isSuccess) to narrow and dispatch - Delegate to private helpers for each path
- Fallthrough behavior: pass errors unchanged (important for monadic composition)
When to Use Each Monad:
- Plain value: Direct computation, no error handling
- Result monad: Fail-fast error handling (first error stops processing)
- Validation monad: Error accumulation (collect all errors before failing)
Pattern 3: Private Helpers with Raw Operators
Purpose: Internal implementation details can use raw operators
When: Extracting logic into private helper functions (prefixed with _)
Structure:
// In map/_mapArray/index.ts
export default function _mapArray<T, U>(f: (arg: T, index?: number) => U) {
return function _mapArrayWithFunction(array: ReadonlyArray<T>): ReadonlyArray<U> {
// Use Toolsmith predicates for validation even in helpers
if (and(isFunction(f))(isArray(array))) {
//++ [EXCEPTION] Using native .map for internal helper
//++ External code uses our map() function, not .map
//++ This is a private helper implementing the public API
return array.map(f)
}
// Fallback: return unchanged
return array as unknown as ReadonlyArray<U>
}
}
Key Points:
- Private helpers (
_name) also use raw operators with documentation - Must still document exceptions
- Implement pieces of public API
- Follow same currying patterns as public functions
- Use Toolsmith predicates for validation even internally
Pattern 4: Logical Operations
Purpose: Implement logical functions like not, and, or
When: Creating fundamental logic utilities
Structure:
// not: Unary logical operation
export default function not(value: Value): boolean {
//++ [EXCEPTION] This is the ONLY permitted use of the ! operator
//++ Everywhere else, use this `not` function instead
return !value
}
// and: Binary curried logical operation
export default function and(left: Value) {
return function andWithLeft(right: Value): boolean {
//++ [EXCEPTION] Using && for internal logic
//++ External code uses this and() function or allPass()
//++ Convert both to boolean first for guaranteed boolean return
return Boolean(left) && Boolean(right)
}
}
// or: Binary curried logical operation
export default function or(left: Value) {
return function orWithLeft(right: Value): boolean {
//++ [EXCEPTION] Using || for internal logic
//++ External code uses this or() function or anyPass()
//++ Convert both to boolean first for guaranteed boolean return
return Boolean(left) || Boolean(right)
}
}
Key Points:
- These ARE the operator substitutes, so they MUST use the raw operators
- Document that external code uses THESE functions instead of operators
- Ensure boolean return (use
Boolean()conversion if needed) - These are the ONLY places
!,&&,||are allowed
Pattern 5: Ternary Curried Functions
Purpose: Functions that take three parameters (curried as three nested functions)
When: Functions like reduce that need function, initial value, and array
Structure:
//++ Reduces array to a single value using a reducer function
export default function reduce<T, U>(fn: (accumulator: U, item: T) => U) {
return function reduceWithFunction(initialValue: U) {
//++ [OVERLOAD] Array reducer: takes array, returns reduced value
function reduceWithFunctionAndInitialValue(array: ReadonlyArray<T>): U
//++ [OVERLOAD] Result reducer: takes and returns Result monad (fail fast)
function reduceWithFunctionAndInitialValue(
array: Result<ValidationError, ReadonlyArray<T>>,
): Result<ValidationError, U>
//++ [OVERLOAD] Validation reducer: takes and returns Validation monad (accumulator)
function reduceWithFunctionAndInitialValue(
array: Validation<ValidationError, ReadonlyArray<T>>,
): Validation<ValidationError, U>
//++ Implementation of the full curried function
function reduceWithFunctionAndInitialValue(
array:
| ReadonlyArray<T>
| Result<ValidationError, ReadonlyArray<T>>
| Validation<ValidationError, ReadonlyArray<T>>,
): U | Result<ValidationError, U> | Validation<ValidationError, U> {
// Use Toolsmith predicates for type narrowing
if (isArray<T>(array)) {
return _reduceArray(fn)(initialValue)(array)
}
if (isOk<ReadonlyArray<T>>(array)) {
return chainResults(_reduceToResult(fn)(initialValue))(array)
}
if (isSuccess<ReadonlyArray<T>>(array)) {
return chainValidations(_reduceToValidation(fn)(initialValue))(array)
}
// Fallback: pass through unchanged
return array
}
return reduceWithFunctionAndInitialValue
}
}
Key Points:
- Three levels of currying:
reduce(fn)(initialValue)(array) - Each level returns a function
- Monadic overloads still apply
- Follow same pattern as binary functions but with extra nesting
Pattern 6: Delegating to Native Methods
Purpose: Implement Toolsmith function by delegating to native JavaScript method
When: Wrapping native array/string/object methods to provide semantic API
Structure:
export default function _reduceArray<T, U>(
fn: (accumulator: U, item: T) => U,
) {
return function _reduceArrayWithFunction(initial: U) {
return function _reduceArrayWithFunctionAndInitial(
array: ReadonlyArray<T>,
): U {
// Validate inputs using Toolsmith predicates
if (and(isFunction(fn))(isArray<T>(array))) {
//++ [EXCEPTION] Using native .reduce for performance
//++ This is the ONLY place .reduce should be used
//++ Everywhere else, use the `reduce` function instead
return array.reduce(fn, initial)
}
// Fallback: return initial value unchanged
return initial
}
}
}
Key Points:
- Validate inputs with Toolsmith predicates
- Delegate to native method with exception comment
- Provide fallback behavior for invalid inputs
- This pattern applies to:
.map,.filter,.reduce,.find,.some,.every, etc.
Pattern 7: Type Guards for Discriminated Unions
Purpose: Check discriminated union tag to narrow types
When: Creating type guards for Result/Validation/Either/Maybe monads
Structure:
export default function isOk<T = unknown>(
value: unknown,
): value is Ok<T> {
//++ [EXCEPTION] Uses === operator and property access to check discriminated union tag
//++ This is a primitive type guard operation with no higher-level abstraction available
return isObject(value) && "_tag" in value && value._tag === "Ok"
}
Key Points:
- Check if value is object first (using Toolsmith
isObject) - Check if property exists with
inoperator - Check tag value with
=== - Return
value is Typefor type narrowing - Enables exhaustive checking and type safety
Common Tags:
- Result:
"Ok"|"Error" - Validation:
"Success"|"Failure" - Either:
"Right"|"Left" - Maybe:
"Just"|"Nothing"
Pattern 8: Generator Functions (Special Case)
Purpose: Generators may use let/loops internally for performance
When: Creating lazy iterators or sequence generators
Structure:
export default function* range(start: number) {
return function* rangeFrom(end: number): Generator<number> {
//++ [EXCEPTION] Using let and for loop in generator for performance
//++ Generators are allowed to use imperative iteration internally
//++ They produce immutable sequences externally
for (let i = start; i <= end; i++) {
yield i
}
}
}
Key Points:
- Generators are special case for constitutional rules
- Can use
letand loops internally - Must yield immutable values
- Must document exception
- External interface remains functional
Using Toolsmith Functions Internally
Important: Even in Toolsmith library code, use Toolsmith predicates and functions strategically.
When to Use Toolsmith Functions Internally
YES - Use Toolsmith function when:
Type narrowing with predicates
if (isArray<T>(value)) { // TypeScript knows value is ReadonlyArray<T> }Discriminated union narrowing
if (isOk<T>(result)) { // TypeScript knows result is Ok<T> }Complex validation
if (and(isFunction(fn))(isArray(array))) { // Both checks in one expression }Avoiding repetition (DRY)
// ✅ Use Toolsmith function if (isEmpty(array)) { return defaultValue } // ❌ Don't repeat the logic if (Array.isArray(array) && array.length === 0) { return defaultValue }Need monadic behavior
// Use Toolsmith map() for monadic behavior return chainResults(_mapToResult(f))(result)
When to Use Raw Operators Internally
YES - Use raw operator when:
Simple primitive operations
return a === b return x > 0 return value + 1Implementing the Toolsmith function itself
// In not/index.ts return !value // This IS the not() implementationPerformance-critical paths
return array.map(f) // Native method is fastestInside predicates
return typeof value === "string" // Type checkingProperty access for type narrowing
return value._tag === "Ok" // Discriminated union check
Common Violations
❌ Never Forget Exception Comments
Wrong:
export default function not(value: Value): boolean {
return !value // ❌ Missing exception comment!
}
Correct:
export default function not(value: Value): boolean {
//++ [EXCEPTION] This is the ONLY permitted use of the ! operator
//++ Everywhere else, use this `not` function instead
return !value
}
❌ Never Use Raw Operators in Consuming Code
Wrong (in application using Toolsmith):
// ❌ These are all wrong in consuming code:
if (!value) { } // Use not(value)
if (array.length === 0) { } // Use isEmpty(array)
if (a === b) { } // Use isEqual(a)(b)
if (condition1 && condition2) { } // Use and(condition1)(condition2)
Correct (in application):
// ✅ Use Toolsmith functions:
if (not(value)) { }
if (isEmpty(array)) { }
if (isEqual(a)(b)) { }
if (and(condition1)(condition2)) { }
❌ Never Skip Currying
Wrong:
// ❌ Multiple parameters not allowed
export default function is(a: unknown, b: unknown): boolean {
return Object.is(a, b)
}
Correct:
// ✅ Curried properly
export default function is<T>(a: T) {
return function isSameAs<U>(b: U): boolean {
//++ [EXCEPTION] Using Object.is internally
//++ External code uses this is() function instead
return Object.is(a, b)
}
}
Exception: Uncurried callbacks passed to native methods are acceptable:
// ✅ Accept uncurried callback for .map
export default function map<T, U>(f: (arg: T, index?: number) => U) {
// f is uncurried because native .map expects uncurried callback
return function mapWithFunction(array: ReadonlyArray<T>) {
return array.map(f) // Native method expects (item, index) => result
}
}
❌ Never Violate Still-Mandatory Rules
These are STILL forbidden even in Toolsmith:
// ❌ WRONG - classes forbidden
class Validator {
validate(value: unknown) { }
}
// ❌ WRONG - mutations forbidden
const result = []
array.forEach(item => result.push(item))
// ❌ WRONG - loops forbidden (except generators)
for (let i = 0; i < array.length; i++) {
process(array[i])
}
// ❌ WRONG - arrow functions forbidden (except type sigs)
const add = (a: number, b: number) => a + b
❌ Never Confuse Internal vs External Rules
Wrong thinking:
- "If Toolsmith can use
===, then consuming code can too" ❌ - "Exception comments are optional" ❌
- "I can use loops in Toolsmith helpers" ❌ (only generators)
Correct thinking:
- "Toolsmith CAN use
===internally, consuming code MUST useisEqual()" ✅ - "Exception comments are mandatory for all violations" ✅
- "Only generators can use loops, with exception comments" ✅
Implementation Checklist
When implementing a Toolsmith library function, verify ALL of these:
1. Structure
- Curried if multi-parameter (one parameter per function level)
- Named function declaration (not arrow function)
- One function per file (
index.ts) - Private helpers in
_subfolderdirectories - Follows file-system-organization skill patterns
2. Operators and Methods
- Raw operators documented with
//++ [EXCEPTION]comment - Exception comment explains what consuming code uses
- Native methods documented with exception comment
- Property access documented if violating abstraction
3. Monadic Behavior (if applicable)
- Overload signature for plain value
- Overload signature for Result monad (if applicable)
- Overload signature for Validation monad (if applicable)
- Implementation handles all cases with type guards
- Fallthrough behavior for errors (pass through unchanged)
4. Still Forbidden (Never Allowed)
- No classes anywhere
- No mutations anywhere (except IO boundaries with comment)
- No loops anywhere (except generators with comment)
- No arrow functions (except type signatures)
- No side effects (except IO boundaries with comment)
5. Documentation
- Envoy comments (
//++) for function purpose - Exception comments (
//++ [EXCEPTION]) on all violations - Clear explanation of what consuming code should use
- Overload comments (
//++ [OVERLOAD]) for monadic functions - Implementation comments for complex logic
6. Type Safety
- Proper TypeScript types for all parameters and returns
- Type predicates (
value is Type) for type guards - Generic types where appropriate
- Readonly for arrays and objects
7. Testing (Critical)
- Test file exists (
index.test.ts) - Tests for plain value path
- Tests for Result monad path (if applicable)
- Tests for Validation monad path (if applicable)
- Tests for edge cases and error conditions
Examples
See examples/ directory for complete implementations demonstrating all patterns:
Predicates:
predicates/isArray/index.ts- Type guard usingArray.isArray()predicates/isEmpty/index.ts- Boolean check using.length === 0
Logic:
logic/not/index.ts- Using!operator (canonical exception example)logic/and/index.ts- Using&&operator (binary curried logic)
Array/Map Pattern (Binary Curried with Monadic Overloads):
array/map/index.ts- Main function with Result/Validation/plain overloadsarray/map/_mapArray/index.ts- Delegates to native.map()array/map/_mapToResult/index.ts- Maps inside Result monadarray/map/_mapToValidation/index.ts- Maps inside Validation monad
Array/Reduce Pattern (Ternary Curried with Monadic Overloads):
array/reduce/index.ts- Main function with three-level curryingarray/reduce/_reduceArray/index.ts- Delegates to native.reduce()array/reduce/_reduceToResult/index.ts- Reduces inside Result monadarray/reduce/_reduceToValidation/index.ts- Reduces inside Validation monad
Type Guards:
monads/result/isOk/index.ts- Discriminated union guard using._tag === "Ok"
Each example demonstrates:
- Proper exception documentation
- Currying patterns (unary, binary, ternary)
- Monadic behavior (where applicable)
- Helper delegation
- Type narrowing with predicates
Cross-References
This Skill References
Core Structure and Patterns:
- function-implementation - Base structure, currying, parameter order, when to return Result vs plain values
- file-system-organization - Helper placement (
_helpers), modularity, one function per file - naming - Function and parameter naming conventions, conjunction selection
Type and Predicate Patterns:
- sitebender-predicates - Predicate-specific patterns, type guards, higher-order predicates
- type-definition - Type guards, branded types, discriminated unions, smart constructors
- error-handling - Result/Validation monad usage, error type design, error composition
Additional Context:
- abbreviations - For naming consistency (no abbreviations unless whitelisted)
This Skill CONTRASTS With
operator-substitutions - Shows what consuming code MUST use
- Key difference: Toolsmith IMPLEMENTS these operator substitutes; it doesn't use them internally
- Consuming code: MUST use
not(),and(),isEqual(),isEmpty()from operator-substitutions - Toolsmith internals: CAN use
!,&&,===,.lengthwith exception documentation
This Skill Is Referenced By
- Internal Toolsmith development documentation
- Advanced function implementation (when extending Toolsmith with new domains)
- NOT by consuming code skills (function-implementation, component, etc.)
Clear Separation of Concerns
Use function-implementation skill when:
- Writing application code
- Implementing functions in other libraries (warden, steward, sentinel)
- Consuming Toolsmith functions
- Following strict constitutional rules
Use toolsmith skill when:
- Implementing functions FOR Toolsmith library itself
- Understanding internal implementation philosophy
- Writing predicates, array operations, monadic functions
- Need pragmatic performance optimizations
Summary
Key Takeaways
The Pragmatic Boundary
- Toolsmith CAN use raw operators internally
- Consuming code MUST use Toolsmith functions
- The abstraction boundary is the public API, not the implementation
Exception Documentation Is Mandatory
- Every raw operator must have
//++ [EXCEPTION]comment - Explain why the exception is needed
- Explain what consuming code should use instead
- Every raw operator must have
Still-Mandatory Rules
- No classes, no mutations, no loops (except generators), no arrow functions
- These apply everywhere in Toolsmith, always
Monadic Behavior Pattern
- Overload signatures for plain/Result/Validation
- Type guards narrow and dispatch to helpers
- Fallthrough behavior for errors
Use Toolsmith Functions Strategically
- Use predicates for type narrowing even internally
- Use raw operators for simple operations
- Balance readability, performance, and maintainability
Predicates Have Special Status
- Can use raw operators freely (with documentation)
- Return plain boolean (not wrapped in Result)
- Used throughout Toolsmith for type narrowing
The Philosophy
Internal: Performance and Pragmatism
- Use native operators and methods
- Optimize for speed and simplicity
- Document all exceptions
External: Semantic and Safe
- Provide semantic functions (
not,isEmpty,isEqual) - Monadic error handling (Result/Validation)
- Type-safe interfaces
The Balance:
- We implement the abstractions, we don't consume them
- Use pragmatic implementation to provide powerful API
- Exception documentation ensures maintainability
When In Doubt
Am I building Toolsmith or consuming it?
- Building → Use this toolsmith skill
- Consuming → Use function-implementation skill
Can I use this raw operator?
- Check if you're in Toolsmith library code (yes)
- Check if it's still-forbidden (classes, mutations, loops) (no)
- Document with
//++ [EXCEPTION]comment (mandatory)
Should I use Toolsmith function or raw operator?
- Type narrowing → Use Toolsmith predicate
- Simple operation → Use raw operator (with comment)
- Repetitive logic → Use Toolsmith function (DRY)
- Need monadic behavior → Use Toolsmith function
When in doubt: Document the exception and explain what consuming code should use instead.