memoize (empty) → 0.1
raw patch · 6 files changed
+659/−0 lines, 6 filesdep +basedep +haskell98dep +template-haskellsetup-changed
Dependencies added: base, haskell98, template-haskell
Files
- Data/Function/Memoize.hs +328/−0
- Data/Function/Memoize/Class.hs +15/−0
- Data/Function/Memoize/TH.hs +247/−0
- LICENSE +25/−0
- Setup.hs +4/−0
- memoize.cabal +40/−0
+ Data/Function/Memoize.hs view
@@ -0,0 +1,328 @@+{-# LANGUAGE+ DeriveFunctor,+ GeneralizedNewtypeDeriving,+ TemplateHaskell,+ UnicodeSyntax+ #-}+{- |+ A function memoization library.++ This includes a class for memoizable argument types and a Template+ Haskell expander for deriving instances of the class.++ Note that most memoization in this style relies on assumptions about+ the implementation of non-strictness (as laziness) that are not+ guaranteed by the semantics. However, it appears to work.+-}+module Data.Function.Memoize (+ -- * Memoization class+ Memoizable(..),+ -- ** Operations+ -- *** Higher-arity memoize+ memoize2, memoize3, memoize4, memoize5, memoize6, memoize7,+ -- *** Memoizing open recursion+ memoFix, memoFix2, memoFix3, memoFix4, memoFix5, memoFix6, memoFix7,+ -- *** Tracing memoization+ traceMemoize,++ -- * For making instances for finite types+ memoizeFinite,++ -- * Deriving 'Memoizable'+ deriveMemoizable, deriveMemoizableParams, deriveMemoize,+) where++import Control.Applicative+import Control.Monad+import Debug.Trace++import Data.Function.Memoize.Class+import Data.Function.Memoize.TH++-- | Memoize a two argument function+memoize2 ∷ (Memoizable a, Memoizable b) ⇒+ (a → b → v) → a → b → v+memoize2 v = memoize (memoize . v)++-- | Memoize a three argument function+memoize3 ∷ (Memoizable a, Memoizable b, Memoizable c) ⇒+ (a → b → c → v) → a → b → c → v+memoize3 v = memoize (memoize2 . v)++-- | Memoize a four argument function+memoize4 ∷ (Memoizable a, Memoizable b, Memoizable c, Memoizable d) ⇒+ (a → b → c → d → v) →+ a → b → c → d → v+memoize4 v = memoize (memoize3 . v)++-- | Memoize a five argument function+memoize5 ∷ (Memoizable a, Memoizable b, Memoizable c, Memoizable d,+ Memoizable e) ⇒+ (a → b → c → d → e → v) →+ a → b → c → d → e → v+memoize5 v = memoize (memoize4 . v)++-- | Memoize a six argument function+memoize6 ∷ (Memoizable a, Memoizable b, Memoizable c, Memoizable d,+ Memoizable e, Memoizable f) ⇒+ (a → b → c → d → e → f → v) →+ a → b → c → d → e → f → v+memoize6 v = memoize (memoize5 . v)++-- | Memoize a seven argument function+memoize7 ∷ (Memoizable a, Memoizable b, Memoizable c, Memoizable d,+ Memoizable e, Memoizable f, Memoizable g) ⇒+ (a → b → c → d → e → f → g → v) →+ a → b → c → d → e → f → g → v+memoize7 v = memoize (memoize6 . v)++-- | Memoizes the least fixed point of a function. This is like+-- 'Data.Function.fix', but it passes the fixed function a memoized+-- version of itself, so this memoizes using all recursive calls as well.+memoFix ∷ Memoizable a ⇒ ((a → v) → a → v) → a → v+memoFix ff = f where f = memoize (ff f)++-- | Two argument version of 'memoFix'.+memoFix2 ∷ (Memoizable a, Memoizable b) ⇒+ ((a → b → v) → a → b → v) → a → b → v+memoFix2 ff = f where f = memoize2 (ff f)++-- | Three argument version of 'memoFix'.+memoFix3 ∷ (Memoizable a, Memoizable b, Memoizable c) ⇒+ ((a → b → c → v) → a → b → c → v) → a → b → c → v+memoFix3 ff = f where f = memoize3 (ff f)++-- | Four argument version of 'memoFix'.+memoFix4 ∷ (Memoizable a, Memoizable b, Memoizable c, Memoizable d) ⇒+ ((a → b → c → d → v) → (a → b → c → d → v)) →+ a → b → c → d → v+memoFix4 ff = f where f = memoize4 (ff f)++-- | Five argument version of 'memoFix'.+memoFix5 ∷ (Memoizable a, Memoizable b, Memoizable c, Memoizable d,+ Memoizable e) ⇒+ ((a → b → c → d → e → v) → (a → b → c → d → e → v)) →+ a → b → c → d → e → v+memoFix5 ff = f where f = memoize5 (ff f)++-- | Six argument version of 'memoFix'.+memoFix6 ∷ (Memoizable a, Memoizable b, Memoizable c, Memoizable d,+ Memoizable e, Memoizable f) ⇒+ ((a → b → c → d → e → f → v) → (a → b → c → d → e → f → v)) →+ a → b → c → d → e → f → v+memoFix6 ff = f where f = memoize6 (ff f)++-- | Seven argument version of 'memoFix'.+memoFix7 ∷ (Memoizable a, Memoizable b, Memoizable c, Memoizable d,+ Memoizable e, Memoizable f, Memoizable g) ⇒+ ((a → b → c → d → e → f → g → v) → (a → b → c → d → e → f → g → v)) →+ a → b → c → d → e → f → g → v+memoFix7 ff = f where f = memoize7 (ff f)++-- | Give a one-argument function whose argument satisfies 'Show',+-- this memoizes the function such that the argument is shown (using+-- 'Debug.Trace.trace') only when the function has to be applied, as+-- opposed to when the answer is available in the memo cache.+traceMemoize ∷ (Memoizable a, Show a) ⇒ (a → b) → a → b+traceMemoize f = memoize (\a → traceShow a (f a))++---+--- Derived instances+---++deriveMemoizable ''()+deriveMemoizable ''Bool+deriveMemoizable ''Ordering+deriveMemoizable ''Maybe+deriveMemoizable ''Either+deriveMemoizable ''[]++deriveMemoizable ''(,)+deriveMemoizable ''(,,)+deriveMemoizable ''(,,,)+deriveMemoizable ''(,,,,)+deriveMemoizable ''(,,,,,)+deriveMemoizable ''(,,,,,,)+deriveMemoizable ''(,,,,,,,)+deriveMemoizable ''(,,,,,,,,)+deriveMemoizable ''(,,,,,,,,,)+deriveMemoizable ''(,,,,,,,,,,)+deriveMemoizable ''(,,,,,,,,,,,)++---+--- Binary-tree based memo caches+---++-- Used for both 'Integer' and arbitrary 'Int'-like types.++data BinaryTreeCache v+ = BinaryTreeCache {+ btValue ∷ v,+ btLeft, btRight ∷ BinaryTreeCache v+ }+ deriving Functor++---+--- 'Integer' memoization+---++instance Memoizable Integer where+ memoize f = integerLookup (f <$> theIntegers)++-- | An integer cache stores a value for 0 and separate caches for the+-- positive and negative integers.+data IntegerCache v+ = IntegerCache {+ icZero ∷ v,+ icNegative, icPositive ∷ PosIntCache v+ }+ deriving Functor++-- | A positive integer cache is represented as a little-ending bitwise+-- trie+type PosIntCache v = BinaryTreeCache v++theIntegers ∷ IntegerCache Integer+theIntegers+ = IntegerCache {+ icZero = 0,+ icNegative = negate <$> thePosInts,+ icPositive = thePosInts+ }++thePosInts ∷ PosIntCache Integer+thePosInts =+ BinaryTreeCache {+ btValue = 1,+ btLeft = fmap (* 2) thePosInts,+ btRight = fmap (succ . (* 2)) thePosInts+ }++integerLookup ∷ IntegerCache v → Integer → v+integerLookup cache n =+ case n `compare` 0 of+ EQ → icZero cache+ GT → posIntLookup (icPositive cache) n+ LT → posIntLookup (icNegative cache) (negate n)++-- PRECONDITION: @n@ is a positive 'Integer'+posIntLookup ∷ PosIntCache v → Integer → v+posIntLookup cache 1 = btValue cache+posIntLookup cache n+ | even n = posIntLookup (btLeft cache) (n `div` 2)+ | otherwise = posIntLookup (btRight cache) (n `div` 2)++---+--- Enumerable types using binary search trees+---++newtype Finite a = ToFinite { fromFinite ∷ a }+ deriving (Eq, Bounded, Enum)++instance (Bounded a, Enum a) ⇒ Memoizable (Finite a) where+ memoize f = finiteLookup (f <$> theFinites)++-- | For finite 'Int'-like types, we use a balanced binary search tree+-- indexed to every element from 'minBound' to 'maxBound'+theFinites ∷ (Bounded a, Enum a) ⇒ BinaryTreeCache a+theFinites = loop minBound maxBound where+ loop start stop =+ BinaryTreeCache {+ btValue = mean,+ btLeft = loop start (pred mean),+ btRight = loop (succ mean) stop+ }+ where mean = meanFinite start stop++finiteLookup ∷ (Bounded a, Enum a) ⇒ BinaryTreeCache v → a → v+finiteLookup cache0 a0 =+ loop start0 stop0 cache0 where+ start0 = fromEnum (minBound `asTypeOf` a0)+ stop0 = fromEnum (maxBound `asTypeOf` a0)+ a = fromEnum a0+ loop start stop cache =+ let mean = meanFinite start stop in+ case a `compare` mean of+ EQ → btValue cache+ LT → loop start (pred mean) (btLeft cache)+ GT → loop (succ mean) stop (btRight cache)++meanFinite ∷ (Bounded a, Enum a) ⇒ a → a → a+meanFinite a b = toEnum (ia + (ib - ia) `div` 2)+ where+ ia = fromEnum a+ ib = fromEnum b++-- | Can be used to memoize over any "finite" type satisfying+-- 'Enum' and 'Bounded'. This builds a binary search tree, treating+-- the memoized type as isomorphic to a range of 'Int', so it will be+-- only as efficient as 'toEnum', 'fromEnum', 'succ', and 'pred'.+--+-- This can be used to make instances for finite types. For example, the+-- instances for 'Int' and 'Char' are declared as:+--+-- @+-- instance Memoizable Int where memoize = memoizeFinite+-- instance Memoizable Char where memoize = memoizeFinite+-- @+memoizeFinite ∷ (Enum a, Bounded a) ⇒ (a → v) → a → v+memoizeFinite f = memoize (f . fromFinite) . ToFinite++instance Memoizable Int where memoize = memoizeFinite+instance Memoizable Char where memoize = memoizeFinite++---+--- Functions+---++instance (Eq a, Bounded a, Enum a, Memoizable b) ⇒ Memoizable (a → b) where+ memoize = functionLookup . theFunctions++functionLookup ∷ (Eq a, Bounded a, Enum a, Memoizable b) ⇒+ FunctionCache b v → (a → b) → v+functionLookup cache f =+ fcNil (foldl fcCons cache (f <$> [minBound .. maxBound]))++theFunctions ∷ (Eq a, Bounded a, Enum a, Memoizable b) ⇒+ ((a → b) → v) → FunctionCache b v+theFunctions f =+ FunctionCache {+ fcNil = f undefined,+ fcCons = memoize (\b → theFunctions (f . extend b))+ }+ where+ extend b g a+ | a == minBound = b+ | otherwise = g (pred a)++data FunctionCache b v+ = FunctionCache {+ fcNil ∷ v,+ fcCons ∷ b → FunctionCache b v+ }++---+--- Example functions+---++-- Memoize on 'Integer'. If memoization doesn't work, this will be+-- horribly slow.+_fib ∷ Integer → Integer+_fib = memoFix $ \fib n → case n of+ 0 → 1+ 1 → 1+ _ → fib (n - 1) + fib (n - 2)++-- Memoize on a function. The use of 'trace' will indicate when+-- the function is called to fill in the memo cache.+_isNot ∷ (Bool → Bool) → Bool+_isNot = memoize $ \f →+ trace "_isNot" $+ f True == False && f False == True++-- Memoize on a curried function!+_countTrue ∷ (Bool → Bool → Bool) → Integer+_countTrue = memoize $ \f →+ trace "_countTrue" $+ toInteger (length (f <$> [False,True] <*> [False,True] >>= guard))+
+ Data/Function/Memoize/Class.hs view
@@ -0,0 +1,15 @@+{-# LANGUAGE+ UnicodeSyntax+ #-}+{- |+ The 'Memoizable' type class.+-}+module Data.Function.Memoize.Class (+ Memoizable(..)+) where++-- | A memoization class. An instance @'Memoizable' T@ for some+-- type @T@ means that that 'memoize' method can memoize for+-- parameters of type @T@.+class Memoizable a where+ memoize ∷ (a → v) → a → v
+ Data/Function/Memoize/TH.hs view
@@ -0,0 +1,247 @@+{-# LANGUAGE+ TemplateHaskell,+ UnicodeSyntax+ #-}+{- |+ Exports functions for deriving instances of 'Memoizable' using+ Template Haskell. The @TemplateHaskell@ language extension must be+ enabled to use the functions exported from this module.+-}+module Data.Function.Memoize.TH (+ deriveMemoizable, deriveMemoizableParams, deriveMemoize,+) where++import Control.Applicative+import Control.Monad+import Language.Haskell.TH++import Data.Function.Memoize.Class++-- |+-- To derive 'Memoizable' instances for the given data types.+-- In the simplest usage, to derive 'Memoizable' for an algebraic+-- datatype named @T@, write:+--+-- @+-- deriveMemoizable ''T+-- @+--+-- This assumes that all the type parameters of @T@ that are not+-- annotated with a kind other than @*@ should be listed as requiring+-- 'Memoizable' instances in the instance context. For example, given+-- a data type declared as+--+-- @+-- data T a (b :: * -> *) c = ...+-- @+--+-- the generated instance will look like+--+-- @+-- instance ('Memoizable' a, 'Memoizable' c) =>+-- 'Memoizable' (T a b c) where ...+-- @+--+-- For more precise control over the context, use+-- 'deriveMemoizableParams'.+--+-- N.B.: The @TemplateHaskell@ language extension must be enabled to use+-- this function.+deriveMemoizable ∷ Name → Q [Dec]+deriveMemoizable n = deriveMemoizable' n Nothing++-- |+-- Like 'deriveMemoizable' but takes a second argument, which is a list+-- of 'Int's to specify which type parameters of the type should be+-- mentioned in the context. For example, given the same definition for+-- @T@ as above, we can write+--+-- @+-- deriveMemoizableParams ''T [3]+-- @+--+-- to leave the first parameter of @T@ out of the context and show+-- only the third, yielding the instance+--+-- @+-- instance 'Memoizable' c => 'Memoizable' (T a b c) where ...+-- @+--+-- N.B.: The @TemplateHaskell@ language extension must be enabled to use+-- this function.+deriveMemoizableParams ∷ Name → [Int] → Q [Dec]+deriveMemoizableParams n indices = deriveMemoizable' n (Just indices)++-- | In cases where neither 'deriveMemoizable' nor+-- 'deriveMemoizableParams' can figure out the right context for an+-- instance declaration, one can declare the instance manually and use+-- this function to derive the method body for 'memoize'. For example,+-- suppose that a data type @T@ is defined as:+--+-- @+-- data T a b = T (a -> Bool) b+-- @+--+-- For @T a b@ to be memoizable, @a -> Bool@ must be, and based on the+-- instance for '(->)', this means that @a@ must satisfy+-- 'Bounded' and 'Enum', so 'deriveMemoizable' cannot build the right+-- context for the 'Memoizable' instance. Instead, one can write:+--+-- @+-- instance ('Enum' a, 'Bounded' a, 'Memoizable' b) =>+-- 'Memoizable' (T a b) where+-- memoize = $(deriveMemoize ''T)+-- @+deriveMemoize ∷ Name → ExpQ+deriveMemoize name0 = do+ (_, _, cons) ← checkName name0+ buildMethodExp cons++-- | The main entry point delegates to check given type name, renames type+-- parameters, and generates the instance.+deriveMemoizable' ∷ Name → Maybe [Int] → Q [Dec]+deriveMemoizable' name0 mindices = do+ (name, tvbs, cons) ← checkName name0+ let tvs = freshNames tvbs+ inst ← instanceD+ (buildContext mindices tvbs tvs)+ (buildHead name tvs)+ [buildMethodDec cons]+ return [inst]++-- | Given the type name for the requested instance, checks if it+-- corresponds to a @data@ or @newtype@, and if so, returns the name,+-- a list of its parameters, and a list of constructor names with+-- their arities.+checkName ∷ Name → Q (Name, [TyVarBndr], [(Name, Int)])+checkName name0 = do+ info ← reify name0+ case info of+ TyConI (DataD _ name tvbs cons _)+ → return (name, tvbs, stdizeCon <$> cons)+ TyConI (NewtypeD _ name tvbs con _)+ → return (name, tvbs, [stdizeCon con])+ _ → fail $+ "deriveMemoizable: Can't derive a Memoizable instance for `" +++ show name0 ++ "' because it isn't a type constructor."+ where+ stdizeCon (NormalC name params) = (name, length params)+ stdizeCon (RecC name fields) = (name, length fields)+ stdizeCon (InfixC _ name _) = (name, 2)+ stdizeCon (ForallC _ _ con) = stdizeCon con++-- | Given a list, produces a list of nicely printable, distinct names.+-- Used so that instances print with nice parameters names, like+--+-- @+-- instance Memoizable (T a b c) where+-- @+--+-- instead of+--+-- @+-- instance Memoizable (T a[1] b[2] c32424534) where+-- @+freshNames ∷ [a] → [Name]+freshNames xs = take (length xs) alphabet+ where+ alphabet = [ mkName (c:s)+ | s ← "" : (show <$> [1 ∷ Integer ..])+ , c ← ['a' .. 'z'] ]++-- | Build the type class instance context, give the necessary+-- information to select which parameters to include. If the first+-- argument is @Just ixs@, then there should be 'Memoizable' instances+-- for exactly those parameters, by index, in the context. Otherwise,+-- choose the parameters that have no explicit kind or kind @*@ from the+-- list of binders. The third argument gives the actual type variable+-- names to use.+buildContext ∷ Maybe [Int] → [TyVarBndr] → [Name] → CxtQ+buildContext mindices tvbs tvs =+ cxt (classP ''Memoizable . (:[]) . varT <$> cxttvs)+ where+ cxttvs = case mindices of+ Just ixs → filterBy (`elem` ixs) [1 ..] tvs+ Nothing → filterBy isStar tvbs tvs+ --+ isStar (PlainTV _) = True+ isStar (KindedTV _ StarK) = True+ isStar (KindedTV _ _) = False+ --+ filterBy ∷ (a → Bool) → [a] → [b] → [b]+ filterBy p xs ys = snd <$> filter (p . fst) (zip xs ys)++-- | Build the 'Memoizable' instance head for the given type name+-- and parameter type variables.+buildHead ∷ Name → [Name] → TypeQ+buildHead name tvs = + appT (conT ''Memoizable) (foldl appT (conT name) (varT <$> tvs))++-- | Build the 'memoize' method. The form of 'memoize' is always+--+-- @+-- memoize f = lookup where+-- cache1 = memoize $ \x1 -> ... memoize $ \x(a1) -> f (C1 x1 ...)+-- ...+-- cacheN = memoize $ \x1 -> ... memoize $ \x(aN) -> f (CN x1 ...)+-- lookup (C1 x1 ...) = cache1 x1 ...+-- ...+-- lookup (CN xN ...) = cacheN xN ...+-- @+--+-- where @C1@ ... @CN@ are the constructors of the data type and+-- @aj@ is the arity of constructor @Cj@.+--+-- In this method, we allocate fresh names for the parameter @f@, the+-- lookup function, and the @N@ caches. We then delegate to build+-- the definitions of @look@ and the caches.+buildMethodDec ∷ [(Name, Int)] → DecQ+buildMethodDec cons = do+ valD (varP 'memoize)+ (normalB (buildMethodExp cons))+ []++-- | Build the body of the 'memoize' method, as described in the comment+-- above 'buildMethodDec'+buildMethodExp ∷ [(Name, Int)] → ExpQ+buildMethodExp cons = do+ f ← newName "f"+ look ← newName "look"+ caches ← mapM (newName . ("cache"++) . nameBase . fst) cons+ lam1E (varP f)+ (letE+ (buildLookup look cons caches+ : zipWith (buildCache f) cons caches)+ (varE look))++-- | Build the look function by building a clause for each constructor+-- of the datatype.+buildLookup ∷ Name → [(Name, Int)] → [Name] → DecQ+buildLookup look cons caches =+ funD look (zipWith buildLookupClause cons caches)++-- | Build a lookup clause for one constructor. We lookup a value+-- by matching that constructor and then passing its parameters to+-- the cache for that constructor.+buildLookupClause ∷ (Name, Int) → Name → ClauseQ+buildLookupClause (con, arity) cache = do+ params ← replicateM arity (newName "a")+ clause [conP con (varP <$> params)]+ (normalB (foldl appE (varE cache) (varE <$> params)))+ []++-- | Build the definition of a cache for the given constructor. We do+-- this by binding the cache name to a cascading sequence of+-- memoizations for each component in the constructor's arity.+buildCache ∷ Name → (Name, Int) → Name → DecQ+buildCache f (con, arity) cache =+ valD (varP cache) (normalB (composeMemos arity f (conE con))) []++-- | Given the remaining arity to memoize, the name of the function to+-- memoize, and the accumulated parameter so far, build the+-- memoization chain.+composeMemos ∷ Int → Name → ExpQ → ExpQ+composeMemos 0 f arg = [| $(varE f) $arg |]+composeMemos arity f arg = do+ [| memoize $ \b -> $(composeMemos (arity - 1) f [| $arg b |]) |]+
+ LICENSE view
@@ -0,0 +1,25 @@+Copyright (c) 2011, Jesse A. Tov+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:+ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.+ * Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.+ * Neither the name of the <organization> nor the+ names of its contributors may be used to endorse or promote products+ derived from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+
+ Setup.hs view
@@ -0,0 +1,4 @@+#!/usr/bin/env runhaskell++import Distribution.Simple+main = defaultMain
+ memoize.cabal view
@@ -0,0 +1,40 @@+name: memoize+version: 0.1+cabal-version: >= 1.6+license: BSD3+license-file: LICENSE+stability: experimental+author: Jesse A. Tov <tov@ccs.neu.edu>+maintainer: tov@ccs.neu.edu+category: Data+synopsis: A memoization library+build-type: Simple+tested-with: GHC == 7.0.2, GHC == 6.12.3++description:+ This library provides a type class 'Memoizable' for memoizing+ functions, along with instances for a variety of argument types.+ It includes a Template Haskell function for deriving+ 'Memoizable' instances for arbitrary algebraic datatypes.+ .+ The library constructs pure memo caches without the use of+ 'unsafePerformIO'. This technique relies on implementation+ assumptions that, while not guaranteed by the semantics of+ Haskell, appear to be true.++library+ build-depends: haskell98 >=1 && <2,+ base >=3 && <5,+ template-haskell >=2++ ghc-options: -Wall -fno-warn-orphans++ exposed-modules:+ Data.Function.Memoize+ other-modules:+ Data.Function.Memoize.TH+ Data.Function.Memoize.Class++source-repository head+ type: git+ location: git://github.com/tov/memoize.git