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