Shpadoinkle-html-0.2.0.0: Shpadoinkle/Html/Memo.hs
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE FlexibleInstances #-}
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
{-|
The memo function remembers the last argument passed and the last result.
When a new value is passed, it compares it with the previous value and if they match,
the previous result is returned from memory. This is useful when computing Html
is expensive and benefits from memoization.
-}
module Shpadoinkle.Html.Memo (
-- * Variadic Class
Memo (..)
-- * Uniadic
, memo1, memo2, memo3, memo4, memo5, memo6, memo7, memo8, memo9
-- * Custom Equality
, memo1', memo2', memo3', memo4', memo5', memo6', memo7', memo8', memo9'
) where
import Data.IORef
import System.IO.Unsafe
{-|
Variadic ditzy memoizer that only recalls at most one thing.
prop> memo = id
-}
class Memo f where memo :: f -> f
instance Eq a => Memo (a -> b) where memo = memo1
instance {-# OVERLAPS #-} (Eq a, Eq b) => Memo (a -> b -> c) where memo = memo2
instance {-# OVERLAPS #-} (Eq a, Eq b, Eq c) => Memo (a -> b -> c -> d) where memo = memo3
instance {-# OVERLAPS #-} (Eq a, Eq b, Eq c, Eq d) => Memo (a -> b -> c -> d -> e) where memo = memo4
instance {-# OVERLAPS #-} (Eq a, Eq b, Eq c, Eq d, Eq e) => Memo (a -> b -> c -> d -> e -> f) where memo = memo5
instance {-# OVERLAPS #-} (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f) => Memo (a -> b -> c -> d -> e -> f -> g) where memo = memo6
instance {-# OVERLAPS #-} (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g) => Memo (a -> b -> c -> d -> e -> f -> g -> h) where memo = memo7
instance {-# OVERLAPS #-} (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h) => Memo (a -> b -> c -> d -> e -> f -> g -> h -> i) where memo = memo8
instance {-# OVERLAPS #-} (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i) => Memo (a -> b -> c -> d -> e -> f -> g -> h -> i -> j) where memo = memo9
memo1' e f a = unsafePerformIO $ do
r <- newIORef (a, f a)
return $ applyEq e f r a
memo2' e f a b = memo1' e (uncurry f) (a,b)
memo3' e f a b c = memo1' e (uncurry2 f) (a,b,c)
memo4' e f a b c d = memo1' e (uncurry3 f) (a,b,c,d)
memo5' e f a b c d g = memo1' e (uncurry4 f) (a,b,c,d,g)
memo6' e f a b c d g h = memo1' e (uncurry5 f) (a,b,c,d,g,h)
memo7' e f a b c d g h i = memo1' e (uncurry6 f) (a,b,c,d,g,h,i)
memo8' e f a b c d g h i j = memo1' e (uncurry7 f) (a,b,c,d,g,h,i,j)
memo9' e f a b c d g h i j k = memo1' e (uncurry8 f) (a,b,c,d,g,h,i,j,k)
memo1 :: Eq a => (a -> b) -> a -> b
memo2 :: Eq a => Eq b => (a -> b -> c) -> a -> b -> c
memo3 :: Eq a => Eq b => Eq c => (a -> b -> c -> d) -> a -> b -> c -> d
memo4 :: Eq a => Eq b => Eq c => Eq d => (a -> b -> c -> d -> e) -> a -> b -> c -> d -> e
memo5 :: Eq a => Eq b => Eq c => Eq d => Eq e => (a -> b -> c -> d -> e -> f) -> a -> b -> c -> d -> e -> f
memo6 :: Eq a => Eq b => Eq c => Eq d => Eq e => Eq f => (a -> b -> c -> d -> e -> f -> g) -> a -> b -> c -> d -> e -> f -> g
memo7 :: Eq a => Eq b => Eq c => Eq d => Eq e => Eq f => Eq g => (a -> b -> c -> d -> e -> f -> g -> h) -> a -> b -> c -> d -> e -> f -> g -> h
memo8 :: Eq a => Eq b => Eq c => Eq d => Eq e => Eq f => Eq g => Eq h => (a -> b -> c -> d -> e -> f -> g -> h -> i) -> a -> b -> c -> d -> e -> f -> g -> h -> i
memo9 :: Eq a => Eq b => Eq c => Eq d => Eq e => Eq f => Eq g => Eq h => Eq i => (a -> b -> c -> d -> e -> f -> g -> h -> i -> j) -> a -> b -> c -> d -> e -> f -> g -> h -> i -> j
memo1 = memo1' (/=)
memo2 = memo2' (/=)
memo3 = memo3' (/=)
memo4 = memo4' (/=)
memo5 = memo5' (/=)
memo6 = memo6' (/=)
memo7 = memo7' (/=)
memo8 = memo8' (/=)
memo9 = memo9' (/=)
uncurry2 f (a,b,c) = f a b c
uncurry3 f (a,b,c,d) = f a b c d
uncurry4 f (a,b,c,d,e) = f a b c d e
uncurry5 f (a,b,c,d,e,g) = f a b c d e g
uncurry6 f (a,b,c,d,e,g,h) = f a b c d e g h
uncurry7 f (a,b,c,d,e,g,h,i) = f a b c d e g h i
uncurry8 f (a,b,c,d,e,g,h,i,j) = f a b c d e g h i j
applyEq :: (a -> a -> Bool) -> (a -> b) -> IORef (a, b) -> a -> b
applyEq e f r a = unsafePerformIO $ do
(a', b) <- readIORef r
if not $ e a' a then return b else do
let b' = f a
writeIORef r (a', b')
return b'