-- |
-- Copyright:  (c) 2016 Ertugrul Söylemez
-- License:    BSD3
-- Maintainer: Markus Läll <markus.l2ll@gmail.com>
-- Stability:  experimental
--
-- This module provides a rapid prototyping suite for GHCi that can be
-- used standalone or integrated into editors.  You can hot-reload
-- individual running components as you make changes to their code.  It
-- is designed to shorten the development cycle during the development
-- of long-running programs like servers, web applications and
-- interactive user interfaces.
--
-- It can also be used in the context of batch-style programs:  Keep
-- resources that are expensive to create in memory and reuse them
-- across module reloads instead of reloading/recomputing them after
-- every code change.
--
-- Technically this package is a safe and convenient wrapper around
-- <https://hackage.haskell.org/package/foreign-store foreign-store>.
--
-- __Read the "Safety and securty" section before using this module!__

{-# LANGUAGE RankNTypes #-}

module Rapid
    ( -- * Introduction
      -- $intro

      -- ** Communication
      -- $communication

      -- ** Reusing expensive resources
      -- $reusing

      -- ** Cabal notes
      -- $cabal

      -- ** Emacs integration
      -- $emacs

      -- ** Safety and security
      -- $safety

      -- * Hot code reloading
      Rapid,
      rapid,

      -- * Threads
      restart,
      restartWith,
      start,
      startWith,
      stop,

      -- * Communication
      createRef,
      deleteRef,
      writeRef
    )
    where

import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Exception
import Data.Dynamic
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Data.Word
import Foreign.Store


-- | Handle to the current Rapid state.

data Rapid k =
    Rapid {
      forall k. Rapid k -> TVar Bool
rLock    :: TVar Bool,               -- ^ Lock on the current state.
      forall k. Rapid k -> TVar (Map k Dynamic)
rRefs    :: TVar (Map k Dynamic),    -- ^ Mutable variables.
      forall k. Rapid k -> TVar (Map k (Async ()))
rThreads :: TVar (Map k (Async ()))  -- ^ Active threads.
    }


-- | Cancel the given thread and wait for it to finish.

cancelAndWait :: Async a -> IO ()
cancelAndWait :: forall a. Async a -> IO ()
cancelAndWait Async a
tv = do
    Async a -> IO ()
forall a. Async a -> IO ()
cancel Async a
tv
    () () -> IO (Either SomeException a) -> IO ()
forall a b. a -> IO b -> IO a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Async a -> IO (Either SomeException a)
forall a. Async a -> IO (Either SomeException a)
waitCatch Async a
tv


-- | Get the value of the mutable variable with the given name.  If it
-- does not exist, it is created and initialised with the value returned
-- by the given action.
--
-- Mutable variables should only be used with values that can be
-- garbage-collected, for example communication primitives like
-- 'Control.Concurrent.MVar.MVar' and 'TVar', but also pure run-time
-- information that is expensive to generate, for example the parsed
-- contents of a file.

createRef
    :: (Ord k, Typeable a)
    => Rapid k  -- ^ Rapid state handle.
    -> k        -- ^ Name of the mutable variable.
    -> IO a     -- ^ Action to create.
    -> IO a
createRef :: forall k a. (Ord k, Typeable a) => Rapid k -> k -> IO a -> IO a
createRef Rapid k
r k
k IO a
gen =
    Rapid k -> k -> (Maybe Dynamic -> IO (Maybe Dynamic, a)) -> IO a
forall k a.
Ord k =>
Rapid k -> k -> (Maybe Dynamic -> IO (Maybe Dynamic, a)) -> IO a
withRef Rapid k
r k
k ((Maybe Dynamic -> IO (Maybe Dynamic, a)) -> IO a)
-> (Maybe Dynamic -> IO (Maybe Dynamic, a)) -> IO a
forall a b. (a -> b) -> a -> b
$ \Maybe Dynamic
mxd ->
        case Maybe Dynamic
mxd of
          Maybe Dynamic
Nothing -> (a -> (Maybe Dynamic, a)) -> IO a -> IO (Maybe Dynamic, a)
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\a
x -> (Dynamic -> Maybe Dynamic
forall a. a -> Maybe a
Just (a -> Dynamic
forall a. Typeable a => a -> Dynamic
toDyn a
x), a
x)) IO a
gen
          Just Dynamic
