boots 0.0.1 → 0.0.2
raw patch · 9 files changed
+72/−23 lines, 9 filesdep ~salakdep ~salak-yamlPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: salak, salak-yaml
API changes (from Hackage documentation)
- Boots: booting :: Plugin () m cxt -> AppT cxt m () -> m ()
+ Boots: boot :: Monad m => Plugin () m (m ()) -> m ()
+ Boots: bootApp :: Plugin () m cxt -> AppT cxt m () -> m ()
+ Boots: isoPlugin :: (m () -> n ()) -> (n () -> m ()) -> Plugin i n u -> Plugin i m u
+ Boots: type App cxt = AppT cxt IO
- Boots: bracketP :: MonadCatch m => m u -> (u -> m ()) -> Plugin i m u
+ Boots: bracketP :: forall m i u. MonadCatch m => m u -> (u -> m ()) -> Plugin i m u
Files
- README.md +11/−3
- boots.cabal +4/−4
- main/Main.hs +1/−1
- src/Boots.hs +29/−0
- src/Boots/Internal.hs +12/−7
- src/Boots/Internal/App.hs +4/−1
- src/Boots/Internal/Plugin.hs +10/−6
- src/Boots/Plugin/Logger.hs +1/−1
- test/Spec.hs +0/−0
README.md view
@@ -7,12 +7,20 @@ Boot applications by using plugins. +### Motivation++Simplify to create an application in Haskell.++When we decide to create an application using Haskell. We may need using configurations, loggers as basic functions. If this application needs storages, caches, etc., then we have to weaving the management of connection of these facilities into the application. Connections need to be created before and be destroyed after using them. There is a common strategy to manage connections, that is using `Control.Monad.Cont`. Then we can encapsulate the management of connections separately. For example, we can write a database plugin `Plugin cxt m DBConnection`, which can manage the database connections in monad `m` with context `cxt`. Context `cxt` may be requested for configurations or logging functions. When all the components of application are encapsulated by plugins, then building an application will be simplified.++### Have a Try+ ```Haskell main :: IO ()-main = booting (pluginSimple "application") go+main = bootApp (pluginSimple "application") go where go = forever $ do- user <- require "user"- logInfo $ "Hello, " <> user <> "!"+ user <- require "user" -- Request for configuration.+ logInfo $ "Hello, " <> user <> "!" -- Request for logging. liftIO $ threadDelay 1000000 ```
boots.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.12 name: boots-version: 0.0.1+version: 0.0.2 license: MIT license-file: LICENSE copyright: 2019 Daniel YU@@ -30,7 +30,7 @@ default-language: Haskell2010 default-extensions: FlexibleInstances MultiParamTypeClasses GeneralizedNewtypeDeriving OverloadedStrings RecordWildCards- FlexibleContexts+ FlexibleContexts RankNTypes ScopedTypeVariables build-depends: base >=4.10 && <5, data-default >=0.7.1.1 && <0.8,@@ -52,7 +52,7 @@ default-language: Haskell2010 default-extensions: FlexibleInstances MultiParamTypeClasses GeneralizedNewtypeDeriving OverloadedStrings RecordWildCards- FlexibleContexts+ FlexibleContexts RankNTypes ScopedTypeVariables build-depends: base >=4.10 && <5, boots -any,@@ -84,7 +84,7 @@ default-language: Haskell2010 default-extensions: FlexibleInstances MultiParamTypeClasses GeneralizedNewtypeDeriving OverloadedStrings RecordWildCards- FlexibleContexts+ FlexibleContexts RankNTypes ScopedTypeVariables build-depends: base >=4.10 && <5, data-default >=0.7.1.1 && <0.8,
main/Main.hs view
@@ -5,7 +5,7 @@ import Control.Monad main :: IO ()-main = booting (pluginSimple "application") go+main = bootApp (pluginSimple "application") go where go = forever $ do logInfo "Hello, world!"
src/Boots.hs view
@@ -8,11 +8,40 @@ -- -- Boot application by using plugins. --+-- * Motivation+--+-- Simplify to create an application in Haskell.+--+-- When we decide to create an application using Haskell.+-- We may need using configurations, loggers as basic functions. +-- If this application needs storages, caches, etc., +-- then we have to weaving the management of connection of these facilities into the application. +-- Connections need to be created before and be destroyed after using them. +-- There is a common strategy to manage connections, that is using `Control.Monad.Cont`. +-- Then we can encapsulate the management of connections separately. +-- For example, we can write a database plugin `Plugin` @cxt@ @m@ @DBConnection@, +-- which can manage the database connections in monad @m@ with context @cxt@. +-- Context @cxt@ may be requested for getting configurations or logging functions. +-- When all the components of application are encapsulated by plugins, then building an application will be simplified.+--+-- * Have a Try+-- -- >>> booting (pluginSimple "application") (logInfo "hello") -- 2019-07-27 19:35:30 INFO [application] Ghci1 - hello -- >>> booting (pluginSimple "application") (require "user" >>= logInfo) -- 2019-07-27 19:37:45 INFO [application] Ghci2 - daniel --+-- Main+-- +-- > main :: IO ()+-- > main = bootApp (pluginSimple "application") go+-- > where+-- > go = forever $ do+-- > user <- require "user" -- Request for configuration.+-- > logInfo $ "Hello, " <> user <> "!" -- Request for logging.+-- > liftIO $ threadDelay 1000000+--+ module Boots( module Boots.Internal , module Boots.Plugin
src/Boots/Internal.hs view
@@ -9,17 +9,22 @@ -- Boot cores. -- module Boots.Internal(- -- * Main function- booting+ -- * Plugin+ boot+ , module Boots.Internal.Plugin -- * Application+ , bootApp , module Boots.Internal.App- -- * Application plugin- , module Boots.Internal.Plugin ) where import Boots.Internal.App import Boots.Internal.Plugin --- | Run application using a plugin. Context @cxt@ can't escape from @m@.-booting :: Plugin () m cxt -> AppT cxt m () -> m ()-booting plugin app = runPlugin () plugin (`runAppT` app)+-- | Run application only in plugin.+boot :: Monad m => Plugin () m (m ()) -> m ()+boot plugin = runPlugin () plugin id++-- | Run application in context with the help of plugin. Context @cxt@ can't escape from @m@.+-- If you want to define your own `AppT` then please use 'boot' or 'runPlugin'.+bootApp :: Plugin () m cxt -> AppT cxt m () -> m ()+bootApp plugin app = runPlugin () plugin (`runAppT` app)
src/Boots/Internal/App.hs view
@@ -10,6 +10,7 @@ -- module Boots.Internal.App( AppT+ , App , runAppT , liftIO , ask@@ -17,7 +18,6 @@ , throwM ) where -import Boots.Internal.Plugin import Control.Monad.Catch import Control.Monad.IO.Unlift import Control.Monad.Reader@@ -25,6 +25,9 @@ -- | Application monad transformation. newtype AppT cxt m a = AppT { unAppT :: ReaderT cxt m a } deriving (Functor, Applicative, Monad, MonadTrans, MonadReader cxt, MonadIO, MonadThrow, MonadCatch, MonadMask)++-- | Simple IO monad.+type App cxt = AppT cxt IO -- | Run application monad transformation. runAppT :: cxt -> AppT cxt m a -> m a
src/Boots/Internal/Plugin.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses #-} -- | -- Module: Boots.Internal.Plugin -- Copyright: 2019 Daniel YU@@ -16,6 +14,7 @@ , promote , withPlugin , mapPlugin+ , isoPlugin , bracketP ) where @@ -50,11 +49,16 @@ withPlugin :: (i -> j) -> Plugin j m u -> Plugin i m u withPlugin f = Plugin . withReaderT f . unPlugin +-- | Transform a plugin with monad @n@ to a plugin with monad @m@.+isoPlugin :: (m () -> n ()) -> (n () -> m ()) -> Plugin i n u -> Plugin i m u+isoPlugin f g = Plugin . mapReaderT go . unPlugin+ where+ go (ContT fnc) = ContT $ \mc -> g $ fnc (f . mc)+ -- | Apply a function to transform the result of a continuation-passing computation. mapPlugin :: (m () -> m ()) -> Plugin i m u -> Plugin i m u mapPlugin f = Plugin . mapReaderT (mapContT f) . unPlugin - -- | Create bracket style plugin, used for manage resources, which need to open and close. -- -- A simple example:@@ -65,7 +69,7 @@ -- using -- close bracketP- :: MonadCatch m+ :: forall m i u. MonadCatch m => m u -- ^ Open resource. -> (u -> m ()) -- ^ Close resource. -> Plugin i m u -- ^ Resource plugin.@@ -73,8 +77,8 @@ where {-# INLINE go #-} go f u = do- v <- try (f u)- cl u+ v <- try $ f u+ _ <- try $ cl u :: m (Either SomeException ()) case v of Left e -> throwM (e :: SomeException) Right x -> return x
src/Boots/Plugin/Logger.hs view
@@ -22,13 +22,13 @@ , logOther ) where -import Data.Monoid((<>)) import Boots.Internal import Boots.Plugin.Salak import Control.Monad import Control.Monad.Logger.CallStack import Control.Monad.Reader import Data.Default+import Data.Monoid ((<>)) import Data.Text (Text, toLower, unpack) import Data.Word import Lens.Micro
test/Spec.hs view