memoize 0.8.1 → 1.0.0
raw patch · 5 files changed
+145/−61 lines, 5 filesdep ~basePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
- Data.Function.Memoize: instance Data.Function.Memoize.Class.Memoizable a => Data.Function.Memoize.Class.Memoizable (GHC.Base.Maybe a)
+ Data.Function.Memoize: instance Data.Function.Memoize.Class.Memoizable a => Data.Function.Memoize.Class.Memoizable (GHC.Maybe.Maybe a)
- Data.Function.Memoize: memoFix4 :: (Memoizable a, Memoizable b, Memoizable c, Memoizable d) => ((a -> b -> c -> d -> v) -> (a -> b -> c -> d -> v)) -> a -> b -> c -> d -> v
+ Data.Function.Memoize: memoFix4 :: (Memoizable a, Memoizable b, Memoizable c, Memoizable d) => ((a -> b -> c -> d -> v) -> a -> b -> c -> d -> v) -> a -> b -> c -> d -> v
- Data.Function.Memoize: 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
+ Data.Function.Memoize: 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
- Data.Function.Memoize: 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
+ Data.Function.Memoize: 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
- Data.Function.Memoize: 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
+ Data.Function.Memoize: 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
Files
- LICENSE +1/−1
- memoize.cabal +18/−7
- src/Data/Function/Memoize/TH.hs +87/−53
- test/Test3Helper.hs +3/−0
- test/test3.hs +36/−0
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2011, Jesse A. Tov+Copyright (c) 2011–2021, Jesse A. Tov All rights reserved. Redistribution and use in source and binary forms, with or without
memoize.cabal view
@@ -1,15 +1,15 @@+cabal-version: 2.2 name: memoize-version: 0.8.1-cabal-version: >= 1.8-license: BSD3+version: 1.0.0+license: BSD-3-Clause license-file: LICENSE stability: experimental-author: Jesse A. Tov <jesse@eecs.northwestern.edu>-maintainer: jesse@eecs.northwestern.edu+author: Jesse A. Tov <jesse.tov@gmail.com>+maintainer: jesse.tov@gmail.com category: Data synopsis: A memoization library build-type: Simple-tested-with: GHC == 7.10.3, GHC == 7.8.4, GHC == 7.6.3+tested-with: GHC == 9.0.1, GHC == 8.10.7, GHC == 8.6.5 description: This library provides a type class 'Memoizable' for memoizing@@ -25,6 +25,7 @@ library build-depends: base >=3 && <5, template-haskell >=2 && <3+ default-language: Haskell98 ghc-options: -Wall -fno-warn-orphans hs-source-dirs: src@@ -35,17 +36,27 @@ Data.Function.Memoize.Class test-suite memoize-test1+ Default-Language: Haskell98 Hs-Source-Dirs: test Type: exitcode-stdio-1.0 Main-is: test1.hs build-depends: base, memoize test-suite memoize-test2+ Default-Language: Haskell98 Hs-Source-Dirs: test Type: exitcode-stdio-1.0 Main-is: test2.hs build-depends: base, memoize- ++test-suite memoize-test3+ Default-Language: Haskell98+ Hs-Source-Dirs: test+ Other-Modules: Test3Helper+ Type: exitcode-stdio-1.0+ Main-is: test3.hs+ build-depends: base, memoize+ source-repository head type: git location: git://github.com/tov/memoize.git
src/Data/Function/Memoize/TH.hs view
@@ -15,11 +15,51 @@ #if __GLASGOW_HASKELL__ < 710 import Control.Applicative #endif+ import Control.Monad import Language.Haskell.TH- import Data.Function.Memoize.Class +---+--- `#DEFINE`S FOR VERSION COMPATIBILITY+---++-- GHC 7.6 changed to StarT from StarK:+#if __GLASGOW_HASKELL__ >= 706+# define COMPAT_STAR StarT+#else+# define COMPAT_STAR StarK+#endif++--- TH 2.10 treats type classes like type constructors:+#if MIN_VERSION_template_haskell(2,10,0)+# define COMPAT_CLASS_PRED(C) (appT (conT (C)) . varT)+#else+# define COMPAT_CLASS_PRED(C) (classP (C) . (:[]) . varT)+#endif++-- TH 2.11 supports GADTs and adds a field to NewtypeD and DataD:+#if MIN_VERSION_template_haskell(2,11,0)+# define COMPAT_TH_GADTS+# define COMPAT_NEWTYPE_D(N, T, C) (NewtypeD _ (N) (T) _ (C) _)+# define COMPAT_DATA_D(N, T, C) (DataD _ (N) (T) _ (C) _)+#else+# undef COMPAT_TH_GADTS+# define COMPAT_NEWTYPE_D(N, T, C) (NewtypeD _ (N) (T) (C) _)+# define COMPAT_DATA_D(N, T, C) (DataD _ (N) (T) (C) _)+#endif++-- GHC 9 adds a type parameter to the TyVarBndr type:+#if __GLASGOW_HASKELL__ >= 900+# define COMPAT_TY_VAR_BNDR(V) (TyVarBndr (V))+# define COMPAT_PLAIN_TV(N) (PlainTV (N) _)+# define COMPAT_KINDED_TV(N, K) (KindedTV (N) _ (K))+#else+# define COMPAT_TY_VAR_BNDR(V) TyVarBndr+# define COMPAT_PLAIN_TV(N) (PlainTV (N))+# define COMPAT_KINDED_TV(N, K) (KindedTV (N) (K))+#endif+ -- | -- To derive 'Memoizable' instances for the given data types. -- In the simplest usage, to derive 'Memoizable' for an algebraic@@ -91,7 +131,7 @@ -- context for the 'Memoizable' instance. Instead, one can write: -- -- @--- instance ('Enum' a, 'Bounded' a, 'Memoizable' b) =>+-- instance ('Eq' a, 'Enum' a, 'Bounded' a, 'Memoizable' b) => -- 'Memoizable' (T a b) where -- memoize = $(deriveMemoize ''T) -- @@@ -116,30 +156,31 @@ -- 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 ∷ Name → Q (Name, [COMPAT_TY_VAR_BNDR(())], [(Name, Int)]) checkName name0 = do- info ← reify name0- case info of-#if MIN_VERSION_template_haskell(2,11,0)- TyConI (DataD _ name tvbs _ cons _)-#else- TyConI (DataD _ name tvbs cons _)-#endif- → return (name, tvbs, stdizeCon <$> cons)-#if MIN_VERSION_template_haskell(2,11,0)- TyConI (NewtypeD _ name tvbs _ con _)-#else- TyConI (NewtypeD _ name tvbs con _)+ let can'tDerive = "deriveMemoizable: Can’t derive a Memoizable " +++ "instance for ‘" ++ show name0 ++ "’ because "+ can'tDeriveNonTC = can'tDerive ++ "it isn’t a type constructor."+ can'tDeriveGadt = can'tDerive ++ "GADTs aren’t supported."+ --+ stdizeCon (NormalC name params) = return (name, length params)+ stdizeCon (RecC name fields) = return (name, length fields)+ stdizeCon (InfixC _ name _) = return (name, 2)+ stdizeCon (ForallC _ _ con) = stdizeCon con+#ifdef COMPAT_TH_GADTS+ stdizeCon (GadtC _ _ _) = fail can'tDeriveGadt+ stdizeCon (RecGadtC _ _ _) = fail can'tDeriveGadt #endif- → 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+ --+ info ← reify name0+ case info of+ TyConI (COMPAT_DATA_D(name, tvbs, cons)) → do+ conInfos ← mapM stdizeCon cons+ return (name, tvbs, conInfos)+ TyConI (COMPAT_NEWTYPE_D(name, tvbs, con)) → do+ conInfo ← stdizeCon con+ return (name, tvbs, [conInfo])+ _ → fail can'tDeriveNonTC -- | Given a list, produces a list of nicely printable, distinct names. -- Used so that instances print with nice parameters names, like@@ -167,25 +208,17 @@ -- choose the parameters that have no explicit kind from the -- list of binders. The third argument gives the actual type variable -- names to use.-buildContext ∷ Maybe [Int] → [TyVarBndr] → [Name] → CxtQ+buildContext ∷ Maybe [Int] → [COMPAT_TY_VAR_BNDR(a)] → [Name] → CxtQ buildContext mindices tvbs tvs =-#if MIN_VERSION_template_haskell(2,10,0)- cxt (appT (conT ''Memoizable) . varT <$> cxttvs)-#else- cxt (classP ''Memoizable . (:[]) . varT <$> cxttvs)-#endif+ cxt (COMPAT_CLASS_PRED(''Memoizable) <$> cxttvs) where cxttvs = case mindices of Just ixs → filterBy (`elem` ixs) [1 ..] tvs Nothing → filterBy isStar tvbs tvs --- isStar (PlainTV _) = True-#if __GLASGOW_HASKELL__ >= 706- isStar (KindedTV _ StarT) = True-#else- isStar (KindedTV _ StarK) = True-#endif- isStar (KindedTV _ _) = False+ isStar (COMPAT_PLAIN_TV(_)) = True+ isStar (COMPAT_KINDED_TV(_, COMPAT_STAR)) = True+ isStar _ = False -- filterBy ∷ (a → Bool) → [a] → [b] → [b] filterBy p xs ys = snd <$> filter (p . fst) (zip xs ys)@@ -224,30 +257,31 @@ -- above 'buildMethodDec' buildMethodExp ∷ [(Name, Int)] → ExpQ buildMethodExp cons = do- f ← newName "f"- look ← newName "look"- caches ← mapM (\ _ -> newName "cache") cons+ f ← newName "fun"+ caches ← mapM (\_ → newName "cache") cons lam1E (varP f) (letE- (buildLookup look cons caches- : zipWith (buildCache f) cons caches)- (varE look))+ (zipWith (buildCache f) cons caches)+ (buildLookup cons caches)) -- | 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)+buildLookup ∷ [(Name, Int)] → [Name] → ExpQ+buildLookup cons caches = do+ a ← newName "arg"+ lam1E (varP a) .+ caseE (varE a) $+ zipWith buildLookupMatch 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)))- []+buildLookupMatch ∷ (Name, Int) → Name → MatchQ+buildLookupMatch (con, arity) cache = do+ params ← replicateM arity (newName "param")+ match (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@@ -262,5 +296,5 @@ 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 |]) |]+ [| memoize $ \b → $(composeMemos (arity - 1) f [| $arg b |]) |]
+ test/Test3Helper.hs view
@@ -0,0 +1,3 @@+module Test3Helper where++data NonstandardParams a b = NonstandardParams (a -> Bool) b
+ test/test3.hs view
@@ -0,0 +1,36 @@+{-# language TemplateHaskell, GADTs #-}++import Data.Function.Memoize+import Control.Monad (forM_, when)+import Test3Helper++-- NonstandardParams is defined by:+--+-- data NonstandardParams a b+-- = NonstandardParams (a -> Bool) b+--+-- This won’t compile because it needs addition typeclass constraints in+-- the instance context:+--+-- $(deriveMemoizable ''NonstandardParams)++instance (Eq a, Enum a, Bounded a, Memoizable b) => Memoizable (NonstandardParams a b) where+ memoize = $(deriveMemoize ''NonstandardParams)++applyToLength :: NonstandardParams Bool Int -> Bool+applyToLength (NonstandardParams f z) = f (odd z)++cases = [ (NonstandardParams id 5, True)+ , (NonstandardParams id 6, False)+ , (NonstandardParams not 5, False)+ , (NonstandardParams not 6, True)+ ]++main :: IO ()+main = do+ let memoized = memoize applyToLength+ forM_ cases $ \(input, expected) -> do+ let actual = applyToLength input+ when (actual /= expected) $+ fail $ "Test failed: got " ++ show actual +++ " when " ++ show expected ++ " expected."