UNPKG

tiny-multimethods

Version:

Tiny port of Clojure's multimethods with zero dependencies and 30 LOC.

165 lines (122 loc) â€Ē 4.24 kB
# tiny-multimethods A lightweight implementation of Clojure-style multimethods for JavaScript in under 30 lines of code. Define functions that dispatch to different implementations based on runtime values. ## Installation ```bash npm install tiny-multimethods ``` ## Usage ```javascript import { defmulti, DEFAULT } from 'tiny-multimethods'; // Create a multimethod that dispatches based on shape type const calculateArea = defmulti( shape => shape.type, ['circle', shape => Math.PI * shape.radius ** 2], ['rectangle', shape => shape.width * shape.height], [DEFAULT, shape => { throw new Error(`Can't calculate area of ${shape.type}`); }] ); // Use it console.log(calculateArea({ type: 'circle', radius: 5 })); // 78.54... console.log(calculateArea({ type: 'rectangle', width: 4, height: 3 })); // 12 // Add new methods dynamically calculateArea.addMethod('triangle', shape => (shape.base * shape.height) / 2 ); ``` ## Features - ðŸŠķ Lightweight: Less than 30 lines of code - ðŸŽŊ Flexible dispatch: Use any function to determine which method to call - 🔌 Extensible: Add new methods at runtime - 🛟 Default cases: Handle unknown values gracefully ## API ### `defmulti(dispatchFn, ...methods)` Creates a new multimethod using the provided dispatch function and method implementations. Parameters: - `dispatchFn`: Function that takes an argument and returns a dispatch value - `...methods`: Array of `[dispatchValue, implementation]` pairs Returns: A function that will dispatch to the appropriate method based on the dispatch function's result. ### `DEFAULT` A special symbol used to define default method implementations. ## Examples ### Content Renderer ```javascript const renderContent = defmulti( content => content.type, ['text', content => `<p>${content.value}</p>`], ['image', content => `<img src="${content.url}" alt="${content.alt || ''}">`], ['video', content => ` <video controls> <source src="${content.url}" type="video/mp4"> </video> `], [DEFAULT, content => `<div>Unsupported content type: ${content.type}</div>`] ); // Usage renderContent({ type: 'text', value: 'Hello World' }); // <p>Hello World</p> ``` ### Priority-based Notifications ```javascript const notifyUser = defmulti( notification => notification.priority, ['high', n => { console.log('ðŸšĻ URGENT:', n.message); return 'high-priority-handled'; }], ['medium', n => { console.log('â„đïļ NOTICE:', n.message); return 'medium-priority-handled'; }], ['low', n => { console.log('📝 FYI:', n.message); return 'low-priority-handled'; }] ); notifyUser({ priority: 'high', message: 'System is down!' }); // ðŸšĻ URGENT: System is down! ``` ### Complex Dispatch ```javascript const calculateShipping = defmulti( // Dispatch based on multiple factors order => `${order.method}-${order.type}`, ['express-fragile', order => { const baseCost = order.weight * 0.1 + order.distance * 0.05; return baseCost * 2.5 + 15; // Extra handling + insurance }], ['standard-regular', order => { return order.weight * 0.1 + order.distance * 0.05; }] ); ``` ### Dynamic Method Addition ```javascript const processor = defmulti( data => data.version, ['v1', data => ({ ...data, upgraded: true })] ); // inspect available methods processor.methods() // => ['v1'] // Add support for new version later processor.addMethod('v2', data => ({ ...data, upgraded: true, timestamp: Date.now() })); // observe new method is available processor.methods() // => ['v1', 'v2'] ``` ## Why Use This? - **Clean Code Organization**: Group related implementations together instead of scattered if/else statements - **Runtime Extensibility**: Add new behaviors without modifying existing code - **Simple but Powerful**: Lightweight implementation that handles most common use cases - **Flexible Dispatch**: Use any function to determine which implementation to use ## Inspiration This implementation is inspired by Clojure's multimethod system, bringing similar capabilities to JavaScript in a lightweight package. ## License MIT ## Contributing Issues and pull requests are welcome! Feel free to contribute to make this tiny utility even better.