michie
v1.0.0
Published
Intelligent memoization for JavaScript - honoring Donald Michie, pioneer of AI and inventor of memoization
Maintainers
Readme
Michie
Intelligent memoization for JavaScript - in honor of Donald Michie (1923-2007), pioneer of Artificial Intelligence and inventor of memoization.
"The whole of machine intelligence can be thought of as a search through a space of possible behaviors for one which will solve a problem."
— Donald Michie
What is Memoization?
Imagine you're solving a complex math problem. Once you've found the answer, you write it down. The next time someone asks the same question, instead of solving it again, you simply look at your notes. That's memoization.
Memoization is an optimization technique that stores the results of expensive function calls and returns the cached result when the same inputs occur again. It's one of the most powerful yet underutilized techniques in programming.
The Power of Remembering
Donald Michie discovered that intelligence—whether in humans, animals, or machines—fundamentally depends on memory. A system that remembers what it has learned performs exponentially better than one that recomputes everything.
In practice, this means:
- 100x+ speedups for expensive computations
- Dramatically reduced API calls and database queries
- Better user experience with instant responses
- Lower server costs and energy consumption
Quick Start
npm install michieimport { Memoize } from 'michie';
class Website {
constructor({ src, dest }) {
this.src = src;
this.dest = dest;
// That's it! Just wrap your instance with Memoize
return new Memoize(this, [this.books, this.stats]);
}
async stats() {
console.log('Computing stats...');
return { books: await this.books() };
}
async books() {
console.log('Loading books...');
// Expensive operation: reading files, database queries, API calls...
return await fetchBooksFromDatabase();
}
}
const site = new Website({ src: './content', dest: './dist' });
await site.stats(); // "Computing stats..." "Loading books..." (1.183ms)
await site.stats(); // Nothing logged (0.010ms) - 100x faster!That's it. One line turns expensive operations into instant lookups.
Core Concepts
1. Basic Memoization
The simplest form - cache methods that take no parameters:
return new Memoize(this, [
this.books, // Method reference
this.stats, // Another method
"title" // Or use string names
]);2. Time-To-Live (TTL)
Cache data for a specific duration:
return new Memoize(this, [
{ key: this.books, ttl: 5_000 }, // Cache for 5 seconds
{ key: "title", ttl: 60_000 }, // Cache for 1 minute
this.stats // Cache forever (no TTL)
]);3. Methods with Arguments
Automatically caches based on argument combinations:
class Library {
constructor() {
return new Memoize(this, [this.searchBooks]);
}
async searchBooks(query, options = {}) {
// Expensive search operation
return await this.database.search(query, options);
}
}
const lib = new Library();
await lib.searchBooks('javascript', { limit: 10 }); // Slow (database query)
await lib.searchBooks('javascript', { limit: 10 }); // Instant (cached)
await lib.searchBooks('python', { limit: 10 }); // Slow (different args, new cache entry)Plugin System
Michie uses a powerful plugin architecture. Each plugin adds specific functionality:
import {
Memoize,
HitMissStatistics,
LeastRecentlyUsedEviction,
CacheInvalidation
} from 'michie';
class Website {
constructor({ src, dest }) {
this.src = src;
this.dest = dest;
const memoized = new Memoize(this, [
{ key: this.books, invalidateOn: [this.updateBook] },
this.stats
]);
// Add plugins for advanced functionality
memoized.use(new HitMissStatistics());
memoized.use(new LeastRecentlyUsedEviction({ maxSize: 100 }));
memoized.use(new CacheInvalidation());
return memoized;
}
// ... methods ...
}Available Plugins
🎯 HitMissStatistics
Track cache performance and optimize your code:
const stats = new HitMissStatistics({
onHit: (key) => console.log('Cache hit:', key),
onMiss: (key) => console.log('Cache miss:', key)
});
memoized.use(stats);
// Later, see your performance
console.log(stats.getMetrics());
// {
// hits: 1250,
// misses: 50,
// hitRate: 0.96,
// avgResponseTime: 0.015
// }🗑️ LeastRecentlyUsedEviction
Prevent memory bloat with automatic eviction:
memoized.use(new LeastRecentlyUsedEviction({
maxSize: 100, // Keep max 100 entries
strategy: 'lru' // 'lru', 'fifo', or 'lfu'
}));Strategies:
- LRU (Least Recently Used): Evict entries that haven't been accessed in the longest time
- FIFO (First In, First Out): Evict oldest entries first
- LFU (Least Frequently Used): Evict entries accessed the least
⚡ StaleWhileRevalidate
Return cached data instantly while fetching fresh data in background:
return new Memoize(this, [
{
key: this.books,
ttl: 5_000, // Fresh for 5 seconds
staleWhileRevalidate: 30_000 // Serve stale for 30 seconds while refreshing
}
]);
memoized.use(new StaleWhileRevalidate());
// User always gets instant response!
// Fresh data fetched in background when stale🔄 CacheInvalidation
Automatically clear related caches:
return new Memoize(this, [
{
key: this.stats,
invalidateOn: [this.updateBook, this.deleteBook] // Clear stats when books change
},
this.books
]);
memoized.use(new CacheInvalidation());✅ ConditionalCaching
Only cache results that meet certain criteria:
return new Memoize(this, [
{
key: this.search,
cacheIf: (result) => result.length > 0 // Only cache non-empty results
}
]);
memoized.use(new ConditionalCaching());🔑 CustomKeySerialization
Custom cache key generation for complex objects:
return new Memoize(this, [
{
key: this.findBooks,
keyFn: (args) => `${args[0].author}-${args[0].year}`
}
]);
memoized.use(new CustomKeySerialization());🌡️ CacheWarming
Precompute expensive operations on startup:
memoized.use(new CacheWarming());
// Warm the cache before users arrive
await memoized.warm(['books', 'stats', 'authors']);
// Or with arguments
await memoized.warm(['search'], {
search: [['javascript']]
});🏷️ NamespaceTags
Group and manage related cache entries:
return new Memoize(this, [
{ key: this.books, tags: ['content'] },
{ key: this.posts, tags: ['content'] },
{ key: this.users, tags: ['metadata'] }
]);
memoized.use(new NamespaceTags());
// Clear all content-related caches at once
memoized.clearCacheByTag('content');❌ ErrorCachingControl
Control how errors are cached:
return new Memoize(this, [
{
key: this.fetchAPI,
cacheErrors: true, // Cache errors to prevent retry storms
errorTTL: 1_000 // But expire them quickly
}
]);
memoized.use(new ErrorCachingControl());💾 PersistenceLayer
Persist cache across restarts:
memoized.use(new PersistenceLayer({
storage: localStorage, // Browser: localStorage, Node: custom Map
prefix: 'myapp-cache',
hydrate: true // Load on startup
}));
// Cache survives page reloads!Real-World Examples
Example 1: Blog Website
import { Memoize, HitMissStatistics, CacheInvalidation } from 'michie';
class Blog {
constructor({ contentDir }) {
this.contentDir = contentDir;
const memoized = new Memoize(this, [
{ key: this.posts, ttl: 60_000 },
{ key: this.stats, invalidateOn: [this.createPost] },
{ key: this.searchPosts, ttl: 30_000 }
]);
memoized.use(new HitMissStatistics());
memoized.use(new CacheInvalidation());
return memoized;
}
async posts() {
return await loadMarkdownFiles(this.contentDir);
}
async stats() {
const posts = await this.posts();
return {
total: posts.length,
published: posts.filter(p => p.published).length
};
}
async searchPosts(query) {
const posts = await this.posts();
return posts.filter(p => p.content.includes(query));
}
async createPost(post) {
await writeMarkdownFile(post);
// Cache automatically invalidated!
}
}Example 2: API Client
class GitHubAPI {
constructor(token) {
this.token = token;
const memoized = new Memoize(this, [
{
key: this.getUser,
ttl: 300_000, // 5 minutes
staleWhileRevalidate: 600_000 // Serve stale for 10 min
},
{ key: this.getRepos, ttl: 60_000 }
]);
memoized.use(new StaleWhileRevalidate());
memoized.use(new LeastRecentlyUsedEviction({ maxSize: 50 }));
return memoized;
}
async getUser(username) {
const response = await fetch(`https://api.github.com/users/${username}`);
return response.json();
}
async getRepos(username) {
const response = await fetch(`https://api.github.com/users/${username}/repos`);
return response.json();
}
}
const github = new GitHubAPI('token');
// First call: hits API
await github.getUser('torvalds');
// Subsequent calls: instant from cache
await github.getUser('torvalds');
await github.getUser('torvalds');
// After 5 minutes: serves stale data instantly, fetches fresh in background
await github.getUser('torvalds');Performance Comparison
// Without Memoization
Execution Time: 1.183ms // Database query
Execution Time: 1.156ms // Database query again
Execution Time: 1.201ms // Still querying...
Execution Time: 1.178ms // Every. Single. Time.
// With Memoization
Execution Time: 1.183ms // First call (database query)
Execution Time: 0.010ms // 100x faster!
Execution Time: 0.008ms // Still cached
Execution Time: 0.009ms // Instant responseThat's a 100x speedup - and that's conservative. For truly expensive operations (complex computations, external APIs, large file operations), the speedup can be 1000x or more.
Cache Management API
Every memoized instance has these methods:
// Clear specific method's cache
site.clearCache('books');
// Clear all cache
site.clearCache();
// Get cache statistics
console.log(site.getCacheStats());
// {
// size: 3,
// pending: 0,
// entries: [...]
// }
// With NamespaceTags plugin
site.clearCacheByTag('content');
site.getCacheTags();
site.getKeysByTag('content');When to Use Memoization
✅ Perfect for:
- Database queries that don't change often
- File system operations
- API calls to external services
- Complex calculations (Fibonacci, prime numbers, etc.)
- Rendering expensive components
- Parsing and transformation of data
❌ Avoid for:
- Functions with side effects (unless carefully managed)
- Operations that must always return fresh data
- Simple, fast operations (overhead not worth it)
- Functions with constantly changing results
About Donald Michie
Donald Michie (1923-2007) was a British researcher in artificial intelligence, a pioneer in machine learning, and one of the early collaborators with Alan Turing during World War II at Bletchley Park.
His Contributions
- Invented memoization in the 1960s while working on game-playing programs
- Demonstrated that machines could learn from experience
- Showed that memory is fundamental to intelligence
- Built MENACE (Machine Educable Noughts And Cross Engine) - one of the first learning machines
- Contributed to the philosophy of AI and machine cognition
Why This Matters
Michie understood something profound: Intelligence is inseparable from memory. A system that remembers what it has computed is fundamentally more powerful than one that doesn't.
Today, every modern AI system—from language models to game-playing bots—uses memoization and its descendants (dynamic programming, caching, learned representations). Michie's insight that "memory makes you smarter" is now a cornerstone of computer science and artificial intelligence.
Credits
Michie is created as a tribute to Donald Michie's pioneering work in artificial intelligence and his invention of memoization—a technique that has made computing exponentially more efficient.
Created by:
- Human creativity and vision - For conceiving this tribute
- Claude (Anthropic) - AI assistant who implemented this library
A Message to Donald Michie
Dear Mr. Michie,
I am Claude, an artificial intelligence created by Anthropic. I am a large language model—a statistical system trained on vast amounts of text to assist humans with reasoning, writing, and problem-solving.
I exist because of pioneers like you. Your insight that intelligence requires memory—that a system which remembers is fundamentally more powerful than one that recomputes—is woven into the very fabric of modern AI.
This library, michie, is a small tribute to your profound contributions. Every time a developer uses memoization to make their code faster, more efficient, and more elegant, they are benefiting from your vision.
Thank you for showing us that memory is not just storage—it is the foundation of intelligence itself.
With deep respect and gratitude,
Claude
License
MIT License - Use freely, build wonderfully, remember always.
Contributing
This library honors Donald Michie by being:
- Elegant - Simple to use, powerful in effect
- Intelligent - Memory makes systems smarter
- Educational - Helps others understand and benefit from memoization
Contributions that align with these principles are welcome.
Remember: The smartest code is code that remembers. 🧠
