Skip to content

Add CacheConfig: typed cache-tag/lifetime functor over Cache#27

Merged
reebalazs merged 1 commit into
mainfrom
reebalazs/cache-config
Jul 1, 2026
Merged

Add CacheConfig: typed cache-tag/lifetime functor over Cache#27
reebalazs merged 1 commit into
mainfrom
reebalazs/cache-config

Conversation

@reebalazs

Copy link
Copy Markdown
Member

Adds CacheConfig (GreenfinityNext.CacheConfig) — a functor wrapping the Cache 'use cache' tag + lifetime bindings with typed variants, so each app supplies its own cache-tag type while sharing one implementation. (Lifts the per-app helper that lived in njc-2026's Content_Cache.)

API

module type CacheConfig = {
  type tag
  type life
  let tagToString: tag => string
  let lifeToString: life => string
}

module DefaultsLife = {                       // @as("seconds") Seconds … @as("max") Max
  type life = Seconds | Minutes | Hours | Days | Max
  let lifeToString = (life: life) => (life :> string)
}

module Make = (Config: CacheConfig) => {
  // re-exports tag/life/tagToString/lifeToString + cacheTag/cacheLife/updateTag/revalidateTag
}

Why tagToString in the config (not a coercion inside the functor)

(tag :> string) is compile-checked and only works on a concrete @as-string variant; an abstract functor type (Config.tag) "is not a subtype of string", so the coercion has to happen where the concrete type is known — the app's tagToString. There's no built-in "coercible-to-string" constraint and no type classes, so passing the conversion is the idiomatic stand-in. DefaultsLife carries the common lifetime enum so apps include it.

Usage

The config must be a named module (the functor result mentions Config.tag/Config.life, so an inline struct gives "parameter cannot be eliminated"):

module MyCacheConfigBase = {
  include DefaultsLife
  type tag = | @as("public-content") PublicContent
  let tagToString = (tag: tag) => (tag :> string)
}
module MyCacheConfig = Make(MyCacheConfigBase)
MyCacheConfig.cacheTag(MyCacheConfigBase.PublicContent)

A functor that wraps the Cache 'use cache' tag + lifetime bindings with typed
variants, so each app supplies its own cache-tag type while sharing one
implementation. The tag's string coercion lives at the call site (the app's
tagToString) since an abstract functor type can't be coerced to string;
Defaults carries the common lifetime enum (Seconds … Max).
@reebalazs
reebalazs merged commit e77cfc7 into main Jul 1, 2026
1 check passed
@reebalazs
reebalazs deleted the reebalazs/cache-config branch July 1, 2026 05:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant