{-# language Safe #-}

module LazyAsync.Actions.Memoize where

import LazyAsync.Actions.Spawn     (lazyAsync)
import LazyAsync.Actions.StartWait (startWait)

import LazyAsync.Prelude (ContT, IO, MonadBaseControl, Traversable, fmap,
                          runContT, traverse)

import qualified LazyAsync.Libraries.Rank2 as Rank2

{- | Creates a situation wherein:

  * The action shall begin running only once the memoized action runs
  * The action shall run at most once
  * The action shall run only within the continuation (when the continuation ends, the action is stopped)
-}
memoize :: (MonadBaseControl IO m) =>
    m a -- ^ Action
    -> ContT r m (m a) -- ^ Memoized action, in a continuation
memoize :: m a -> ContT r m (m a)
memoize m a
action = (LazyAsync (StM m a) -> m a)
-> ContT r m (LazyAsync (StM m a)) -> ContT r m (m a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap LazyAsync (StM m a) -> m a
forall (base :: * -> *) (m :: * -> *) a.
(MonadBaseControl base m, MonadIO base) =>
LazyAsync (StM m a) -> m a
startWait (m a -> ContT r m (LazyAsync (StM m a))
forall (m :: * -> *) a r.
MonadBaseControl IO m =>
m a -> ContT r m (LazyAsync (StM m a))
lazyAsync m a
action)

-- | Akin to 'memoize'
withMemoizedIO :: IO a -> (IO a -> IO b) -> IO b
withMemoizedIO :: IO a -> (IO a -> IO b) -> IO b
withMemoizedIO IO a
action = ContT b IO (IO a) -> (IO a -> IO b) -> IO b
forall k (r :: k) (m :: k -> *) a. ContT r m a -> (a -> m r) -> m r
runContT (IO a -> ContT b IO (IO a)
forall (m :: * -> *) a r.
MonadBaseControl IO m =>
m a -> ContT r m (m a)
memoize IO a
action)

-- | 🌈 'memoizeMany' is equivalent to @('traverse' 'memoize')@
memoizeMany :: (MonadBaseControl IO m, Traversable t) => t (m a) -> ContT r m (t (m a))
memoizeMany :: t (m a) -> ContT r m (t (m a))
memoizeMany = (m a -> ContT r m (m a)) -> t (m a) -> ContT r m (t (m a))
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse m a -> ContT r m (m a)
forall (m :: * -> *) a r.
MonadBaseControl IO m =>
m a -> ContT r m (m a)
memoize

-- | 🌈 'memoizeRank2' is equivalent to @('Rank2.traverse' 'memoize')@
memoizeRank2 :: (MonadBaseControl IO m, Rank2.Traversable t) => t m -> ContT r m (t m)
memoizeRank2 :: t m -> ContT r m (t m)
memoizeRank2 = (forall a. m a -> ContT r m (m a)) -> t m -> ContT r m (t m)
forall k (g :: (k -> *) -> *) (m :: * -> *) (p :: k -> *)
       (q :: k -> *).
(Traversable g, Applicative m) =>
(forall (a :: k). p a -> m (q a)) -> g p -> m (g q)
Rank2.traverse forall a. m a -> ContT r m (m a)
forall (m :: * -> *) a r.
MonadBaseControl IO m =>
m a -> ContT r m (m a)
memoize

-- | Akin to 'memoizeMany'
withMemoizedListIO :: [IO a] -> ([IO a] -> IO b) -> IO b
withMemoizedListIO :: [IO a] -> ([IO a] -> IO b) -> IO b
withMemoizedListIO [IO a]
x = ContT b IO [IO a] -> ([IO a] -> IO b) -> IO b
forall k (r :: k) (m :: k -> *) a. ContT r m a -> (a -> m r) -> m r
runContT ([IO a] -> ContT b IO [IO a]
forall (m :: * -> *) (t :: * -> *) a r.
(MonadBaseControl IO m, Traversable t) =>
t (m a) -> ContT r m (t (m a))
memoizeMany [IO a]
x)