xd
              | Just a
x <- Dynamic -> Maybe a
forall a. Typeable a => Dynamic -> Maybe a
fromDynamic Dynamic
xd -> (Maybe Dynamic, a) -> IO (Maybe Dynamic, a)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Dynamic -> Maybe Dynamic
forall a. a -> Maybe a
Just Dynamic
xd, a
x)
              | Bool
otherwise -> IOError -> IO (Maybe Dynamic, a)
forall e a. Exception e => e -> IO a
throwIO (String -> IOError
userError String
"createRef: Wrong reference type")


-- | Delete the mutable variable with the given name, if it exists.

deleteRef
    :: (Ord k)
    => Rapid k  -- ^ Rapid state handle.
    -> k        -- ^ Name of the mutable variable.
    -> IO ()
deleteRef :: forall k. Ord k => Rapid k -> k -> IO ()
deleteRef Rapid k
r k
k =
    Rapid k -> k -> (Maybe Dynamic -> IO (Maybe Dynamic, ())) -> IO ()
forall k a.
Ord k =>
Rapid k -> k -> (Maybe Dynamic -> IO (Maybe Dynamic, a)) -> IO a
withRef Rapid k
r k
k (\Maybe Dynamic
_ -> (Maybe Dynamic, ()) -> IO (Maybe Dynamic, ())
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe Dynamic
forall a. Maybe a
Nothing, ()))


-- | Retrieve the current Rapid state handle, and pass it to the given
-- continuation.  If the state handle doesn't exist, it is created.  The
-- key type @k@ is used for naming reloadable services like threads.
--
-- __Warning__: The key type must not change during a session.  If you
-- need to change the key type, currently the safest option is to
-- restart GHCi.
--
-- This function uses the
-- <https://hackage.haskell.org/package/foreign-store foreign-store library>
-- to establish a state handle that survives GHCi reloads and is
-- suitable for hot reloading.
--
-- The first argument is the 'Store' index.  If you do not use the
-- /foreign-store/ library in your development workflow, just use 0,
-- otherwise use any unused index.

rapid
    :: forall k r.
       Word32             -- ^ Store index (if in doubt, use 0).
    -> (Rapid k -> IO r)  -- ^ Action on the Rapid state.
    -> IO r
rapid :: forall k r. Word32 -> (Rapid k -> IO r) -> IO r
rapid Word32
stNum Rapid k -> IO r
k =
    ((forall a. IO a -> IO a) -> IO r) -> IO r
forall b. ((forall a. IO a -> IO a) -> IO b) -> IO b
mask (((forall a. IO a -> IO a) -> IO r) -> IO r)
-> ((forall a. IO a -> IO a) -> IO r) -> IO r
forall a b. (a -> b) -> a -> b
$ \forall a. IO a -> IO a
unmask ->
        Word32 -> IO (Maybe (Store Any))
forall a. Word32 -> IO (Maybe (Store a))
lookupStore Word32
stNum IO (Maybe (Store Any))
-> (Maybe (Store Any) -> IO (Rapid k)) -> IO (Rapid k)
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
        IO (Rapid k)
-> (Store Any -> IO (Rapid k)) -> Maybe (Store Any) -> IO (Rapid k)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Store (Rapid k) -> IO (Rapid k) -> IO (Rapid k)
forall a. Store a -> IO a -> IO a
storeAction Store (Rapid k)
forall k. Store (Rapid k)
store IO (Rapid k)
forall {k}. IO (Rapid k)
create)
              (\Store Any
_ -> Store (Rapid k) -> IO (Rapid k)
forall a. Store a -> IO a
readStore Store (Rapid k)
forall k. Store (Rapid k)
store) IO (Rapid k) -> (Rapid k -> IO r) -> IO r
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
        (IO r -> IO r) -> Rapid k -> IO r
