netwire-3.0.0: Control/Wire/Trans/Memoize.hs
-- |
-- Module: Control.Wire.Trans.Memoize
-- Copyright: (c) 2011 Ertugrul Soeylemez
-- License: BSD3
-- Maintainer: Ertugrul Soeylemez <es@ertes.de>
--
-- Memoizing wire transformers.
module Control.Wire.Trans.Memoize
( -- * Memoizing
WCache(..),
WPurify(..)
)
where
import Control.Arrow
import Control.Monad.Fix
import Control.Wire.Classes
import Control.Wire.TimedMap
import Control.Wire.Types
import Data.AdditiveGroup
-- | Remember the most recently produced values. You can limit both the
-- maximum age and the number of remembered values. The second input
-- value specifies the maximum age, the third specifies the maximum
-- number.
--
-- Note: Inhibtion is never remembered.
--
-- Note: Decreasing the size limit has O(n * log n) complexity, where n
-- is the difference to the old limit.
--
-- * Depends: Current instant.
--
-- * Inhibits: Whenever result is not cached and argument wire inhibits.
class Arrow (>~) => WCache t (>~) | (>~) -> t where
cache :: Ord a => Wire e (>~) a b -> Wire e (>~) ((a, t), Int) b
instance (AdditiveGroup t, MonadClock t m, Ord t) => WCache t (Kleisli m) where
cache = cache' tmEmpty
where
cache' :: Ord a => TimedMap t a b -> Wire e (Kleisli m) a b -> Wire e (Kleisli m) ((a, t), Int) b
cache' xs' w' =
WmGen $ \((x', maxAge), limit) -> do
t <- getTime
(mx, w) <-
case tmLookup x' xs' of
Nothing -> toGenM w' x'
Just x -> return (Right x, w')
let xs = tmLimitSize limit .
tmLimitAge (t ^-^ maxAge) .
either (const id) (tmInsert t x') mx $ xs'
return (mx, cache' xs w)
-- | Remember the last produced value. Whenever an input is repeated,
-- the argument wire is ignored and the memoized result is returned
-- instantly. Note: inhibition will not be remembered.
--
-- * Depends: Current instant.
--
-- * Inhibits: Like the argument wire for non-memoized inputs.
class Arrow (>~) => WPurify (>~) where
purify :: Eq a => Wire e (>~) a b -> Wire e (>~) a b
instance Monad m => WPurify (Kleisli m) where
purify w' =
case w' of
WmPure f ->
WmPure $ \x' ->
let (mx, w) = f x' in
(mx, either (const $ purify w) (\x -> purify' x' x w) mx)
WmGen c ->
WmGen $ \x' -> do
(mx, w) <- c x'
return (mx, either (const $ purify w) (\x -> purify' x' x w) mx)
where
purify' :: Eq a => a -> b -> Wire e (Kleisli m) a b -> Wire e (Kleisli m) a b
purify' x0' x0 =
fix $ \again w' ->
case w' of
WmPure f ->
WmPure $ \x' ->
if x' /= x0'
then
let (mx, w) = f x' in
(mx, either (const $ again w) (\x -> purify' x' x w) mx)
else (Right x0, again w')
WmGen c ->
WmGen $ \x' ->
if x' /= x0'
then do
(mx, w) <- c x'
return (mx, either (const $ again w) (\x -> purify' x' x w) mx)
else return (Right x0, again w')