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.Cached

First 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"
30

Second deref (loaded from cache, instant):

(time @cached-result)
"Elapsed time: 0.3385 msecs"
30

Wrapping 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"
20

Same args — cache hit:

(time @(cached-expensive 5 15))
"Elapsed time: 0.438541 msecs"
20

Different 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"
15

Nil 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
nil

Loading nil from cache:

(deref nil-result)
nil

Important: 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

Cleanup

(pocket/cleanup!)
10:06:31.876 INFO scicloj.pocket - Cache cleanup: /tmp/pocket-demo
{:dir "/tmp/pocket-demo", :existed true}
source: notebooks/pocket_book/getting_started.clj