forall {b}. (IO r -> IO b) -> Rapid k -> IO b
pass IO r -> IO r
forall a. IO a -> IO a
unmask

    where
    create :: IO (Rapid k)
create =
        (TVar Bool
 -> TVar (Map k Dynamic) -> TVar (Map k (Async ())) -> Rapid k)
-> IO
     (TVar Bool
      -> TVar (Map k Dynamic) -> TVar (Map k (Async ())) -> Rapid k)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure TVar Bool
-> TVar (Map k Dynamic) -> TVar (Map k (Async ())) -> Rapid k
forall k.
TVar Bool
-> TVar (Map k Dynamic) -> TVar (Map k (Async ())) -> Rapid k
Rapid
        IO
  (TVar Bool
   -> TVar (Map k Dynamic) -> TVar (Map k (Async ())) -> Rapid k)
-> IO (TVar Bool)
-> IO (TVar (Map k Dynamic) -> TVar (Map k (Async ())) -> Rapid k)
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Bool -> IO (TVar Bool)
forall a. a -> IO (TVar a)
newTVarIO Bool
False
        IO (TVar (Map k Dynamic) -> TVar (Map k (Async ())) -> Rapid k)
-> IO (TVar (Map k Dynamic))
-> IO (TVar (Map k (Async ())) -> Rapid k)
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Map k Dynamic -> IO (TVar (Map k Dynamic))
forall a. a -> IO (TVar a)
newTVarIO Map k Dynamic
forall k a. Map k a
M.empty
        IO (TVar (Map k (Async ())) -> Rapid k)
-> IO (TVar (Map k (Async ()))) -> IO (Rapid k)
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Map k (Async ()) -> IO (TVar (Map k (Async ())))
forall a. a -> IO (TVar a)
newTVarIO Map k (Async ())
forall k a. Map k a
M.empty

    pass :: (IO r -> IO b) -> Rapid k -> IO b
pass IO r -> IO b
unmask Rapid k
r = do
        STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
            TVar Bool -> STM Bool
forall a. TVar a -> STM a
readTVar (Rapid k -> TVar Bool
forall k. Rapid k -> TVar Bool
rLock Rapid k
r) STM Bool -> (Bool -> STM ()) -> STM ()
forall a b. STM a -> (a -> STM b) -> STM b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Bool -> STM ()
check (Bool -> STM ()) -> (Bool -> Bool) -> Bool -> STM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not
            TVar Bool -> Bool -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar (Rapid k -> TVar Bool
forall k. Rapid k -> TVar Bool
rLock Rapid k
r) Bool
True
        IO r -> IO b
unmask (Rapid k -> IO r
k Rapid k
r) IO b -> IO () -> IO b
forall a b. IO a -> IO b -> IO a
`finally` STM () -> IO ()
forall a. STM a -> IO a
atomically (TVar Bool -> Bool -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar (Rapid k -> TVar Bool
forall k. Rapid k -> TVar Bool
rLock Rapid k
r) Bool
False)

    store :: Store (Rapid k)
    store :: forall k. Store (Rapid k)
store = Word32 -> Store (Rapid k)
forall a. Word32 -> Store a
Store Word32
stNum


-- | Create a thread with the given name that runs the given action.
--
-- The thread is restarted each time an update occurs.

restart
    :: (Ord k)
    => Rapid k  -- ^ Rapid state handle.
    -> k        -- ^ Name of the thread.
    -> IO ()    -- ^ Action the thread runs.
    -> IO ()
restart :: forall k. Ord k => Rapid k -> k -> IO () -> IO ()
restart = (forall a. IO a -> IO (Async a)) -> Rapid k -> k -> IO () -> IO ()
forall k.
Ord k =>
(forall a. IO a -> IO (Async a)) -> Rapid k -> k -> IO () -> IO ()
restartWith IO a -> IO (Async a)
forall a. IO a -> IO (Async a)
async


