haxl- A Haskell library for efficient, concurrent, and concise data access.

Safe HaskellNone




Memoization support. This module is provided for access to Haxl internals only; most users should import Haxl.Core instead.


Basic memoization

cachedComputation :: forall req u a. (Eq (req a), Hashable (req a), Typeable (req a)) => req a -> GenHaxl u a -> GenHaxl u a Source #

cachedComputation memoizes a Haxl computation. The key is a request.

Note: These cached computations will not be included in the output of dumpCacheAsHaskell.

preCacheComputation :: forall req u a. (Eq (req a), Hashable (req a), Typeable (req a)) => req a -> GenHaxl u a -> GenHaxl u a Source #

Like cachedComputation, but fails if the cache is already populated.

Memoization can be (ab)used to "mock" a cached computation, by pre-populating the cache with an alternative implementation. In that case we don't want the operation to populate the cache to silently succeed if the cache is already populated.

High-level memoization

memo :: (Typeable a, Typeable k, Hashable k, Eq k) => k -> GenHaxl u a -> GenHaxl u a Source #

Memoize a computation using an arbitrary key. The result will be calculated once; the second and subsequent time it will be returned immediately. It is the caller's responsibility to ensure that for every two calls memo key haxl, if they have the same key then they compute the same result.

data MemoFingerprintKey a where Source #

A memo key derived from a 128-bit MD5 hash. Do not use this directly, it is for use by automatically-generated memoization.

memoize :: GenHaxl u a -> GenHaxl u (GenHaxl u a) Source #

Transform a Haxl computation into a memoized version of itself.

Given a Haxl computation, memoize creates a version which stores its result in a MemoVar (which memoize creates), and returns the stored result on subsequent invocations. This permits the creation of local memos, whose lifetimes are scoped to the current function, rather than the entire request.

memoize1 :: (Eq a, Hashable a) => (a -> GenHaxl u b) -> GenHaxl u (a -> GenHaxl u b) Source #

Transform a 1-argument function returning a Haxl computation into a memoized version of itself.

Given a function f of type a -> GenHaxl u b, memoize1 creates a version which memoizes the results of f in a table keyed by its argument, and returns stored results on subsequent invocations with the same argument.


allFriends :: [Int] -> GenHaxl u [Int]
allFriends ids = do
  memoizedFriendsOf <- memoize1 friendsOf
  concat <$> mapM memoizeFriendsOf ids

The above implementation will not invoke the underlying friendsOf repeatedly for duplicate values in ids.

memoize2 :: (Eq a, Hashable a, Eq b, Hashable b) => (a -> b -> GenHaxl u c) -> GenHaxl u (a -> b -> GenHaxl u c) Source #

Transform a 2-argument function returning a Haxl computation, into a memoized version of itself.

The 2-ary version of memoize1, see its documentation for details.

Local memoization

newMemo :: GenHaxl u (MemoVar u a) Source #

Create a new MemoVar for storing a memoized computation. The created MemoVar is initially empty, not tied to any specific computation. Running this memo (with runMemo) without preparing it first (with prepareMemo) will result in an exception.

newMemoWith :: GenHaxl u a -> GenHaxl u (MemoVar u a) Source #

Convenience function, combines newMemo and prepareMemo.

prepareMemo :: MemoVar u a -> GenHaxl u a -> GenHaxl u () Source #

Store a computation within a supplied MemoVar. Any memo stored within the MemoVar already (regardless of completion) will be discarded, in favor of the supplied computation. A MemoVar must be prepared before it is run.

runMemo :: MemoVar u a -> GenHaxl u a Source #

Continue the memoized computation within a given MemoVar. Notes:

  1. If the memo contains a complete result, return that result.
  2. If the memo contains an in-progress computation, continue it as far as possible for this round.
  3. If the memo is empty (it was not prepared), throw an error.

For example, to memoize the computation one given by:

one :: Haxl Int
one = return 1


  oneMemo <- newMemoWith one
  let memoizedOne = runMemo aMemo one
  oneResult <- memoizedOne

To memoize mutually dependent computations such as in:

h :: Haxl Int
h = do
  a <- f
  b <- g
  return (a + b)
  f = return 42
  g = succ <$> f

without needing to reorder them, use:

h :: Haxl Int
h = do
  fMemoRef <- newMemo
  gMemoRef <- newMemo

  let f = runMemo fMemoRef
      g = runMemo gMemoRef

  prepareMemo fMemoRef $ return 42
  prepareMemo gMemoRef $ succ <$> f

  a <- f
  b <- g
  return (a + b)