2 Getting Started
Last modified: 2026-02-08
Pocket is a Clojure library for filesystem-based caching of expensive computations. It persists results to disk so they survive JVM restarts. Cache keys are derived from the function identity and its arguments, so the same computation always maps to the same cache entry.
Setup
(ns pocket-book.getting-started
(:require
;; Pocket API:
[scicloj.pocket :as pocket]
;; Annotating kinds of visualizations:
[scicloj.kindly.v4.kind :as kind]
;; Logging setup for this chapter (see Logging chapter):
[pocket-book.logging]))First, we set up a cache directory and define an expensive computation:
(def cache-dir "/tmp/pocket-demo")(pocket/set-base-cache-dir! cache-dir)10:06:30.644 INFO scicloj.pocket - Cache dir set to: /tmp/pocket-demo
"/tmp/pocket-demo"Start fresh so the examples below run from a clean slate:
(pocket/cleanup!)10:06:30.645 INFO scicloj.pocket - Cache cleanup: /tmp/pocket-demo
{:dir "/tmp/pocket-demo", :existed false}(defn expensive-calculation
"Simulates an expensive computation"
[x y]
(println (str "Computing " x " + " y " (this is expensive!)"))
(Thread/sleep 400)
(+ x y))Background: deref in Clojure
In Clojure, deref extracts a value from a reference type. It can be written as (deref x) or with the shorthand reader macro @x — both are equivalent. Pocket’s cached returns a Cached object that implements IDeref, so you use @ (or deref) to trigger the computation and retrieve the result.
Creating a cached computation
cached creates a lazy cached computation. It returns a Cached object — the computation won’t run until we deref it:
(def cached-result
(pocket/cached #'expensive-calculation 10 20))(type cached-result)scicloj.pocket.impl.cache.CachedFirst deref (computes and caches):
(time @cached-result)10:06:30.650 INFO scicloj.pocket.impl.cache - Cache miss, computing: pocket-book.getting-started/expensive-calculation
Computing 10 + 20 (this is expensive!)
"Elapsed time: 401.977817 msecs"
30Second deref (loaded from cache, instant):
(time @cached-result)"Elapsed time: 0.3385 msecs"
30Wrapping functions with caching-fn
For convenience, caching-fn wraps a function so that every call returns a Cached object:
(def cached-expensive
(pocket/caching-fn #'expensive-calculation))First call:
(time @(cached-expensive 5 15))10:06:31.058 INFO scicloj.pocket.impl.cache - Cache miss, computing: pocket-book.getting-started/expensive-calculation
Computing 5 + 15 (this is expensive!)
"Elapsed time: 402.068495 msecs"
20Same args — cache hit:
(time @(cached-expensive 5 15))"Elapsed time: 0.438541 msecs"
20Different args — new computation:
(time @(cached-expensive 7 8))10:06:31.465 INFO scicloj.pocket.impl.cache - Cache miss, computing: pocket-book.getting-started/expensive-calculation
Computing 7 + 8 (this is expensive!)
"Elapsed time: 401.948657 msecs"
15Nil handling
Pocket properly handles nil values. Since the cache uses files on disk, it needs to distinguish “never computed” from “computed and got nil”. It does this with a special marker file:
(defn returns-nil [] nil)(def nil-result (pocket/cached #'returns-nil))Cached nil value:
(deref nil-result)10:06:31.871 INFO scicloj.pocket.impl.cache - Cache miss, computing: pocket-book.getting-started/returns-nil
nilLoading nil from cache:
(deref nil-result)nilImportant: use vars or keywords for functions
Always pass functions as vars (#'fn-name) or keywords, not as bare function objects. Vars have stable names that produce consistent cache keys across sessions. Keywords are useful for extracting from cached maps (e.g., (cached :train split-c)). Pocket throws an error if you pass a bare function:
;; ✅ (pocket/cached #'my-function args)
;; ✅ (pocket/cached :train cached-map)
;; ❌ (pocket/cached my-function args)See the Usage Practices chapter for a detailed explanation and more best practices.
Next steps
Configuration — cache directory, in-memory eviction policies,
pocket.ednRecursive Caching in Pipelines — chaining cached computations
Usage Practices — invalidation strategies, testing, serialization, and more
Cleanup
(pocket/cleanup!)10:06:31.876 INFO scicloj.pocket - Cache cleanup: /tmp/pocket-demo
{:dir "/tmp/pocket-demo", :existed true}