-- | Create a thread with the given name that runs the given action.
--
-- The thread is restarted each time an update occurs.
--
-- The first argument is the function used to create the thread.  It can
-- be used to select between 'async', 'asyncBound' and 'asyncOn'.

restartWith
    :: (Ord k)
    => (forall a. IO a -> IO (Async a))  -- ^ Thread creation function.
    -> Rapid k  -- ^ Rapid state handle.
    -> k        -- ^ Name of the thread.
    -> IO ()    -- ^ Action the thread runs.
    -> IO ()
restartWith :: forall k.
Ord k =>
(forall a. IO a -> IO (Async a)) -> Rapid k -> k -> IO () -> IO ()
restartWith forall a. IO a -> IO (Async a)
myAsync Rapid k
r k
k IO ()
action =
    Rapid k
-> k -> (Maybe (Async ()) -> IO (Maybe (Async ()))) -> IO ()
forall k.
Ord k =>
Rapid k
-> k -> (Maybe (Async ()) -> IO (Maybe (Async ()))) -> IO ()
withThread Rapid k
r k
k ((Maybe (Async ()) -> IO (Maybe (Async ()))) -> IO ())
-> (Maybe (Async ()) -> IO (Maybe (Async ()))) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Maybe (Async ())
mtv -> do
        (Async () -> IO ()) -> Maybe (Async ()) -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Async () -> IO ()
forall a. Async a -> IO ()
cancelAndWait Maybe (Async ())
mtv
        Async () -> Maybe (Async ())
forall a. a -> Maybe a
Just (Async () -> Maybe (Async ()))
-> IO (Async ()) -> IO (Maybe (Async ()))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO () -> IO (Async ())
forall a. IO a -> IO (Async a)
myAsync IO ()
action


-- | Create a thread with the given name that runs the given action.
--
-- When an update occurs and the thread is currently not running, it is
-- started.

start
    :: (Ord k)
    => Rapid k  -- ^ Rapid state handle.
    -> k        -- ^ Name of the thread.
    -> IO ()    -- ^ Action the thread runs.
    -> IO ()
start :: forall k. Ord k => Rapid k -> k -> IO () -> IO ()
start = (forall a. IO a -> IO (Async a)) -> Rapid k -> k -> IO () -> IO ()
forall k.
Ord k =>
(forall a. IO a -> IO (Async a)) -> Rapid k -> k -> IO () -> IO ()
startWith IO a -> IO (Async a)
forall a. IO a -> IO (Async a)
async


-- | Create a thread with the given name that runs the given action.
--
-- When an update occurs and the thread is currently not running, it is
-- started.
--
-- The first argument is the function used to create the thread.  It can
-- be used to select between 'async', 'asyncBound' and 'asyncOn'.

startWith
    :: (Ord k)
    => (forall a. IO a -> IO (Async a))  -- ^ Thread creation function.
    -> Rapid k  -- ^ Rapid state handle.
    -> k        -- ^ Name of the thread.
    -> IO ()    -- ^ Action the thread runs.
    -> IO ()
startWith :: forall k.
Ord k =>
(forall a. IO a -> IO (Async a)) -> Rapid k -> k -> IO () -> IO ()
startWith forall a. IO a -> IO (Async a)
myAsync Rapid k
r k
k IO ()
action =
    Rapid k
-> k -> (Maybe (Async ()) -> IO (Maybe (Async ()))) -> IO ()
forall k.
Ord k =>
Rapid k
-> k -> (Maybe (Async ()) -> IO (Maybe (Async ()))) -> IO ()
withThread Rapid k
r k
k ((Maybe (Async ()) -> IO (Maybe (Async ()))) -> IO ())
-> (Maybe (Async ()) -> IO (Maybe (Async ()))) -> IO ()
forall a b. (a -> b) -> a -> b
$
        IO (Maybe (Async ()))
