packages feed

rapid (empty) → 0.1.0

raw patch · 6 files changed

+787/−0 lines, 6 filesdep +asyncdep +basedep +containerssetup-changed

Dependencies added: async, base, containers, foreign-store, stm

Files

+ LICENSE view
@@ -0,0 +1,202 @@++                                 Apache License+                           Version 2.0, January 2004+                        http://www.apache.org/licenses/++   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION++   1. Definitions.++      "License" shall mean the terms and conditions for use, reproduction,+      and distribution as defined by Sections 1 through 9 of this document.++      "Licensor" shall mean the copyright owner or entity authorized by+      the copyright owner that is granting the License.++      "Legal Entity" shall mean the union of the acting entity and all+      other entities that control, are controlled by, or are under common+      control with that entity. For the purposes of this definition,+      "control" means (i) the power, direct or indirect, to cause the+      direction or management of such entity, whether by contract or+      otherwise, or (ii) ownership of fifty percent (50%) or more of the+      outstanding shares, or (iii) beneficial ownership of such entity.++      "You" (or "Your") shall mean an individual or Legal Entity+      exercising permissions granted by this License.++      "Source" form shall mean the preferred form for making modifications,+      including but not limited to software source code, documentation+      source, and configuration files.++      "Object" form shall mean any form resulting from mechanical+      transformation or translation of a Source form, including but+      not limited to compiled object code, generated documentation,+      and conversions to other media types.++      "Work" shall mean the work of authorship, whether in Source or+      Object form, made available under the License, as indicated by a+      copyright notice that is included in or attached to the work+      (an example is provided in the Appendix below).++      "Derivative Works" shall mean any work, whether in Source or Object+      form, that is based on (or derived from) the Work and for which the+      editorial revisions, annotations, elaborations, or other modifications+      represent, as a whole, an original work of authorship. For the purposes+      of this License, Derivative Works shall not include works that remain+      separable from, or merely link (or bind by name) to the interfaces of,+      the Work and Derivative Works thereof.++      "Contribution" shall mean any work of authorship, including+      the original version of the Work and any modifications or additions+      to that Work or Derivative Works thereof, that is intentionally+      submitted to Licensor for inclusion in the Work by the copyright owner+      or by an individual or Legal Entity authorized to submit on behalf of+      the copyright owner. For the purposes of this definition, "submitted"+      means any form of electronic, verbal, or written communication sent+      to the Licensor or its representatives, including but not limited to+      communication on electronic mailing lists, source code control systems,+      and issue tracking systems that are managed by, or on behalf of, the+      Licensor for the purpose of discussing and improving the Work, but+      excluding communication that is conspicuously marked or otherwise+      designated in writing by the copyright owner as "Not a Contribution."++      "Contributor" shall mean Licensor and any individual or Legal Entity+      on behalf of whom a Contribution has been received by Licensor and+      subsequently incorporated within the Work.++   2. Grant of Copyright License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      copyright license to reproduce, prepare Derivative Works of,+      publicly display, publicly perform, sublicense, and distribute the+      Work and such Derivative Works in Source or Object form.++   3. Grant of Patent License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      (except as stated in this section) patent license to make, have made,+      use, offer to sell, sell, import, and otherwise transfer the Work,+      where such license applies only to those patent claims licensable+      by such Contributor that are necessarily infringed by their+      Contribution(s) alone or by combination of their Contribution(s)+      with the Work to which such Contribution(s) was submitted. If You+      institute patent litigation against any entity (including a+      cross-claim or counterclaim in a lawsuit) alleging that the Work+      or a Contribution incorporated within the Work constitutes direct+      or contributory patent infringement, then any patent licenses+      granted to You under this License for that Work shall terminate+      as of the date such litigation is filed.++   4. Redistribution. You may reproduce and distribute copies of the+      Work or Derivative Works thereof in any medium, with or without+      modifications, and in Source or Object form, provided that You+      meet the following conditions:++      (a) You must give any other recipients of the Work or+          Derivative Works a copy of this License; and++      (b) You must cause any modified files to carry prominent notices+          stating that You changed the files; and++      (c) You must retain, in the Source form of any Derivative Works+          that You distribute, all copyright, patent, trademark, and+          attribution notices from the Source form of the Work,+          excluding those notices that do not pertain to any part of+          the Derivative Works; and++      (d) If the Work includes a "NOTICE" text file as part of its+          distribution, then any Derivative Works that You distribute must+          include a readable copy of the attribution notices contained+          within such NOTICE file, excluding those notices that do not+          pertain to any part of the Derivative Works, in at least one+          of the following places: within a NOTICE text file distributed+          as part of the Derivative Works; within the Source form or+          documentation, if provided along with the Derivative Works; or,+          within a display generated by the Derivative Works, if and+          wherever such third-party notices normally appear. The contents+          of the NOTICE file are for informational purposes only and+          do not modify the License. You may add Your own attribution+          notices within Derivative Works that You distribute, alongside+          or as an addendum to the NOTICE text from the Work, provided+          that such additional attribution notices cannot be construed+          as modifying the License.++      You may add Your own copyright statement to Your modifications and+      may provide additional or different license terms and conditions+      for use, reproduction, or distribution of Your modifications, or+      for any such Derivative Works as a whole, provided Your use,+      reproduction, and distribution of the Work otherwise complies with+      the conditions stated in this License.++   5. Submission of Contributions. Unless You explicitly state otherwise,+      any Contribution intentionally submitted for inclusion in the Work+      by You to the Licensor shall be under the terms and conditions of+      this License, without any additional terms or conditions.+      Notwithstanding the above, nothing herein shall supersede or modify+      the terms of any separate license agreement you may have executed+      with Licensor regarding such Contributions.++   6. Trademarks. This License does not grant permission to use the trade+      names, trademarks, service marks, or product names of the Licensor,+      except as required for reasonable and customary use in describing the+      origin of the Work and reproducing the content of the NOTICE file.++   7. Disclaimer of Warranty. Unless required by applicable law or+      agreed to in writing, Licensor provides the Work (and each+      Contributor provides its Contributions) on an "AS IS" BASIS,+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or+      implied, including, without limitation, any warranties or conditions+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A+      PARTICULAR PURPOSE. You are solely responsible for determining the+      appropriateness of using or redistributing the Work and assume any+      risks associated with Your exercise of permissions under this License.++   8. Limitation of Liability. In no event and under no legal theory,+      whether in tort (including negligence), contract, or otherwise,+      unless required by applicable law (such as deliberate and grossly+      negligent acts) or agreed to in writing, shall any Contributor be+      liable to You for damages, including any direct, indirect, special,+      incidental, or consequential damages of any character arising as a+      result of this License or out of the use or inability to use the+      Work (including but not limited to damages for loss of goodwill,+      work stoppage, computer failure or malfunction, or any and all+      other commercial damages or losses), even if such Contributor+      has been advised of the possibility of such damages.++   9. Accepting Warranty or Additional Liability. While redistributing+      the Work or Derivative Works thereof, You may choose to offer,+      and charge a fee for, acceptance of support, warranty, indemnity,+      or other liability obligations and/or rights consistent with this+      License. However, in accepting such obligations, You may act only+      on Your own behalf and on Your sole responsibility, not on behalf+      of any other Contributor, and only if You agree to indemnify,+      defend, and hold each Contributor harmless for any liability+      incurred by, or claims asserted against, such Contributor by reason+      of your accepting any such warranty or additional liability.++   END OF TERMS AND CONDITIONS++   APPENDIX: How to apply the Apache License to your work.++      To apply the Apache License to your work, attach the following+      boilerplate notice, with the fields enclosed by brackets "[]"+      replaced with your own identifying information. (Don't include+      the brackets!)  The text should be enclosed in the appropriate+      comment syntax for the file format. We also recommend that a+      file or class name and description of purpose be included on the+      same "printed page" as the copyright notice for easier+      identification within third-party archives.++   Copyright [yyyy] [name of copyright owner]++   Licensed under the Apache License, Version 2.0 (the "License");+   you may not use this file except in compliance with the License.+   You may obtain a copy of the License at++       http://www.apache.org/licenses/LICENSE-2.0++   Unless required by applicable law or agreed to in writing, software+   distributed under the License is distributed on an "AS IS" BASIS,+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+   See the License for the specific language governing permissions and+   limitations under the License.
+ NOTICE view
@@ -0,0 +1,2 @@+rapid+Copyright 2016 Ertugrul Söylemez
+ README.md view
@@ -0,0 +1,29 @@+Rapid+=====++Copyright 2016 Ertugrul Söylemez++Licensed under the Apache License, Version 2.0 (the "License");+you may not use these files except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+++Introduction+------------++This is a helper library for rapid prototyping in GHCi sessions.  It+provides hot-reloadable background threads as well as values that can be+reused across module reloads (to save initialisation time).  Main use+cases include the development of long-running applications, especially+those with multiple separable units:  servers, web applications,+interactive user interfaces, etc.++A tutorial-style introduction is contained within the `Rapid` module.
+ Rapid.hs view
@@ -0,0 +1,476 @@+-- Copyright 2016 Ertugrul Söylemez+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE ScopedTypeVariables #-}++-- |+-- Module:     Rapid+-- Copyright:  Copyright 2016 Ertugrul Söylemez+-- License:    Apache License 2.0+-- Maintainer: Ertugrul Söylemez <esz@posteo.de>+--+-- This library provides a safer and more convenient wrapper around the+-- <https://hackage.haskell.org/package/foreign-store foreign-store library>.+--+-- You can use it for background services within a GHCi session that+-- survive loading, reloading and unloading modules, which is+-- particularly useful when writing long-running programs like servers+-- and user interfaces.+--+-- __Please read the "Safety and securty" section below!__++module Rapid+    ( -- * Introduction+      -- $intro++      -- ** Communication+      -- $communication++      -- ** Reusing expensive resources+      -- $reusing++      -- ** Emacs integration+      -- $emacs++      -- ** Safety and security+      -- $safety++      -- * Hot code reloading+      Rapid,+      rapid,++      -- * Threads+      restart,+      start,+      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 {+      rLock    :: TVar Bool,               -- ^ Lock on the current state.+      rRefs    :: TVar (Map k Dynamic),    -- ^ Mutable variables.+      rThreads :: TVar (Map k (Async ()))  -- ^ Active threads.+    }+++-- | Cancel the given thread and wait for it to finish.++cancelAndWait :: Async a -> IO ()+cancelAndWait tv = do+    cancel tv+    () <$ waitCatch 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 r k gen =+    withRef r k $ \mxd ->+        case mxd of+          Nothing -> fmap (\x -> (Just (toDyn x), x)) gen+          Just xd+              | Just x <- fromDynamic xd -> pure (Just xd, x)+              | otherwise -> throwIO (userError "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 r k =+    withRef r k (\_ -> pure (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 stNum k =+    mask $ \unmask ->+        lookupStore stNum >>=+        maybe (storeAction store create)+              (\_ -> readStore store) >>=+        pass unmask++    where+    create =+        pure Rapid+        <*> newTVarIO False+        <*> newTVarIO M.empty+        <*> newTVarIO M.empty++    pass unmask r = do+        atomically $ do+            readTVar (rLock r) >>= check . not+            writeTVar (rLock r) True+        unmask (k r) `finally` atomically (writeTVar (rLock r) False)++    store :: Store (Rapid k)+    store = Store 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 r k action =+    withThread r k $ \mtv -> do+        whenJust mtv cancelAndWait+        Just <$> async 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 -> k -> IO () -> IO ()+start r k action =+    withThread r k $+        maybe (Just <$> async action)+              (\tv -> poll tv >>=+                      maybe (pure (Just tv))+                            (\_ -> Just <$> async 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 r k _ =+    withThread r k $ \mtv ->+        Nothing <$ whenJust mtv cancelAndWait+++-- | If 'Just', perform the given effect.+--+-- prop> whenJust Nothing  _ = pure ()+-- prop> whenJust (Just x) f = () <$ f x++whenJust :: (Applicative m) => Maybe a -> (a -> m r) -> m ()+whenJust Nothing  _ = pure ()+whenJust (Just x) f = () <$ f x+++-- | 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 r k f = do+    (mx, y) <- atomically (M.lookup k <$> readTVar (rRefs r)) >>=+               f+    atomically $ modifyTVar' (rRefs r) (maybe (M.delete k) (M.insert k) mx)+    pure 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 r k f =+    atomically (M.lookup k <$> readTVar (rThreads r)) >>=+    f >>=+    atomically . modifyTVar' (rThreads r) . maybe (M.delete k) (M.insert 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 r k gen =+    withRef r k $ \_ ->+        fmap (\x -> (Just (toDyn x), x)) gen+++{- $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++To use this library in your project create a module conventionally named+@DevelMain@ that exports an action conventionally named @update@:++> module DevelMain (update) where+>+> import Rapid+>+> update :: IO ()+> update =+>     rapid 0 $ \r ->+>         -- Your service management goes here.+>         pure ()++The idea is that within a GHCi session this @update@ action is run+whenever you want to reload your project during development.  In the+simplest case, like in a web application, your project consists of a+single service that is just restarted each time you reload.  Here is an+example using the Snap Framework:++> 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!"))++Once you run @update@ in a GHCi session, a server is started (port 8000)+that keeps running in the background, even when you reload modules.  The+REPL is fully responsive, so you can continue working.  When you want to+apply the changes you have made, you run @update@ again.  To see this in+action, change the text string in the example, reload the module and+then run @update@.  Also observe that nothing is changed until you+actually run @update@.++When you want to stop a running background thread, replace 'restart'+within the @update@ action by 'stop' and run @update@.  The action given+to 'stop' is actually ignored.  It only takes the action argument for+your convenience.++You can run multiple threads at the same time and also have threads that+are not restarted during a reload, but are only started and then kept+running:++> update =+>     rapid 0 $ \r ->+>         start r "database" myDatabase+>         start r "worker" myBackgroundWorker+>         restart r "webserver" myWebServer++Usually you would put 'restart' in front of the component that you are+currently working on, while using 'start' with all others.++-}+++{- $reusing++Mutable references as introduced in the previous section can also be+used to shorten the development cycle in the case when an expensive+resource has to be created.  As an example imagine that you need to+parse a huge file into a data structure.  You can keep the result of+that in memory across reloads.  Example with parsing JSON:++> 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.++If you want to recreate the value at some point, you can just change+'createRef' to 'writeRef' and then run @update@.  Keep in mind to change+it back @createRef@ afterward.  Use 'deleteRef' to remove values you no+longer need, so they can be garbage-collected.++-}+++{- $safety++It's easy to crash your GHCi session with this library.  In order to+prevent that, you must follow these rules:++  * Do not change your service name type (the type argument of 'Rapid',+    i.e. the second argument to 'restart', 'start' and 'stop') within a+    session.  The 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, you should restart GHCi.++  * Be careful with mutable variable created with 'createRef':  If the+    value type changes (e.g. constructors or fields were changed), the+    variable must be recreated, for example by using 'writeRef' once.  This+    most likely entails restarting all threads that were using the variable.+    Again the safest option is to just 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.  If packages change, restart GHCi.++  * __This library is a development tool!  Do not even think of using it to hot-reload in a productive environment!__+    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 very unsafe in nature and requires that we maintain+binary compatibility.  This library hides most of that unsafety, but+still requires that you follow the rules above.++Please take the last rule seriously and never ever use this library in+production!  If something goes wrong during a reload, we do not get a+convenient run-time exception; we get a memory violation, which can+cause anything from a segfault to a remotely exploitable security hole.++-}
+ Setup.lhs view
@@ -0,0 +1,21 @@+Setup script+Copyright 2016 Ertugrul Söylemez++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.++> module Main where+>+> import Distribution.Simple+>+> main :: IO ()+> main = defaultMain
+ rapid.cabal view
@@ -0,0 +1,57 @@+-- Copyright 2016 Ertugrul Söylemez+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+--     http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++name:     rapid+version:  0.1.0+category: Development+synopsis: GHCi background threads, hot reloading and reload-surviving values++maintainer:   Ertugrul Söylemez <esz@posteo.de>+author:       Ertugrul Söylemez <esz@posteo.de>+copyright:    Copyright 2016 Ertugrul Söylemez+homepage:     http://hub.darcs.net/esz/rapid+bug-reports:  http://hub.darcs.net/esz/rapid/issues+license:      Apache+license-file: LICENSE++description:  This package provides a safe and convenient wrapper around+    <https://hackage.haskell.org/package/foreign-store foreign-store>+    for hot-reloadable background threads during a GHCi session, useful+    for 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 to keep+    resources that are expensive to create in memory instead of having+    to recreate them after every module reload.++build-type:         Simple+cabal-version:      >= 1.10+extra-source-files: NOTICE README.md++source-repository head+    type:     darcs+    location: http://hub.darcs.net/esz/rapid+++library+    build-depends:+        async               >= 2.1 && < 3,+        base                >= 4.8 && < 5,+        containers          >= 0.5 && < 1,+        foreign-store       >= 0.2 && < 1,+        stm                 >= 2.4 && < 3+    default-language: Haskell2010+    ghc-options: -W -fdefer-typed-holes+    exposed-modules:+        Rapid