Summary
Use Effect.cached to memoize preset loading, avoiding redundant dynamic imports when the same preset is referenced multiple times.
Problem
If a user's config references the same preset multiple times (or if multiple commands load the same config), the preset is imported fresh each time:
// Current implementation
const loadPreset = (name: string): Effect.Effect<Preset, PresetLoadError> =>
Effect.gen(function* () {
const module = yield* Effect.tryPromise({
try: () => import(name), // Fresh import every time
catch: error => new PresetLoadError({ preset: name, message: String(error) })
})
// ...
})
Inefficiency: Each call to loadPreset("@effect-migrate/preset-basic") re-imports the module, even though module loading is inherently cacheable.
Proposed Solution
Use Effect.cached to memoize preset loading per preset name:
import { Cache, Effect } from "effect"
// Create a cache with preset name as key
const presetCache = Cache.make({
capacity: 100, // Max number of presets to cache
timeToLive: "infinity", // Presets don't change during CLI run
lookup: loadPresetUncached
})
const loadPresetUncached = (name: string): Effect.Effect<Preset, PresetLoadError> =>
Effect.gen(function* () {
const module = yield* Effect.tryPromise({
try: () => import(name),
catch: error => new PresetLoadError({ preset: name, message: String(error) })
})
const preset = module.default ?? module.preset
if (!isValidPreset(preset)) {
return yield* Effect.fail(
new PresetLoadError({
preset: name,
message: "Invalid preset shape: must have 'rules' array"
})
)
}
return preset
})
// Public API
export const loadPreset = (name: string): Effect.Effect<Preset, PresetLoadError> =>
Effect.gen(function* () {
const cache = yield* presetCache
return yield* cache.get(name)
})
Alternative: Simple Memoization
For simpler approach without Cache:
import { Effect } from "effect"
const loadPresetEffect = (name: string): Effect.Effect<Preset, PresetLoadError> =>
Effect.gen(function* () {
const module = yield* Effect.tryPromise({
try: () => import(name),
catch: error => new PresetLoadError({ preset: name, message: String(error) })
})
// ... validation
})
// Memoize with Effect.cached
const cachedLoadPreset = Effect.cached(loadPresetEffect)
export const loadPreset = (name: string) => cachedLoadPreset(name)
Benefits
- Performance: Avoid redundant imports
- Consistency: Same preset instance used throughout CLI run
- Memory efficient: Cache bounded by number of unique presets (small)
Use Cases
- Multiple commands:
audit and metrics both load config → same preset imported twice
- Config reload: If config is reloaded during watch mode
- Testing: Faster test runs when same preset used across tests
Acceptance Criteria
Measurement
Before/after benchmark:
it.effect("should cache preset loads", () =>
Effect.gen(function* () {
const start1 = Date.now()
yield* loadPreset("@effect-migrate/preset-basic")
const duration1 = Date.now() - start1
const start2 = Date.now()
yield* loadPreset("@effect-migrate/preset-basic") // Should be cached
const duration2 = Date.now() - start2
expect(duration2).toBeLessThan(duration1 * 0.1) // 10x faster
})
)
Related
Summary
Use
Effect.cachedto memoize preset loading, avoiding redundant dynamic imports when the same preset is referenced multiple times.Problem
If a user's config references the same preset multiple times (or if multiple commands load the same config), the preset is imported fresh each time:
Inefficiency: Each call to
loadPreset("@effect-migrate/preset-basic")re-imports the module, even though module loading is inherently cacheable.Proposed Solution
Use
Effect.cachedto memoize preset loading per preset name:Alternative: Simple Memoization
For simpler approach without
Cache:Benefits
Use Cases
auditandmetricsboth load config → same preset imported twiceAcceptance Criteria
Measurement
Before/after benchmark:
Related
Cachefromeffect/CacheorEffect.cached