-> (Async () -> IO (Maybe (Async ())))
-> Maybe (Async ())
-> IO (Maybe (Async ()))
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Async () -> Maybe (Async ())
forall a. a -> Maybe a
Just (Async () -> Maybe (Async ()))
-> IO (Async ()) -> IO (Maybe (Async ()))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO () -> IO (Async ())
forall a. IO a -> IO (Async a)
myAsync IO ()
action)
              (\Async ()
tv -> Async () -> IO (Maybe (Either SomeException ()))
forall a. Async a -> IO (Maybe (Either SomeException a))
poll Async ()
tv IO (Maybe (Either SomeException ()))
-> (Maybe (Either SomeException ()) -> IO (Maybe (Async ())))
-> IO (Maybe (Async ()))
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
                      IO (Maybe (Async ()))
-> (Either SomeException () -> IO (Maybe (Async ())))
-> Maybe (Either SomeException ())
-> IO (Maybe (Async ()))
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Maybe (Async ()) -> IO (Maybe (Async ()))
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Async () -> Maybe (Async ())
forall a. a -> Maybe a
Just Async ()
tv))
                            (\Either SomeException ()
_ -> Async () -> Maybe (Async ())
forall a. a -> Maybe a
Just (Async () -> Maybe (Async ()))
-> IO (Async ()) -> IO (Maybe (Async ()))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO () -> IO (Async ())
forall a. IO a -> IO (Async a)
myAsync IO ()
action))


-- | Delete the thread with the given name.
--
-- When an update occurs and the thread is currently running, it is
-- cancelled.

stop :: (Ord k) => Rapid k -> k -> x -> IO ()
stop :: forall k x. Ord k => Rapid k -> k -> x -> IO ()
stop Rapid k
r k
k x
_ =
    Rapid k
-> k -> (Maybe (Async ()) -> IO (Maybe (Async ()))) -> IO ()
forall k.
Ord k =>
Rapid k
-> k -> (Maybe (Async ()) -> IO (Maybe (Async ()))) -> IO ()
withThread Rapid k
r k
k ((Maybe (Async ()) -> IO (Maybe (Async ()))) -> IO ())
-> (Maybe (Async ()) -> IO (Maybe (Async ()))) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Maybe (Async ())
mtv ->
        Maybe (Async ())
forall a. Maybe a
Nothing Maybe (Async ()) -> IO () -> IO (Maybe (Async ()))
forall a b. a -> IO b -> IO a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ (Async () -> IO ()) -> Maybe (Async ()) -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Async () -> IO ()
forall a. Async a -> IO ()
cancelAndWait Maybe (Async ())
mtv


-- | Apply the given transform to the reference with the given name.

withRef
    :: (Ord k)
    => Rapid k
    -> k
    -> (Maybe Dynamic -> IO (Maybe Dynamic, a))
    -> IO a
withRef :: forall k a.
Ord k =>
Rapid k -> k -> (Maybe Dynamic -> IO (Maybe Dynamic, a)) -> IO a
withRef Rapid k
r k
k Maybe Dynamic -> IO (Maybe Dynamic, a)
f = do
    (Maybe Dynamic
mx, a
y) <- STM (Maybe Dynamic) -> IO (Maybe Dynamic)
forall a. STM a -> IO a
atomically (k -> Map k Dynamic -> Maybe Dynamic
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup k
k (Map k Dynamic -> Maybe Dynamic)
-> STM (Map k Dynamic) -> STM (Maybe Dynamic)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TVar (Map k Dynamic) -> STM (Map k Dynamic)
forall a. TVar a -> STM a
readTVar (Rapid k -> TVar (Map k Dynamic)
forall k. Rapid k -> TVar (Map k Dynamic)
rRefs Rapid k
r)) IO (Maybe Dynamic)
-> (Maybe Dynamic -> IO (Maybe Dynamic, a))
-> IO (Maybe Dynamic, a)
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
               Maybe Dynamic -> IO (Maybe Dynamic, a)
f
    STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ TVar (Map k Dynamic) -> (Map k Dynamic -> Map k Dynamic) -> STM ()
forall a. TVar a -> (a -> a) -> STM ()
modifyTVar' (Rapid k -> TVar (Map k Dynamic)
forall k. Rapid k -> TVar (Map k Dynamic)
rRefs Rapid k
r) ((Map k Dynamic -> Map k Dynamic)
-> (Dynamic -> Map k Dynamic -> Map k Dynamic)
-> Maybe Dynamic
-> Map k Dynamic
-> Map k Dynamic
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (k -> Map k Dynamic -> Map k Dynamic
forall k a. Ord k => k -> Map k a -> Map k a
M.delete k
k) (k -> Dynamic -> Map k Dynamic -> Map k Dynamic
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert k
k) Maybe Dynamic
mx)
    a -> IO a
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
y


-- | Apply the given transform to the thread with the given name.

withThread
    :: (Ord k)
    => Rapid k
    -> k
    -> (Maybe (Async ()) -> IO (Maybe (Async ())))
    -> IO ()
withThread :: forall k.
Ord k =>
Rapid k
-> k -> (Maybe (Async ()) -> IO (Maybe (Async ()))) -> IO ()
withThread Rapid k
r k
k Maybe (Async ()) -> IO (Maybe (Async ()))
f =
    STM (Maybe (Async ())) -> IO (Maybe (Async ()))
forall a. STM a -> IO a
atomically (k -> Map k (Async ()) -> Maybe (Async ())
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup k
k (Map k (Async ()) -> Maybe (Async ()))
-> STM (Map k (Async ())) -> STM (Maybe (Async ()))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TVar (Map k (Async ())) -> STM (Map k (Async ()))
forall a. TVar a -> STM a
readTVar (Rapid k -> TVar (Map k (Async ()))
forall k. Rapid k -> TVar (Map k (Async ()))
rThreads Rapid k
r)) IO (Maybe (Async ()))
-> (Maybe (Async ()) -> IO (Maybe (Async ())))
-> IO (Maybe (Async ()))
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
    Maybe (Async ()) -> IO (Maybe (Async ()))
f IO (Maybe (Async ())) -> (Maybe (Async ()) -> IO ()) -> IO ()
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
    STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ())
-> (Maybe (Async ()) -> STM ()) -> Maybe (Async ()) -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TVar (Map k (Async ()))
-> (Map k (Async ()) -> Map k (Async ())) -> STM ()
forall a. TVar a -> (a -> a) -> STM ()
modifyTVar' (Rapid k -> TVar (Map k (Async ()))
forall k. Rapid k -> TVar (Map k (Async ()))
rThreads Rapid k
r) ((Map k (Async ()) -> Map k (Async ())) -> STM ())
-> (Maybe (Async ()) -> Map k (Async ()) -> Map k (Async ()))
-> Maybe (Async ())
-> STM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Map k (Async ()) -> Map k (Async ()))
-> (Async () -> Map k (Async ()) -> Map k (Async ()))
-> Maybe (Async ())
-> Map k (Async ())
-> Map k (Async ())
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (k -> Map k (Async ()) -> Map k (Async ())
forall k a. Ord k => k -> Map k a -> Map k a
M.delete k
k) (k -> Async () -> Map k (Async ()) -> Map k (Async ())
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert k
k)


-- | Overwrite the mutable variable with the given name with the value
-- returned by the given action.  If the mutable variable does not
-- exist, it is created.
--
-- This function may be used to change the value type of a mutable
-- variable.

writeRef
    :: (Ord k, Typeable a)
    => Rapid k  -- ^ Rapid state handle.
    -> k        -- ^ Name of the mutable variable.
    -> IO a     -- ^ Value action.
    -> IO a
writeRef :: forall k a. (Ord k, Typeable a) => Rapid k -> k -> IO a -> IO a
writeRef Rapid k
r k
k IO a
gen =
    Rapid k -> k -> (Maybe Dynamic -> IO (Maybe Dynamic, a)) -> IO a
forall k a.
Ord k =>
Rapid k -> k -> (Maybe Dynamic -> IO (Maybe Dynamic, a)) -> IO a
withRef Rapid k
r k
k ((Maybe Dynamic -> IO (Maybe Dynamic, a)) -> IO a)
-> (Maybe Dynamic -> IO (Maybe Dynamic, a)) -> IO a
forall a b. (a -> b) -> a -> b
$ \Maybe Dynamic
_ ->
        (a -> (Maybe Dynamic, a)) -> IO a -> IO (Maybe Dynamic, a)
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\a
x -> (Dynamic -> Maybe Dynamic
forall a. a -> Maybe a
Just (a -> Dynamic
forall a. Typeable a => a -> Dynamic
toDyn a
x), a
x)) IO a
gen


{- $cabal

In general a Cabal project should not have this library as a build-time
dependency.  However, in certain environments (like Nix-based
development) it may be beneficial to include it in the @.cabal@ file
regardless.  A simple solution is to add a flag:

> flag Devel
>     default: False
>     description: Enable development dependencies
>     manual: True
>
> library
>     build-depends:
>         base >= 4.8 && < 5,
>         {- ... -}
>     if flag(devel)
>         build-depends: rapid
>     {- ... -}

Now you can configure your project with @-fdevel@ during development and
have this module available.

-}


{- $communication

If you need your background threads to communicate with each other, for
example by using concurrency primitives, some additional support is
required.  You cannot just create a 'TVar' within your @update@ action.
It would be a different one for every invocation, so threads that are
restarted would not communicate with already running threads, because
they would use a fresh @TVar@, while the old threads would still use the
old one.

To solve this, you need to wrap your 'newTVar' action with 'createRef'.
The @TVar@ created this way will survive reloads in the same way as
background threads do.  In particular, if there is already one from an
older invocation of @update@, it will be reused:

> import Control.Concurrent.STM
> import Control.Monad
> import Rapid
>
> update =
>     rapid 0 $ \r -> do
>         mv1 <- createRef r "var1" newEmptyTMVarIO
>         mv2 <- createRef r "var2" newEmptyTMVarIO
>
>         start r "producer" $
>             mapM_ (atomically . putTMVar mv1) [0 :: Integer ..]
>
>         restart r "consumer" $
>             forever . atomically $ do
>                 x <- takeTMVar mv1
>                 putTMVar mv2 (x, "blah")
>
>         -- For debugging the update action:
>         replicateM_ 3 $
>             atomically (takeTMVar mv2) >>= print

You can now change the string @"blah"@ in the consumer thread and then
run @update@.  You will notice that the numbers in the left component of
the tuples keep increasing even after a reload, while the string in the
right component changes.  That means the producer thread was not
restarted, but the consumer thread was.  Yet the restarted consumer
thread still refers to the same @TVar@ as before, so it still receives
from the producer.

-}


{- $emacs

This library integrates well with
<https://haskell.github.io/haskell-mode/manual/latest/Interactive-Haskell.html haskell-interactive-mode>,
particularly with its somewhat hidden
@haskell-process-reload-devel-main@ function.

This function finds your @DevelMain@ module by looking for a buffer
named @DevelMain.hs@, loads or reloads it in your current project's
interactive session and then runs @update@.  Assuming that you are
already using /haskell-interactive-mode/ all you need to do to use it is
to keep your @DevelMain@ module open in a buffer and type @M-x
haskell-process-reload-devel-main RET@ when you want to hot-reload.  You
may want to bind it to a key:

> (define-key haskell-mode-map (kbd "C-c m") 'haskell-process-reload-devel-main)

Since you will likely always reload the current module before running
@update@, you can save a few keystrokes by defining a small function
that does both and bind that one to a key instead:

> (defun my-haskell-run-devel ()
>   "Reloads the current module and then hot-reloads code via DevelMain.update."
>   (interactive)
>   (haskell-process-load-file)
>   (haskell-process-reload-devel-main))
>
> (define-key haskell-mode-map (kbd "C-c m") 'my-haskell-run-devel)

-}


{- $intro

While developing a project you may want to have your app running in
the background and restart (parts of) it as you iterate.  The premises
to using this library are:

1. you already have such a project

2. you use GHCi

To use this functionality, create a new module in your project and
export the @update@ action:

> module DevelMain (update) where
>
> import Rapid
>
> update :: IO ()
> update =
>     rapid 0 $ \r ->
>         -- We'll list our components here shortly.
>         pure ()

After loading this module in GHCi you run @update@ whenever you want
to restart the application in the background.  E.g, in the case of a
web server that server is simply restarted on every @update@:

> import qualified Data.Text as T
> import Rapid
> import Snap.Core
> import Snap.Http.Server
>
> update =
>     rapid 0 $ \r ->
>         restart r "webserver" $
>             quickHttpServe (writeText (T.pack "Hello world!"))

The app keeps running in the background even when you reload modules,
and GHCi REPL continues to be functional as well.  To apply new
changes, you simply reload @DevelMain@ again and run @update@.
Changing "Hello world!" to something else above will start responding
with the new text after you run @update@.

To stop the background thread, replace @restart@ with @stop@ within
@update@ and run it.  Note that the action given to 'stop' is actually
ignored.  It only takes the action argument for your convenience.

You can run multiple threads in the background simultaneously, have
some of them restart while others not:

> import MyProject.MyDatabase
> import MyProject.MyBackgroundWorker
> import MyProject.MyWebServer
> import Rapid
>
> update =
>     rapid 0 $ \r -> do
>         start r "database" myDatabase       -- doesn't restart on update
>         start r "worker" myBackgroundWorker -- doesn't restart on update
>         restart r "webserver" myWebServer   -- restarts on update

Usually you'd use @restart@ in front of the component you are working
on, while using @start@ for others.

Note that even while working on @MyProject.MyWebServer@ you're always
reloading @DevelMain@ to get the new @update@.

-}


{- $reusing

This library can also be used to shorten the development cycle when
using expensive resources:

> import Control.Exception
> import Data.Aeson
> import qualified Data.ByteString as B
>
> update =
>     rapid 0 $ \r ->
>         value <- createRef r "file" $
>             B.readFile "blah.json" >>=
>             either (throwIO . userError) pure . eitherDecode
>
>         -- You can now reuse 'value' across reloads.

The above parses blah.json just once on startup. To actually recreate
the value replace @createRef@ to @writeRef@ temporarily and run @update@.

Using @deleteRef@ in the same manner removes values you no longer need.

-}


{- $safety

It's easy to crash GHCi with this library.  In order to prevent that,
follow these rules:

  * Do not change your service name type (the second argument to
    @start@, @stop@ and @restart@) within a session.  Simplest way
    to do that is to resist the temptation to define a custom name
    type and just use strings instead.  If you do change the name
    type then you need to restart GHCi.

  * Be careful with mutable variables created with @createRef@: if the
    value type changes (e.g. constructors or fields were changed), so
    must the variable be recreated, e.g by using @writeRef@ once.
    This likely also entails restarting all the threads that were
    using this variable.  Again, the safest option is to restart GHCi.

  * If any package in the current environment changes (especially this
    library itself), for example by updating a package via @cabal@ or
    @stack@, the @update@ action is likely to crash or go wrong in
    subtle ways due to binary incompatibility.  Again, restarting GHCi
    solves this.

  * __This library is a development tool!  Do not use it to hot-reload
    productive environments!__ There are much safer and more
    appropriate ways to hot-reload code in production, for example by
    using a plugin system.

The reason for this unsafety is that the underlying /foreign-store/
library is itself unsafe by nature, requiring us to maintain binary
compatibility.  This library hides most of that unsafety, but still
requires you to follow the rules listed above.

-}