packages feed

capability (empty) → 0.1.0.0

raw patch · 27 files changed

+3038/−0 lines, 27 filesdep +basedep +capabilitydep +containerssetup-changed

Dependencies added: base, capability, containers, dlist, exceptions, generic-lens, hspec, hspec-jenkins, lens, monad-control, monad-unlift, mtl, mutable-containers, primitive, safe-exceptions, silently, streaming, temporary, text, transformers, unliftio, unliftio-core

Files

+ CONTRIBUTING.md view
@@ -0,0 +1,44 @@+# Contributor's guide++## Bug Reports++Please [open an issue][new-issue].++The more detailed your report, the faster it can be resolved. Once the+bug has been resolved, the person responsible will tag the issue as+_needs confirmation_ and assign the issue back to you. Once you have+tested and confirmed that the issue is resolved, close the issue. If+you are not a member of the project, you will be asked for+confirmation and we will close it.++[new-issue]: https://github.com/tweag/capability/issues/new++## Code++1. Explain your idea and discuss your plan with members of the team.+   The best way to do this is to create an [issue][issue-tracker] or+   comment on an existing issue.+1. Prepare a git commit with your change. Don't forget to+   add [tests][tests]. Update [README.md](./README.md) if appropriate.+1. [Create a pull request][create-pull-request]. This will start the+   code review process. Enable "Allow edits from maintainers" if the+   branch is on a fork, so that reviewers can fix trivial problems+   (typos) directly. **All submissions, including submissions by+   project members, require review.**+1. You may be asked to make some changes. Continuous integration bots+   will test your change automatically on supported platforms. Once+   everything looks good, your change will be merged. The minimum+   criteria for acceptance are:++   * a patch should be a minimal and accurate answer to exactly one+     identified and agreed problem,+   * a patch that modifies a stable public API should not break existing+     applications unless there is overriding consensus on the value of+     doing this.+   * A patch must adhere to the [code style guidelines][style-guide]+     of the project.++[issue-tracker]: https://github.com/tweag/capability/issues+[tests]: ./examples+[create-pull-request]: https://help.github.com/articles/creating-a-pull-request/+[style-guide]: https://github.com/tweag/guides/blob/master/style/
+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Revision history for capability++## 0.1.0.0 -- 2018-10-04++* Initial release.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2018 EURL Tweag++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Andreas Herrmann nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,163 @@+# capability: effects, extensionally++A capability is a type class that says explicitly which effects+a function is allowed to use. The [`mtl`][mtl] works like this too.+But unlike the `mtl`, this library decouples effects from their+implementation. What this means in practice:++- You can implement large sets of capabilities using the+  efficient [`ReaderT` pattern][readert], rather than a slow monad+  transformer stack.+- Capabilities compose well: e.g. it's easy to have multiple reader+  effects.+- You can use a writer effect without implementing it as a writer+  monad (which is known to [leak space][writer-space-leak]).+- You can reason about effects. For instance, if a monad provides a+ reader effect at type `IORef A`, it also provides a state effect at type `A`++For more on these, you may want to read the announcement [blog+ post][blog].++This library is an alternative to the [`mtl`][mtl]. It defines a set+of standard, reusable capability type classes, such as the `HasReader`+and `HasState` type classes, which provide the standard reader and+state effects, respectively.++Where `mtl` instances only need to be defined once and for all,+capability-style programming has traditionally suffered from verbose+boilerplate: rote instance definitions for every new implementation of+the capability. Fortunately GHC 8.6 introduced+the [`DerivingVia`][deriving-via] language extension. We use it to+remove the boilerplate, turning capability-style programming into an+appealing alternative to `mtl`-style programming. The+[`generic-lens`][generic-lens] library is used to access fields of+structure in the style of the [`ReaderT` pattern][readert].++An additional benefit of separating capabilities from their+implementation is that they avoid a pitfall of the `mtl`. In the+`mtl`, two different `MonadState` are disambiguated by their types,+which means that it is difficult to have two `MonadState Int` in the+same monad stack. Capability type classes are parameterized by a name+(also known as a *tag*). This makes it possible to combine multiple+versions of the same capability. For example,++```haskell+twoStates :: (HasState "a" Int m, HasState "b" Int m) => m ()+```++Here, the tags `"a"` and `"b"` refer to different state spaces.++In summary, compared to the `mtl`:++- capabilities represent what effects a function can use, rather than+  how the monad is constructed;+- capabilities are named, rather than disambiguated by type;+- capabilites are discharged with deriving-via combinators+  and [`generic-lens`][generic-lens], rather than with instance+  resolution.++An example usage looks like this:++``` haskell+testParity :: (HasReader "foo" Int m, HasState "bar" Bool m) => m ()+testParity = do+  num <- ask @"foo"+  put @"bar" (even num)++data Ctx = Ctx { foo :: Int, bar :: IORef Bool }+  deriving Generic++newtype M a = M { runM :: Ctx -> IO a }+  deriving (Functor, Applicative, Monad) via ReaderT Ctx IO+  -- Use DerivingVia to derive a HasReader instance.+  deriving (HasReader "foo" Int) via+    -- Pick the field foo from the Ctx record in the ReaderT environment.+    Field "foo" "ctx" (MonadReader (ReaderT Ctx IO))+  -- Use DerivingVia to derive a HasState instance.+  deriving (HasState "bar" Bool) via+    -- Convert a reader of IORef to a state capability.+    ReaderIORef (Field "bar" "ctx" (MonadReader (ReaderT Ctx IO)))++example :: IO ()+example = do+    rEven <- newIORef False+    runM testParity (Ctx 2 rEven)+    readIORef rEven >>= print+    runM testParity (Ctx 3 rEven)+    readIORef rEven >>= print+```++For more complex examples, see the [Examples section](#examples) and+the [`examples` subtree](./examples).++This package is not available on Hackage yet, as some of its+dependencies have not been updated to GHC 8.6, yet.++API documentation can be found in the artifacts tab of any successful+build in the [CircleCI project][circleci].++[circleci]: https://circleci.com/gh/tweag/capabilities-via/tree/master+[mtl]: http://hackage.haskell.org/package/mtl+[blog]: https://www.tweag.io/posts/2018-10-04-capability.html+[deriving-via]: https://downloads.haskell.org/~ghc/8.6.1/docs/html/users_guide/glasgow_exts.html#deriving-via+[generic-lens]: https://hackage.haskell.org/package/generic-lens+[readert]: https://www.fpcomplete.com/blog/2017/06/readert-design-pattern+[writer-space-leak]: https://blog.infinitenegativeutility.com/2016/7/writer-monads-and-space-leaks++## Examples++An example is provided in [`WordCount`](examples/WordCount.hs).+Execute the following commands to try it out:++```+$ nix-shell --pure --run "cabal configure --enable-tests"+$ nix-shell --pure --run "cabal repl examples"++ghci> :set -XOverloadedStrings+ghci> wordAndLetterCount "ab ba"+Letters+'a': 2+'b': 2+Words+"ab": 1+"ba": 1+```++To execute all examples and see if they produce the expected results run++```+$ nix-shell --pure --run "cabal test examples --show-details=streaming --test-option=--color"+```++## Build instructions++### Nix Shell++Some of this package's dependencies require patches to build with GHC 8.6.+These patches are defined in+[`nix/haskell/default.nix`](nix/haskell/default.nix).+A development environment with all patched dependencies in scope is defined in+[`shell.nix`](shell.nix).++### Cachix Nix Cache++A Nix cache for this package's dependencies is provided via [cachix][cachix].+If you have [cachix][cachix] installed, then you can activate it by executing++```+$ cachix use tweag+```++[cachix]: https://cachix.org/++### Build++The build instructions assume that you have [Nix][nix] installed.+Execute the following command to build the library.++```+$ nix-shell --pure --run "cabal configure"+$ nix-shell --pure --run "cabal build"+```++[nix]: https://nixos.org/nix/
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ capability.cabal view
@@ -0,0 +1,94 @@+name: capability+version: 0.1.0.0+homepage: https://github.com/tweag/capability+license: BSD3+license-file: LICENSE+maintainer: andreas.herrmann@tweag.io+copyright: 2018 EURL Tweag+category: Control+build-type: Simple+extra-source-files:+  ChangeLog.md+  CONTRIBUTING.md+  README.md+cabal-version: 1.18+tested-with: GHC==8.6.1+synopsis: Extensional capabilities and deriving combinators+description:+  Standard capability type classes for extensional effects and combinators+  to derive capability instances with little boilerplate.++source-repository head+  type: git+  location: https://github.com/tweag/capability++flag hspec-jenkins+  description:+    You can enable the use of the `hspec-jenkins` package using `-fhspec-jenkins`.+    .+    This package allows JUnit formatted test reporting for CI.+  default: False++library+  exposed-modules:+    Capability+    Capability.Accessors+    Capability.Error+    Capability.Reader+    Capability.Reader.Internal.Class+    Capability.Reader.Internal.Strategies+    Capability.State+    Capability.State.Internal.Class+    Capability.State.Internal.Strategies+    Capability.Stream+    Capability.Writer+    Capability.Writer.Discouraged+  build-depends:+      base >= 4.12 && < 5.0+    , dlist >= 0.8 && < 0.9+    , exceptions >= 0.6 && < 0.11+    , generic-lens >= 1.0 && < 1.1+    , lens >= 4.16 && < 5.0+    , monad-control >= 1.0 && < 1.1+    , monad-unlift >= 0.2 && < 0.3+    , mtl >= 2.0 && < 3.0+    , mutable-containers >= 0.3 && < 0.4+    , primitive >= 0.6 && < 0.7+    , safe-exceptions >= 0.1 && < 0.2+    , streaming >= 0.2 && < 0.3+    , transformers >= 0.5.5 && < 0.6+    , unliftio >= 0.2 && < 0.3+    , unliftio-core >= 0.1 && < 0.2+  ghc-options: -Wall+  hs-source-dirs: src+  default-language: Haskell2010++test-suite examples+  type: exitcode-stdio-1.0+  other-modules:+    WordCount+    CountLog+    Error+    Reader+    State+    Stream+    Test.Common+    Writer+  main-is: Test.hs+  build-depends:+      base >= 4.12 && < 5.0+    , capability+    , containers >= 0.6 && < 0.7+    , hspec >= 2.0 && < 3.0+    , lens >= 4.16 && < 5.0+    , mtl >= 2.0 && < 3.0+    , silently >= 1.2 && < 1.3+    , streaming >= 0.2 && < 0.3+    , temporary >= 1.0 && < 1.4+    , text >= 0.2 && < 1.3+    , unliftio >= 0.2 && < 0.3+  if flag(hspec-jenkins)+    build-depends: hspec-jenkins+  ghc-options: -Wall+  hs-source-dirs: examples+  default-language: Haskell2010
+ examples/CountLog.hs view
@@ -0,0 +1,184 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE UndecidableInstances #-}++-- | Demonstrates how to derive user-defined capabilities using this library.+module CountLog where++import Capability.Reader+import Capability.State+import Control.Monad.IO.Class+import Control.Monad.Reader (ReaderT (..), runReaderT)+-- The @StateT@ constructor has to be imported even though it is not used+-- explicitly. Otherwise, the deriving via of @Counter CounterM@ would fail.+import Control.Monad.State.Strict (State, StateT (..), runState)+import qualified Data.Char+import Data.Coerce (coerce)+import Data.IORef+import GHC.Generics (Generic)+import Test.Common+import Test.Hspec+++----------------------------------------------------------------------+-- Logger Capability+++class Monad m => Logger m where+  logStr :: String -> m ()++-- | Any @HasReader "logger" (String -> IO ())@ can be a @Logger@.+newtype TheLoggerReader m a = TheLoggerReader (m a)+  deriving (Functor, Applicative, Monad)+instance+  (HasReader "logger" (String -> IO ()) m, MonadIO m)+  => Logger (TheLoggerReader m)+  where+    logStr msg =+      coerce (ask @"logger" >>= liftIO . ($ msg) :: m ())++-- Example program ---------------------------------------------------++-- | Log the given number.+logNum :: Logger m => Int -> m ()+logNum = logStr . ("num: " ++) . show++-- ReaderT instance --------------------------------------------------++data LogCtx = LogCtx { logger :: String -> IO () }+  deriving Generic++regularLogger :: LogCtx+regularLogger = LogCtx { logger = putStrLn }++loudLogger :: LogCtx+loudLogger = LogCtx { logger = putStrLn . map Data.Char.toUpper }+++-- | Deriving @HasReader@ from @MonadReader@.+newtype LogM m (a :: *) = LogM (ReaderT LogCtx m a)+  deriving (Functor, Applicative, Monad)+  deriving Logger via+    (TheLoggerReader (Field "logger" ()+    (MonadReader (ReaderT LogCtx m))))++runLogM :: LogCtx -> LogM m a -> m a+runLogM ctx (LogM m) = runReaderT m ctx+++----------------------------------------------------------------------+-- Counter Capability+++class Monad m => Counter m where+  count :: m Int++-- | Any @HasState "counter" Int m@ can be a @Counter@.+newtype TheCounterState m a = TheCounterState (m a)+  deriving (Functor, Applicative, Monad)+instance+  (HasState "counter" Int m, Monad m)+  => Counter (TheCounterState m)+  where+    count = coerce @(m Int) $+      state @"counter" $ \n -> let !n' = n + 1 in (n', n')++-- Example program ---------------------------------------------------++-- | Use a counter to count up twice.+doubleCount :: Counter m => m Int+doubleCount = count >> count++-- StateT instance ---------------------------------------------------++-- | Deriving @HasState@ from @MonadState@.+newtype CounterM a = CounterM (State Int a)+  deriving (Functor, Applicative, Monad)+  deriving Counter via+    TheCounterState (MonadState (State Int))++runCounterM :: CounterM a -> (a, Int)+runCounterM (CounterM m) = runState m 0++-- ReaderT IORef instance --------------------------------------------++-- | Deriving @HasState@ from @HasReader@ of an @IORef@.+newtype Counter'M m (a :: *) = Counter'M (ReaderT (IORef Int) m a)+  deriving (Functor, Applicative, Monad)+  deriving Counter via+    TheCounterState (ReaderIORef (MonadReader (ReaderT (IORef Int) m)))++runCounter'M :: MonadIO m => Counter'M m a -> m a+runCounter'M (Counter'M m) = runReaderT m =<< liftIO (newIORef 0)+++----------------------------------------------------------------------+-- Mixed Capabilities++-- Example program ---------------------------------------------------++-- | Double count and log the result, repeat once.+mixed :: (Counter m, Logger m) => m ()+mixed = do+  doubleCount >>= logNum+  doubleCount >>= logNum++-- ReaderT instance --------------------------------------------------++data CountLogCtx = CountLogCtx+  { countCtx :: IORef Int+  , logCtx :: LogCtx+  } deriving Generic+++-- | Deriving two capabilities from the record fields of @MonadReader@.+newtype CountLogM m (a :: *) = CountLogM (ReaderT CountLogCtx m a)+  deriving (Functor, Applicative, Monad)+  deriving Counter via+    TheCounterState (ReaderIORef (Rename "countCtx" (Field "countCtx" ()+    (MonadReader (ReaderT CountLogCtx m)))))+  deriving Logger via+    (TheLoggerReader (Field "logger" "logCtx" (Field "logCtx" ()+    (MonadReader (ReaderT CountLogCtx m)))))++runCountLogM :: MonadIO m => CountLogM m b -> m b+runCountLogM (CountLogM m) = do+  ref <- liftIO $ newIORef 0+  runReaderT m CountLogCtx+    { countCtx = ref+    , logCtx = regularLogger+    }+++----------------------------------------------------------------------+-- Test Cases++spec :: Spec+spec = do+  describe "LogM" $ do+    context "regularLogger" $+      it "evaluates logNum" $+        runLogM regularLogger (logNum 8) `shouldPrint` "num: 8\n"+    context "loudLogger" $+      it "evaluates logNum" $+        runLogM loudLogger (logNum 8) `shouldPrint` "NUM: 8\n"+  describe "CounterM" $+    it "evaluates doubleCount" $+      runCounterM doubleCount `shouldBe` (2, 2)+  describe "Counter'M" $+    it "evaluates doubleCount" $+      runCounter'M doubleCount `shouldReturn` 2+  describe "CountLogM" $ do+    it "evaluates logNum" $+      runCountLogM (logNum 8) `shouldPrint` "num: 8\n"+    it "evaluates doubleCount" $+      runCountLogM doubleCount `shouldReturn` 2+    it "evaluates mixed" $+      runCountLogM mixed `shouldPrint` "num: 2\nnum: 4\n"
+ examples/Error.hs view
@@ -0,0 +1,153 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeInType #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE UndecidableInstances #-}++-- | Example uses and instances of the @HasError@ capability.+module Error where++import Capability.Error+import Control.Monad (when)+import Control.Monad.Except (ExceptT (..))+import Control.Monad.IO.Class (MonadIO, liftIO)+import GHC.Generics (Generic)+import Test.Common+import Test.Hspec+import Text.Read (readEither)++----------------------------------------------------------------------+-- Example Programs++-- Calculator Example ------------------------------------------------++data ParserError+  = InvalidInput String+  deriving (Show, Typeable)+  deriving anyclass Exception++parseNumber :: HasThrow "parser" ParserError m+  => String -> m Int+parseNumber input = case readEither input of+  Left err -> throw @"parser" $ InvalidInput err+  Right num -> pure num+++data MathError+  = NegativeInput+  deriving (Show, Typeable)+  deriving anyclass Exception++sqrtNumber :: HasThrow "math" MathError m+  => Int -> m Int+sqrtNumber num+  | num < 0 = throw @"math" NegativeInput+  | otherwise = pure $ round $ sqrt @Double $ fromIntegral num+++-- | Errors that can occur in the calculator application.+data CalcError+    -- | The parser component failed.+  = ParserError ParserError+    -- | The math component failed.+  | MathError MathError+  deriving (Generic, Show, Typeable)+  deriving anyclass Exception++-- | Calculator application+--+-- Prompts for positive numbers and prints their square roots.+calculator ::+  ( HasCatch "calc" CalcError m, MonadIO m )+  => m ()+calculator = do+  liftIO $ putStr "Enter positive number or 'Q' to quit\n> "+  line <- liftIO getLine+  case line of+    "Q" -> pure ()+    input -> do+      catch @"calc"+        do+          -- Errors in the parser or math component are converted to a+          -- @CalcError@ by wrapping with the corresponding constructor.+          let wrapParserError = wrapError @"calc" @"parser"+                @(Rename "ParserError" :.: Ctor "ParserError" "calc")+              wrapMathError = wrapError @"calc" @"math"+                @(Rename "MathError" :.: Ctor "MathError" "calc")+          num <- wrapParserError $ parseNumber input+          root <- wrapMathError $ sqrtNumber num+          liftIO $ putStrLn $ "sqrt = " ++ show root+        \e -> liftIO $ putStrLn $ "Error: " ++ show e+      calculator+++-- Nested Example ----------------------------------------------------++nested :: (HasThrow "foo" String m, HasThrow "bar" () m) => Int -> m Int+nested n = do+  when (n < 0) $+    throw @"foo" "negative number"+  when (odd n) $+    throw @"bar" ()+  pure n+++----------------------------------------------------------------------+-- Instances++-- | Deriving @HasThrow/HasCatch@ from @unliftio@.+newtype Calculator a = Calculator { runCalculator :: IO a }+  deriving newtype (Functor, Applicative, Monad, MonadIO)+  deriving+    ( HasThrow "calc" CalcError+    , HasCatch "calc" CalcError+    ) via MonadUnliftIO CalcError IO+++-- | Deriving separate @HasThrow@ capabilities from different transformer+-- layers.+newtype MaybeEither a =+  MaybeEither { runMaybeEither :: Maybe (Either String a) }+  deriving (Functor, Applicative, Monad) via+    ExceptT String Maybe+  deriving (HasThrow "foo" String) via+    MonadError (ExceptT String Maybe)+  deriving (HasThrow "bar" ()) via+    Lift (ExceptT String (MonadError Maybe))+++----------------------------------------------------------------------+-- Test Cases++spec :: Spec+spec = do+  describe "Calculator" $+    it "evaluates calculator" $ do+      let input = "4\n-1\nxyz\nQ\n"+          output =+            "Enter positive number or 'Q' to quit\n\+            \> sqrt = 2\n\+            \Enter positive number or 'Q' to quit\n\+            \> Error: MathError NegativeInput\n\+            \Enter positive number or 'Q' to quit\n\+            \> Error: ParserError (InvalidInput \"Prelude.read: no parse\")\n\+            \Enter positive number or 'Q' to quit\n\+            \> "+      runCalculator calculator+        `withInput` input+        `shouldPrint` output+  describe "MaybeEither" $+    it "evaluates nested" $ do+      runMaybeEither (nested 2) `shouldBe` Just (Right 2)+      runMaybeEither (nested (-1)) `shouldBe` Just (Left "negative number")+      runMaybeEither (nested 1) `shouldBe` Nothing
+ examples/Reader.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeInType #-}++-- | Example uses and instances of the @HasReader@ capability.+module Reader where++import Capability.Reader+import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.Reader (ReaderT (..))+import GHC.Generics (Generic)+import Test.Common+import Test.Hspec+++----------------------------------------------------------------------+-- Example Programs++-- | Returns the triple of the number in context "foo".+tripleFoo :: HasReader "foo" Int m => m Int+tripleFoo = do+  single <- ask @"foo"+  double <- asks @"foo" (*2)+  pure $ single + double++-- | Prints the triple and sixfold of the number in context "foo".+fooExample :: (HasReader "foo" Int m, MonadIO m) => m ()+fooExample = do+  liftIO . print =<< tripleFoo+  liftIO . print =<< local @"foo" (*2) tripleFoo+++-- | Prints the double of "bar" and the triple of "foo".+fooBarExample+  :: (HasReader "foo" Int m, HasReader "bar" Int m, MonadIO m) => m ()+fooBarExample = do+  local @"bar" (*2) $ do+    liftIO . print =<< ask @"bar"+    liftIO . print =<< tripleFoo+++----------------------------------------------------------------------+-- Instances++-- | @HasReader@ instance derived via @MonadReader@.+newtype FooReaderT m (a :: *) = FooReaderT (ReaderT Int m a)+  deriving (Functor, Applicative, Monad, MonadIO)+  deriving (HasReader "foo" Int) via MonadReader (ReaderT Int m)++runFooReaderT :: FooReaderT m a -> m a+runFooReaderT (FooReaderT m) = runReaderT m 1+++data FooBar = FooBar+  { foo :: Int+  , bar :: Int+  } deriving Generic++-- | Multiple @HasReader@ instances derived via record fields in @MonadReader@.+newtype FooBarReader a = FooBarReader (ReaderT FooBar IO a)+  deriving (Functor, Applicative, Monad, MonadIO)+  deriving (HasReader "foo" Int) via+    Field "foo" () (MonadReader (ReaderT FooBar IO))+  deriving (HasReader "bar" Int) via+    Field "bar" () (MonadReader (ReaderT FooBar IO))++runFooBarReader :: FooBarReader a -> IO a+runFooBarReader (FooBarReader m) = runReaderT m FooBar { foo = 1, bar = 2 }+++-- | Multiple @HasReader@ instances on the same underlying @MonadReader@.+--+-- Demonstrates colliding instances.+-- Note, do not do this in practice. The derived @HasReader@ instances interact+-- in unexpected ways.+newtype BadFooBarReader a = BadFooBarReader (ReaderT Int IO a)+  deriving (Functor, Applicative, Monad, MonadIO)+  deriving (HasReader "foo" Int) via MonadReader (ReaderT Int IO)+  deriving (HasReader "bar" Int) via MonadReader (ReaderT Int IO)++runBadFooBarReader :: BadFooBarReader a -> IO a+runBadFooBarReader (BadFooBarReader m) = runReaderT m 1+++----------------------------------------------------------------------+-- Test Cases++spec :: Spec+spec = do+  describe "FooReaderT" $+    it "evaluates fooExample" $+      runFooReaderT fooExample `shouldPrint` "3\n6\n"+  describe "FooBarReader" $ do+    it "evaluates fooExample" $+      runFooBarReader fooExample `shouldPrint` "3\n6\n"+    it "evaluates fooBarExample" $+      runFooBarReader fooBarExample `shouldPrint` "4\n3\n"+  describe "BadFooBarReader" $ do+    it "evaluates fooExample" $+      runBadFooBarReader fooExample `shouldPrint` "3\n6\n"+    it "evaluates fooBarExample" $+      runBadFooBarReader fooBarExample `shouldNotPrint` "4\n3\n"
+ examples/State.hs view
@@ -0,0 +1,126 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeInType #-}+{-# LANGUAGE TypeOperators #-}++-- | Example uses and instances of the @HasState@ capability.+module State where++import Capability.Reader+import Capability.State+import Control.Monad.Reader (ReaderT (..))+import Control.Monad.State.Strict (State, StateT (..), runState)+import Data.IORef+import GHC.Generics (Generic)+import Test.Hspec++----------------------------------------------------------------------+-- Example Programs++incFoo :: HasState "foo" Int m => m ()+incFoo = modify @"foo" (+1)++twoStates :: (HasState "foo" Int m, HasState "bar" Int m) => m ()+twoStates = do+  incFoo+  modify @"bar" (subtract 1)+++useZoom :: HasState "foobar" (Int, Int) m => m Int+useZoom = do+  put @"foobar" (2, 2)+  -- Zoom in on the first element in the current state, rename tag 1 to "foo".+  zoom @"foobar" @"foo" @(Rename 1 :.: Pos 1 "foobar") $+    incFoo+  gets @"foobar" (\(foo, bar) -> foo + bar)+++----------------------------------------------------------------------+-- Instances++data TwoStates = TwoStates+  { tsFoo :: IORef Int+  , tsBar :: IORef Int+  } deriving Generic++-- | Deriving @HasState@ from @HasReader@ of an @IORef@.+--+-- In this case two separate state capabilities are derived from the record+-- fields of the @HasReader@ context.+newtype TwoStatesM a = TwoStatesM (ReaderT TwoStates IO a)+  deriving (Functor, Applicative, Monad)+  deriving (HasState "foo" Int) via+    ReaderIORef (Rename "tsFoo" (Field "tsFoo" ()+    (MonadReader (ReaderT TwoStates IO))))+  deriving (HasState "bar" Int) via+    ReaderIORef (Rename "tsBar" (Field "tsBar" ()+    (MonadReader (ReaderT TwoStates IO))))++runTwoStatesM :: TwoStatesM a -> IO (a, (Int, Int))+runTwoStatesM (TwoStatesM m) = do+  fooRef <- newIORef 0+  barRef <- newIORef 0+  result <- runReaderT m TwoStates+    { tsFoo = fooRef+    , tsBar = barRef+    }+  fooVal <- readIORef fooRef+  barVal <- readIORef barRef+  pure (result, (fooVal, barVal))+++-- | Deriving two @HasState@ instances from the components of a tuple in+-- @MonadState@.+newtype PairStateM a = PairStateM (State (Int, Int) a)+  deriving (Functor, Applicative, Monad)+  deriving (HasState "foo" Int) via+    Rename 1 (Pos 1 () (MonadState (State (Int, Int))))+  deriving (HasState "bar" Int) via+    Rename 2 (Pos 2 () (MonadState (State (Int, Int))))++runPairStateM :: PairStateM a -> (a, (Int, Int))+runPairStateM (PairStateM m) = runState m (0, 0)+++-- | Combining the @HasState@ instances from two nested @StateT@ transformers.+--+-- Note, that this is not the recommended way to provide multiple `HasState`+-- capabilities. Use the approach shown above in 'TwoStatesM' instead. However,+-- this pattern can be useful to transation existing code to this library.+newtype NestedStatesM a = NestedStatesM (StateT Int (State Int) a)+  deriving (Functor, Applicative, Monad)+  deriving (HasState "foo" Int) via MonadState (StateT Int (State Int))+  deriving (HasState "bar" Int) via Lift (StateT Int (MonadState (State Int)))++runNestedStatesM :: NestedStatesM a -> ((a, Int), Int)+runNestedStatesM (NestedStatesM m) = runState (runStateT m 0) 0+++runFooBarState+  :: (forall m. HasState "foobar" (Int, Int) m => m a)+  -> (Int, Int) -> (a, (Int, Int))+runFooBarState (MonadState m) = runState m+++----------------------------------------------------------------------+-- Test Cases++spec :: Spec+spec = do+  describe "TwoStatesM" $+    it "evaluates twoStates" $+      runTwoStatesM twoStates `shouldReturn` ((), (1, -1))+  describe "PairStateM" $+    it "evaluates twoStates" $+      runPairStateM twoStates `shouldBe` ((), (1, -1))+  describe "NestedStatesM" $+    it "evaluates twoStates" $+      runNestedStatesM twoStates `shouldBe` (((), 1), -1)+  describe "runFooBarState" $+    it "evaluates useZoom" $+      runFooBarState useZoom (0, 0) `shouldBe` (5, (3, 2))
+ examples/Stream.hs view
@@ -0,0 +1,92 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeInType #-}++-- | Example uses and instances of the @HasStream@ capability.+module Stream where++import Capability.State+import Capability.Stream+import Control.Monad.State.Strict (State, StateT (..), evalStateT, runState)+import qualified Data.Set as Set+import Streaming (Stream, Of)+import qualified Streaming.Prelude as S+import Test.Common+import Test.Hspec++----------------------------------------------------------------------+-- Example Programs++iota :: HasStream "nums" Int m => Int -> m ()+iota n+  | n < 0 = error "negative number passed to iota."+  | otherwise = go 0+  where+    go i+      | i == n = pure ()+      | otherwise = yield @"nums" i >> go (succ i)++labelledNodes+  :: (HasState "counter" Int m, HasStream "out" (Int, a) m, Foldable t)+  => t a -> m ()+labelledNodes = mapM_ $ \a -> do+  n <- state @"counter" $ \n -> (n, succ n)+  yield @"out" (n, a)+++----------------------------------------------------------------------+-- Instances++-- | @HasStream a@ derived from @HasState [a]@. Will produce reversed list.+newtype StreamAccM a = StreamAccM (State [Int] a)+  deriving (Functor, Applicative, Monad)+  deriving (HasStream "nums" Int) via+    StreamStack (MonadState (State [Int]))++runStreamAccM :: StreamAccM a -> (a, [Int])+runStreamAccM (StreamAccM m) = runState m []+++-- | @'Streaming.Stream' ('Streaming.Of' a)@ has a @HasStream a@ instance.+printStreamOfInt :: Stream (Of Int) IO () -> IO ()+printStreamOfInt = S.stdoutLn . S.map show+++-- | Composed @StateT@ and @Stream@ to provide @HasState@ and @HasStream@.+newtype StateOverStream a =+  StateOverStream (StateT Int (Stream (Of (Int, Char)) IO) a)+  deriving (Functor, Applicative, Monad)+  deriving (HasState "counter" Int) via+    MonadState (StateT Int (Stream (Of (Int, Char)) IO))+  deriving (HasStream "out" (Int, Char)) via+    Lift (StateT Int (Stream (Of (Int, Char)) IO))++printStateOverStream :: StateOverStream () -> IO ()+printStateOverStream (StateOverStream m) = do+  S.stdoutLn . S.map show $ evalStateT m 0++printLabelledNodes :: IO ()+printLabelledNodes =+  printStateOverStream $ labelledNodes $+    Set.fromList "Hello world!"+++----------------------------------------------------------------------+-- Test Cases++spec :: Spec+spec = do+  describe "StreamAccM" $+    it "evaluates iota" $+      runStreamAccM (iota 10) `shouldBe` ((), [9, 8 .. 0])+  describe "Stream (Of Int)" $+    it "evaluates iota" $+      printStreamOfInt (iota 3) `shouldPrint` "0\n1\n2\n"+  describe "StateOverStream" $ do+    it "evaluates labelledNodes" $+      printLabelledNodes `shouldPrint`+        "(0,' ')\n(1,'!')\n(2,'H')\n(3,'d')\n(4,'e')\+        \\n(5,'l')\n(6,'o')\n(7,'r')\n(8,'w')\n"
+ examples/Test.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE CPP #-}++module Main+  ( main+  ) where++import Test.Hspec+import Test.Hspec.Runner++#ifdef MIN_VERSION_hspec_jenkins+import Test.Hspec.Formatters.Jenkins (xmlFormatter)+#endif++import qualified CountLog+import qualified Error+import qualified Reader+import qualified State+import qualified Stream+import qualified Writer+import qualified WordCount+++spec :: Spec+spec = do+  describe "CountLog" CountLog.spec+  describe "Error" Error.spec+  describe "Reader" Reader.spec+  describe "State" State.spec+  describe "Stream" Stream.spec+  describe "Writer" Writer.spec+  describe "WordCount" WordCount.spec+++main :: IO ()+main = do+  let cfg = defaultConfig+#ifdef MIN_VERSION_hspec_jenkins+        { configFormatter = Just xmlFormatter }+#endif+  hspecWith cfg spec
+ examples/Test/Common.hs view
@@ -0,0 +1,45 @@+module Test.Common+  ( shouldPrint+  , shouldNotPrint+  , withInput+  ) where++import GHC.IO.Handle (hDuplicate, hDuplicateTo)+import System.IO+import System.IO.Silently (capture_)+import System.IO.Temp (withSystemTempFile)+import Test.Hspec+import UnliftIO.Exception (bracket)+++shouldPrint :: IO a -> String -> IO ()+shouldPrint action expected = do+  actual <- capture_ action+  actual `shouldBe` expected+++shouldNotPrint :: IO a -> String -> IO ()+shouldNotPrint action expected = do+  actual <- capture_ action+  actual `shouldNotBe` expected+++-- | Execute the given action with @stdin@ redirected to read the given input.+withInput :: IO a -> String -> IO a+withInput action input =+  withSystemTempFile "capability-mock-input" $ \tmpFile tmpOut -> do+    -- write input to temp-file+    hPutStr tmpOut input+    hClose tmpOut+    -- read stdin from temp-file+    buffering <- hGetBuffering stdin+    withFile tmpFile ReadMode $ \tmpIn -> do+      let redirect = do+            old <- hDuplicate stdin+            hDuplicateTo tmpIn stdin+            pure old+          restore old = do+            hDuplicateTo old stdin+            hSetBuffering stdin buffering+            hClose old+      bracket redirect restore (\_ -> action)
+ examples/WordCount.hs view
@@ -0,0 +1,155 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeInType #-}++module WordCount where++import Capability.Reader+import Capability.State+import Capability.Writer+import Control.Lens (ifor_)+import Data.Coerce (coerce)+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Control.Monad.Reader (ReaderT (..))+import Data.IORef+import Data.Text (Text)+import qualified Data.Text as Text+import Data.Monoid (Sum (..))+import GHC.Generics (Generic)+import Test.Common+import Test.Hspec++-- | Accumulating key-value mapping.+--+-- The 'Monoid' instance will 'mappend'+-- values to keys that occur on both sides.+newtype Accum map = Accum map++instance (Ord k, Semigroup v)+  => Semigroup (Accum (Map k v)) where+    (<>) = coerce $ Map.unionWith @k @v (<>)++instance (Ord k, Semigroup v)+  => Monoid (Accum (Map k v)) where+    mempty = coerce $ Map.empty @k @v+    mappend = (<>)+++-- | Counts occurrences of values @k@.+newtype Occurrences k = Occurrences (Map k Int)+  deriving (Monoid, Semigroup) via Accum (Map k (Sum Int))+  deriving Show++-- | A single occurrence of the given value.+oneOccurrence :: k -> Occurrences k+oneOccurrence k = Occurrences $ Map.singleton k 1+++-- | Count the occurrence of a single letter.+countLetter ::+  HasWriter "letterCount" (Occurrences Char) m+  => Char -> m ()+countLetter letter = tell @"letterCount" (oneOccurrence letter)++-- | Count the occurrence of a single word.+countWord ::+  HasWriter "wordCount" (Occurrences Text) m+  => Text -> m ()+countWord word = tell @"wordCount" (oneOccurrence word)+++-- | Count the occurrence of a single word and all the letters in it.+countWordAndLetters ::+  ( HasWriter "letterCount" (Occurrences Char) m+  , HasWriter "wordCount" (Occurrences Text) m )+  => Text -> m ()+countWordAndLetters word = do+  countWord word+  mapM_ countLetter (Text.unpack word)+++-- | Count the occurrences of words and letters in a text,+-- excluding white space.+countWordsAndLettersInText ::+  ( HasWriter "letterCount" (Occurrences Char) m+  , HasWriter "wordCount" (Occurrences Text) m )+  => Text -> m ()+countWordsAndLettersInText text =+  mapM_ countWordAndLetters (Text.words text)+++-- | Counter application context.+data CounterCtx = CounterCtx+  { letterCount :: IORef (Occurrences Char)+    -- ^ Counting letter occurrences.+  , wordCount :: IORef (Occurrences Text)+    -- ^ Counting word occurrences.+  } deriving Generic++-- | Counter application monad.+newtype Counter a = Counter { runCounter :: CounterCtx -> IO a }+  deriving (Functor, Applicative, Monad) via (ReaderT CounterCtx IO)+  deriving (HasWriter "letterCount" (Occurrences Char)) via+    (WriterLog  -- Generate HasWriter using HasState of Monoid+    (ReaderIORef  -- Generate HasState from HasReader of IORef+    (Field "letterCount" "ctx"  -- Focus on the field letterCount+    (MonadReader  -- Generate HasReader using mtl MonadReader+    (ReaderT CounterCtx IO)))))  -- Use mtl ReaderT newtype+  deriving (HasWriter "wordCount" (Occurrences Text)) via+    WriterLog (ReaderIORef+    (Field "wordCount" "ctx" (MonadReader (ReaderT CounterCtx IO))))+++-- | Given a text count the occurrences of all words and letters in it,+-- excluding white space, and print the outcome to standard output.+wordAndLetterCount :: Text -> IO ()+wordAndLetterCount text = do+  lettersRef <- newIORef $ Occurrences Map.empty+  wordsRef <- newIORef $ Occurrences Map.empty+  let ctx = CounterCtx+        { letterCount = lettersRef+        , wordCount = wordsRef+        }+      counter :: Counter ()+      counter = countWordsAndLettersInText text+  runCounter counter ctx+  let printOccurrencesOf name ref = do+        putStrLn name+        Occurrences occurrences <- readIORef ref+        ifor_ occurrences $ \item num ->+          putStrLn $ show item ++ ": " ++ show num+  printOccurrencesOf "Letters" lettersRef+  printOccurrencesOf "Words" wordsRef+++----------------------------------------------------------------------+-- Test Cases++spec :: Spec+spec = do+  describe "Counter" $ do+    it "handles the empty text" $+      wordAndLetterCount "" `shouldPrint`+        "Letters\n\+        \Words\n"+    it "handles one word" $+      wordAndLetterCount "banana" `shouldPrint`+        "Letters\n'a': 3\n'b': 1\n'n': 2\n\+        \Words\n\"banana\": 1\n"+    it "handles two words" $+      wordAndLetterCount "mississipi river" `shouldPrint`+        "Letters\n'e': 1\n'i': 5\n'm': 1\n'p': 1\n'r': 2\n's': 4\n'v': 1\n\+        \Words\n\"mississipi\": 1\n\"river\": 1\n"+    it "handles two lines" $+      wordAndLetterCount "banana apple\napple banana" `shouldPrint`+        "Letters\n'a': 8\n'b': 2\n'e': 2\n'l': 2\n'n': 4\n'p': 4\n\+        \Words\n\"apple\": 2\n\"banana\": 2\n"
+ examples/Writer.hs view
@@ -0,0 +1,87 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeInType #-}++-- | Example uses and instances of the @HasWriter@ capability.+module Writer where++import Capability.State+import Capability.Writer+import Control.Monad.State.Strict (State, StateT (..), runState)+import Data.Monoid (Sum (..))+import Test.Hspec++----------------------------------------------------------------------+-- Example Programs++-- | Increase a counter using a writer monad.+useWriter :: HasWriter "count" (Sum Int) m => m ()+useWriter = do+  tell @"count" 1+  tell @"count" 2+  tell @"count" 3+++-- | Mix writer and state monad operations on the same tag.+--+-- Note, mixing capabilities on the same tag like this is inadvisable in+-- real applications.  The @HasState@ capability could be used to clear the+-- accumulated outcome of the @HasWriter@ capability.+mixWriterState+  :: (HasState "count" Int m, HasWriter "count" (Sum Int) m)+  => m Int+mixWriterState = do+  tell @"count" 1+  one <- get @"count"+  tell @"count" $ Sum one+  pure one+++----------------------------------------------------------------------+-- Instances++-- | @HasWriter w@ capability derived from @HasState w@.+--+-- The monoid instance ('Data.Monoid.Sum') is also selected in the deriving+-- via clause.+newtype WriterM a = WriterM (State Int a)+  deriving (Functor, Applicative, Monad)+  deriving (HasWriter "count" (Sum Int))+    via WriterLog (Coerce (Sum Int) (MonadState (State Int)))++runWriterM :: WriterM a -> (a, Int)+runWriterM (WriterM m) = runState m 0+++-- | @HasWriter w@ capability derived from @HasState w@, while also exposing+-- the underlying @HasState@.+--+-- Note, that this is inadvisable in real applications.+-- See caveat on 'mixWriterState'.+newtype BadWriterM a = BadWriterM (State Int a)+  deriving (Functor, Applicative, Monad)+  deriving (HasWriter "count" (Sum Int))+    via WriterLog (Coerce (Sum Int) (MonadState (State Int)))+  deriving (HasState "count" Int)+    via MonadState (State Int)++runBadWriterM :: BadWriterM a -> (a, Int)+runBadWriterM (BadWriterM m) = runState m 0+++----------------------------------------------------------------------+-- Test Cases++spec :: Spec+spec = do+  describe "WriterM" $+    it "evaluates useWriter" $+      runWriterM useWriter `shouldBe` ((), 6)+  describe "BadWriterM" $ do+    it "evaluates useWriter" $+      runBadWriterM useWriter `shouldBe` ((), 6)+    it "evaluates mixWriterState" $+      runBadWriterM mixWriterState `shouldBe` (1, 2)
+ src/Capability.hs view
@@ -0,0 +1,90 @@+-- | A capability is a type class over a monad which specifies the effects that+-- a function is allowed to perform. Capabilities differ from traditional monad+-- transformer type classes in that they are completely independent of the way+-- the monad is constructed. A state capability can for instance be implemented+-- as a lens on a field in a larger state monad, or an error capability could+-- provide for throwing only a subset of the errors of an error monad.+--+-- This library defines several standard, reusable capabilities that replace the+-- mtl's monad-transformer type classes. Because capabilities are not tied to+-- a particular implementation of the monad, they cannot be discharged by+-- instance resolution. Instead this library provides combinators in the form of+-- newtypes with instances, to be used with deriving-via. To learn about+-- deriving via, watch Baldur Blondal's introductory video+-- <https://skillsmatter.com/skillscasts/10934-lightning-talk-stolen-instances-taste-just-fine>.+--+-- By way of comparison, with the mtl you would write something like+--+-- @+-- foo :: (MonadReader E, MonadState S) => a -> m ()+-- @+--+-- You can use @foo@ at type @a -> ReaderT E (State S)@. But you can't use @foo@+-- with the @ReaderT@ pattern+-- <https://www.fpcomplete.com/blog/2017/06/readert-design-pattern>. With this+-- library, you would instead have:+--+-- @+-- foo :: (HasReader "conf" E, HasState "st" S) => a -> m ()+-- @+--+-- Where @"conf"@ and @"st"@ are the names (also referred to as tags) of the+-- capabilities demanded by foo. Contrary to the mtl, capabilities are named,+-- rather than disambiguated by the type of their implied state, or exception.+-- This makes it easy to have multiple state capabilities.+--+-- To /provide/ these capabilities, for instance with the ReaderT pattern, do as+-- follows (for a longer form tutorial, check the+-- <https://github.com/tweag/capability#readme README>):+--+-- @+-- newtype MyM a = MyM (ReaderT (E, IORef s))+--   deriving (Functor, Applicative, Monad)+--   deriving (HasState "st" Int) via+--     ReaderIORef (Rename 2 (Pos 2 ()+--     (MonadReader (ReaderT (E, IORef s) IO))))+--   deriving (HasReader "conf" Int) via+--     (Rename 1 (Pos 1 ()+--     (MonadReader (ReaderT (E, IORef s) IO))))+-- @+--+-- Then you can use @foo@ at type @MyM@. Or any other type which can provide+-- these capabilites.+--+-- == Module structure+--+-- Each module introduces a capability type class (or several related type+-- classes). Each class comes with a number of instances on newtypes (each+-- newtype should be seen as a combinator to be used with deriving-via to+-- provide the capability). Many newtypes come from the common+-- "Capability.Accessors" module (re-exported by each of the other modules),+-- which in particular contains a number of ways to address components of a data+-- type using the generic-lens library.+--+-- * "Capability.Reader" reader effects+-- * "Capability.State" state effects+-- * "Capability.Writer" writer effects+-- * "Capability.Error" throw and catch errors+-- * "Capability.Stream" streaming effect (aka generators)+--+-- Some of the capability modules have a “discouraged” companion (such as+-- "Capability.Writer.Discouraged"). These modules contain deriving-via+-- combinators which you can use if you absolutely must: they are correct, but+-- inefficient, so we recommend that you do not.+--+-- == Further considerations+--+-- The tags of capabilities can be of any kind, they are not restricted to+-- symbols. When exporting functions demanding capabilities in libraries, it is+-- recommended to use a type as follows:+--+-- @+-- data Conf+--+-- foo :: HasReader Conf C => m ()+-- @+--+-- This way, @Conf@ can be qualified in case of a name conflict with another+-- library.++module Capability where
+ src/Capability/Accessors.hs view
@@ -0,0 +1,161 @@+-- | Defines @newtype@s that serve as combinators+-- to compose deriving via strategies.++{-# LANGUAGE DataKinds #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE TypeInType #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UnboxedTuples #-}++module Capability.Accessors+  ( Coerce(..)+  , Rename(..)+  , Field(..)+  , Pos(..)+  , Ctor(..)+  , Lift(..)+  , (:.:)(..)+  ) where++import Control.Monad.IO.Class (MonadIO)+import Control.Monad.Primitive (PrimMonad)+import GHC.TypeLits (Nat, Symbol)++-- | Coerce the type in the context @m@ to @to@.+--+-- Example:+--+-- @+-- newtype MyInt = MyInt Int+-- newtype MyReader a = MyReader (Reader Int a)+--   deriving (HasReader "a" MyInt) via+--     Coerce MyInt (MonadReader (Reader Int))+-- @+--+-- Converts the @'Capability.Reader.HasReader' \"a\" Int@ instance of+-- @'Capability.Reader.MonadReader' (Reader Int)@ to a+-- @'Capability.Reader.HasReader' \"a\" MyInt@+-- instance using @Coercible Int MyInt@.+newtype Coerce (to :: *) m (a :: *) = Coerce (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)++-- | Rename the tag.+--+-- Example:+--+-- @+-- newtype MyReader a = MyReader (Reader Int a)+--   deriving (HasReader "foo" Int) via+--     Rename "bar" (MonadReader (Reader Int))+-- @+--+-- Converts the @'Capability.Reader.HasReader' \"bar\" Int@ instance of+-- @'Capability.Reader.MonadReader' (Reader Int)@ to a+-- @'Capability.Reader.HasReader' \"foo\" Int@ instance by renaming the tag.+--+-- Note, that 'Capability.Reader.MonadReader' itself does not fix a tag,+-- and @Rename@ is redundant in this example.+--+-- See 'Pos' below for a common use-case.+newtype Rename (oldtag :: k) m (a :: *) = Rename (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)++-- | Access the record field @field@ in the context @m@.+--+-- Example:+--+-- @+-- data Foo = Foo { foo :: Int }+-- newtype MyReader a = MyReader (Reader Foo a)+--   deriving (HasReader "foo" Int) via+--     Field "foo" () (MonadReader (Reader Foo))+-- @+--+-- Converts the @'Capability.Reader.HasReader' () Foo@ instance of+-- @'Capability.Reader.MonadReader' (Reader Foo)@ to a+-- @'Capability.Reader.HasReader' \"foo\" Int@+-- instance by focusing on the field @foo@ in the @Foo@ record.+--+-- See 'Rename' for a way to change the tag.+newtype Field (field :: Symbol) (oldtag :: k) m (a :: *) = Field (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)++-- | Access the value at position @pos@ in the context @m@.+--+-- Example:+--+-- @+-- newtype MyReader a = MyReader (Reader (Int, Bool) a)+--   deriving (HasReader 1 Int) via+--     Pos 1 () (MonadReader (Reader (Int, Bool)))+-- @+--+-- Converts the @'Capability.Reader.HasReader' () (Int, Bool)@ instance of+-- @'Capability.Reader.MonadReader' (Reader (Int, Bool))@ to a+-- @'Capability.Reader.HasReader' 1 Int@ instance+-- by focusing on the first element of the tuple.+--+-- The implied number tag can be renamed to a more descriptive name using+-- the 'Rename' combinator:+--+-- @+-- newtype MyReader a = MyReader (Reader (Int, Bool) a)+--   deriving (HasReader "foo" Int) via+--     Rename 1 (Pos 1 () (MonadReader (Reader (Int, Bool))))+-- @+newtype Pos (pos :: Nat) (oldtag :: k) m (a :: *) = Pos (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)++-- | Choose the given constructor in the sum-type in context @m@.+--+-- Example:+--+-- @+-- data MyError = ErrA String | ErrB String+-- newtype MyExcept a = MyExcept (ExceptT MyError Identity a)+--   deriving (HasThrow \"ErrB" String) via+--     Ctor \"ErrB" () (MonadError (ExceptT MyError Identity))+-- @+--+-- Converts the @'Capability.Error.HasThrow' () \"MyError\"@ instance of+-- @'Capability.Error.MonadError' (ExceptT MyError Identity)@ to a+-- @'Capability.Error.HasThrow' \"ErrB\" String@+-- instance by wrapping thrown @String@s in the @ErrB@ constructor.+newtype Ctor (ctor :: Symbol) (oldtag :: k) m (a :: *) = Ctor (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)++-- | Skip one level in a monad transformer stack.+--+-- Note, that instances generated with this strategy can incur a performance+-- penalty.+--+-- Example:+--+-- @+-- newtype MyStates a = MyStates (StateT Int (State Bool) a)+--   deriving (HasState "foo" Bool) via+--     Lift (StateT Int (MonadState (State Bool)))+-- @+--+-- Uses the 'Control.Monad.Trans.Class.MonadTrans' instance of+-- @StateT Int@ to lift+-- the @'Capability.State.HasState' "\foo\" Bool@ instance of the underlying+-- @'Capability.State.MonadState' (State Bool)@ over the+-- @StateT Int@ monad transformer.+newtype Lift m (a :: *) = Lift (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)+++-- | Compose two accessors.+--+-- This is not necessary in deriving via clauses, but in places where a+-- transformer is expected as a type argument. E.g. 'HasError.wrapError'.+newtype (:.:)+  (t2 :: (* -> *) -> * -> *)+  (t1 :: (* -> *) -> * -> *)+  (m :: * -> *)+  (a :: *)+  = (:.:) (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)+infixr 9 :.:
+ src/Capability/Error.hs view
@@ -0,0 +1,423 @@+-- | Defines capabilities for actions that may fail or throw exceptions,+-- and optionally may catch exceptions.+--+-- Monads based on @IO@ can use the underlying @IO@ exception mechanism+-- to that end. The details of synchronous and asynchronous exception+-- handling depend on the strategy used.+-- See 'MonadThrow', 'SafeExceptions', 'MonadUnliftIO'.+--+-- The associated tag can be used to select the exception type, or to+-- select a layer in monad transformer stacks. Note, that it is illegal+-- to have multiple tags refer to overlapping exception types in the same+-- layer. Consider the following example+--+-- @+-- newtype M a = M (IO a)+--   deriving (HasThrow "foo" IOException) via+--     MonadUnliftIO IO+--   deriving (HasThrow "bar" SomeException) via+--     MonadUnliftIO IO+-- @+--+-- In this case the tags @"foo"@ and @"bar"@ refer to overlapping exception+-- types in the same layer, because @catch \@"bar"@ may also catch an exception+-- thrown under @"foo"@.++{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeInType #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE UndecidableInstances #-}++{-# OPTIONS_GHC -Wno-simplifiable-class-constraints #-}++module Capability.Error+  ( -- * Interface+    HasThrow(..)+  , throw+  , HasCatch(..)+  , catch+  , catchJust+  , wrapError+    -- * Strategies+  , MonadError(..)+  , MonadThrow(..)+  , MonadCatch(..)+  , SafeExceptions(..)+  , MonadUnliftIO(..)+    -- ** Modifiers+  , module Capability.Accessors+    -- * Re-exported+  , Exception(..)+  , Typeable+  ) where++import Capability.Accessors+import Control.Exception (Exception(..))+import qualified Control.Exception.Safe as Safe+import Control.Lens (preview, review)+import Control.Monad ((<=<))+import qualified Control.Monad.Catch as Catch+import qualified Control.Monad.Except as Except+import Control.Monad.IO.Class (MonadIO)+import qualified Control.Monad.IO.Unlift as UnliftIO+import Control.Monad.Primitive (PrimMonad)+import Control.Monad.Trans.Class (MonadTrans, lift)+import Control.Monad.Trans.Control (MonadTransControl(..))+import Data.Coerce (Coercible, coerce)+import qualified Data.Generics.Sum.Constructors as Generic+import Data.Typeable (Typeable)+import GHC.Exts (Proxy#, proxy#)+import qualified UnliftIO.Exception as UnliftIO++-- | Capability to throw exceptions of type @e@ under @tag@.+--+-- @HasThrow@/@HasCatch@ capabilities at different tags should be independent.+-- See 'HasCatch'.+class Monad m+  => HasThrow (tag :: k) (e :: *) (m :: * -> *) | tag m -> e+  where+    -- | For technical reasons, this method needs an extra proxy argument.+    -- You only need it if you are defining new instances of 'HasReader'.+    -- Otherwise, you will want to use 'throw'.+    -- See 'throw' for more documentation.+    throw_ :: Proxy# tag -> e -> m a++-- | Throw an exception in the specified exception capability.+throw :: forall tag e m a. HasThrow tag e m => e -> m a+throw = throw_ (proxy# @_ @tag)+{-# INLINE throw #-}++-- | Capability to catch exceptions of type @e@ under @tag@.+--+-- @HasThrow@/@HasCatch@ capabilities at different tags should be independent.+-- In particular, the following program should throw @SomeError@ and not+-- return @()@.+-- > example ::+-- >   (HasThrow "Left" SomeError m, HasCatch "Right" SomeError m)+-- >   => m ()+-- > example =+-- >   catch @"Left"+-- >     (throw @"Right" SomeError)+-- >     \_ -> pure ()+--+-- See 'wrapError' for a way to combine multiple exception types into one.+class HasThrow tag e m+  => HasCatch (tag :: k) (e :: *) (m :: * -> *) | tag m -> e+  where+    -- | For technical reasons, this method needs an extra proxy argument.+    -- You only need it if you are defining new instances of 'HasReader'.+    -- Otherwise, you will want to use 'catch'.+    -- See 'catch' for more documentation.+    catch_ :: Proxy# tag -> m a -> (e -> m a) -> m a+    -- | For technical reasons, this method needs an extra proxy argument.+    -- You only need it if you are defining new instances of 'HasReader'.+    -- Otherwise, you will want to use 'catchJust'.+    -- See 'catchJust' for more documentation.+    catchJust_ :: Proxy# tag -> (e -> Maybe b) -> m a -> (b -> m a) -> m a++-- | Provide a handler for exceptions thrown in the given action+-- in the given exception capability.+catch :: forall tag e m a. HasCatch tag e m => m a -> (e -> m a) -> m a+catch = catch_ (proxy# @_ @tag)+{-# INLINE catch #-}++-- | Like 'catch', but only handle the exception if the provided function+-- returns 'Just'.+catchJust :: forall tag e m a b. HasCatch tag e m+  => (e -> Maybe b) -> m a -> (b -> m a) -> m a+catchJust = catchJust_ (proxy# @_ @tag)+{-# INLINE catchJust #-}++-- | Wrap exceptions @inner@ originating from the given action according to+-- the accessor @t@.+--+-- Example:+--+-- > wrapError+-- >   @"AppError" @"ComponentError" @(Ctor "ComponentError" "AppError")+-- >   component+-- >+-- > component :: HasError "ComponentError" ComponentError m => m ()+-- > data AppError = ComponentError ComponentError+--+-- This function is experimental and subject to change.+-- See <https://github.com/tweag/capability/issues/46>.+wrapError :: forall outertag innertag t outer inner m a.+  ( forall x. Coercible (t m x) (m x)+  , forall m'. HasCatch outertag outer m'+    => HasCatch innertag inner (t m')+  , HasCatch outertag outer m )+  => (forall m'. HasCatch innertag inner m' => m' a) -> m a+wrapError action = coerce @(t m a) action+{-# INLINE wrapError #-}++-- XXX: Does it make sense to add a HasMask capability similar to @MonadMask@?+--   What would the meaning of the tag be?++-- | Derive 'HasError from @m@'s 'Control.Monad.Except.MonadError' instance.+newtype MonadError m (a :: *) = MonadError (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)++instance Except.MonadError e m => HasThrow tag e (MonadError m) where+  throw_ :: forall a. Proxy# tag -> e -> MonadError m a+  throw_ _ = coerce @(e -> m a) $ Except.throwError+  {-# INLINE throw_ #-}++instance Except.MonadError e m => HasCatch tag e (MonadError m) where+  catch_ :: forall a.+    Proxy# tag -> MonadError m a -> (e -> MonadError m a) -> MonadError m a+  catch_ _ = coerce @(m a -> (e -> m a) -> m a) $ Except.catchError+  {-# INLINE catch_ #-}+  catchJust_ :: forall a b.+    Proxy# tag+    -> (e -> Maybe b)+    -> MonadError m a+    -> (b -> MonadError m a)+    -> MonadError m a+  catchJust_ tag f m h = catch_ tag m $ \e -> maybe (throw_ tag e) h $ f e+  {-# INLINE catchJust_ #-}++-- | Derive 'HasThrow' from @m@'s 'Control.Monad.Catch.MonadThrow' instance.+newtype MonadThrow (e :: *) m (a :: *) = MonadThrow (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)++instance (Catch.Exception e, Catch.MonadThrow m)+  => HasThrow tag e (MonadThrow e m)+  where+    throw_ :: forall a. Proxy# tag -> e -> MonadThrow e m a+    throw_ _ = coerce @(e -> m a) $ Catch.throwM+    {-# INLINE throw_ #-}++-- | Derive 'HasCatch from @m@'s 'Control.Monad.Catch.MonadCatch instance.+newtype MonadCatch (e :: *) m (a :: *) = MonadCatch (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)+  deriving (HasThrow tag e) via MonadThrow e m++instance (Catch.Exception e, Catch.MonadCatch m)+  => HasCatch tag e (MonadCatch e m)+  where+    catch_ :: forall a.+      Proxy# tag+      -> MonadCatch e m a+      -> (e -> MonadCatch e m a)+      -> MonadCatch e m a+    catch_ _ = coerce @(m a -> (e -> m a) -> m a) $ Catch.catch+    {-# INLINE catch_ #-}+    catchJust_ :: forall a b.+      Proxy# tag+      -> (e -> Maybe b)+      -> MonadCatch e m a+      -> (b -> MonadCatch e m a)+      -> MonadCatch e m a+    catchJust_ _ = coerce @((e -> Maybe b) -> m a -> (b -> m a) -> m a) $+      Catch.catchJust+    {-# INLINE catchJust_ #-}+++-- | Derive 'HasError' using the functionality from the @safe-exceptions@+-- package.+newtype SafeExceptions (e :: *) m (a :: *) = SafeExceptions (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)++instance (Safe.Exception e, Safe.MonadThrow m)+  => HasThrow tag e (SafeExceptions e m)+  where+    throw_ :: forall a. Proxy# tag -> e -> SafeExceptions e m a+    throw_ _ = coerce @(e -> m a) $ Safe.throw+    {-# INLINE throw_ #-}++instance (Safe.Exception e, Safe.MonadCatch m)+  => HasCatch tag e (SafeExceptions e m)+  where+    catch_ :: forall a.+      Proxy# tag+      -> SafeExceptions e m a+      -> (e -> SafeExceptions e m a)+      -> SafeExceptions e m a+    catch_ _ = coerce @(m a -> (e -> m a) -> m a) $ Safe.catch+    {-# INLINE catch_ #-}+    catchJust_ :: forall a b.+      Proxy# tag+      -> (e -> Maybe b)+      -> SafeExceptions e m a+      -> (b -> SafeExceptions e m a)+      -> SafeExceptions e m a+    catchJust_ _ = coerce @((e -> Maybe b) -> m a -> (b -> m a) -> m a) $+      Safe.catchJust+    {-# INLINE catchJust_ #-}++-- | Derive 'HasError' using the functionality from the @unliftio@ package.+newtype MonadUnliftIO (e :: *) m (a :: *) = MonadUnliftIO (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)++instance (UnliftIO.Exception e, MonadIO m)+  => HasThrow tag e (MonadUnliftIO e m)+  where+    throw_ :: forall a. Proxy# tag -> e -> MonadUnliftIO e m a+    throw_ _ = coerce @(e -> m a) $ UnliftIO.throwIO+    {-# INLINE throw_ #-}++instance (UnliftIO.Exception e, UnliftIO.MonadUnliftIO m)+  => HasCatch tag e (MonadUnliftIO e m)+  where+    catch_ :: forall a.+      Proxy# tag+      -> MonadUnliftIO e m a+      -> (e -> MonadUnliftIO e m a)+      -> MonadUnliftIO e m a+    catch_ _ = coerce @(m a -> (e -> m a) -> m a) $ UnliftIO.catch+    {-# INLINE catch_ #-}+    catchJust_ :: forall a b.+      Proxy# tag+      -> (e -> Maybe b)+      -> MonadUnliftIO e m a+      -> (b -> MonadUnliftIO e m a)+      -> MonadUnliftIO e m a+    catchJust_ _ = coerce @((e -> Maybe b) -> m a -> (b -> m a) -> m a) $+      UnliftIO.catchJust+    {-# INLINE catchJust_ #-}++-- | Rename the tag.+--+-- Apply cautiously. See @'HasCatch' newtag e ('Rename' oldtag m)@.+instance HasThrow oldtag e m => HasThrow newtag e (Rename oldtag m) where+  throw_ :: forall a. Proxy# newtag -> e -> Rename oldtag m a+  throw_ _ = coerce @(e -> m a) $ throw @oldtag+  {-# INLINE throw_ #-}++-- | Rename the tag.+--+-- Apply cautiously. E.g. the following code produces colliding instances,+-- where exceptions thrown in @\"Foo\"@ cannot be distinguished from exceptions+-- thrown in @\"Bar\"@ and vice-versa.+--+-- > newtype Bad a = Bad (IO a)+-- >   deriving (Functor, Applicative, Monad)+-- >   deriving+-- >     ( HasThrow "Foo" m+-- >     , HasCatch "Foo" m+-- >     ) via Rename () (MonadUnliftIO SomeError IO)+-- >   deriving+-- >     ( HasThrow "Bar" m+-- >     , HasCatch "Bar" m+-- >     ) via Rename () (MonadUnliftIO SomeError IO)+instance HasCatch oldtag e m => HasCatch newtag e (Rename oldtag m) where+  catch_ :: forall a.+    Proxy# newtag+    -> Rename oldtag m a+    -> (e -> Rename oldtag m a)+    -> Rename oldtag m a+  catch_ _ = coerce @(m a -> (e -> m a) -> m a) $ catch @oldtag+  {-# INLINE catch_ #-}+  catchJust_ :: forall a b.+    Proxy# newtag+    -> (e -> Maybe b)+    -> Rename oldtag m a+    -> (b -> Rename oldtag m a)+    -> Rename oldtag m a+  catchJust_ _ = coerce @((e -> Maybe b) -> m a -> (b -> m a) -> m a) $+    catchJust @oldtag+  {-# INLINE catchJust_ #-}++-- | Wrap the exception @e@ with the constructor @ctor@ to throw an exception+-- of type @sum@.+instance+  -- The constraint raises @-Wsimplifiable-class-constraints@. This could+  -- be avoided by instead placing @AsConstructor'@s constraints here.+  -- Unfortunately, it uses non-exported symbols from @generic-lens@.+  (Generic.AsConstructor' ctor sum e, HasThrow oldtag sum m)+  => HasThrow ctor e (Ctor ctor oldtag m)+  where+    throw_ :: forall a. Proxy# ctor -> e -> Ctor ctor oldtag m a+    throw_ _ = coerce @(e -> m a) $+      throw @oldtag . review (Generic._Ctor' @ctor @sum)+    {-# INLINE throw_ #-}++-- | Catch an exception of type @sum@ if its constructor matches @ctor@.+instance+  -- The constraint raises @-Wsimplifiable-class-constraints@. This could+  -- be avoided by instead placing @AsConstructor'@s constraints here.+  -- Unfortunately, it uses non-exported symbols from @generic-lens@.+  (Generic.AsConstructor' ctor sum e, HasCatch oldtag sum m)+  => HasCatch ctor e (Ctor ctor oldtag m)+  where+    catch_ :: forall a.+      Proxy# ctor+      -> Ctor ctor oldtag m a+      -> (e -> Ctor ctor oldtag m a)+      -> Ctor ctor oldtag m a+    catch_ _ = coerce @(m a -> (e -> m a) -> m a) $+      catchJust @oldtag @sum $ preview (Generic._Ctor' @ctor @sum)+    {-# INLINE catch_ #-}+    catchJust_ :: forall a b.+      Proxy# ctor+      -> (e -> Maybe b)+      -> Ctor ctor oldtag m a+      -> (b -> Ctor ctor oldtag m a)+      -> Ctor ctor oldtag m a+    catchJust_ _ = coerce @((e -> Maybe b) -> m a -> (b -> m a) -> m a) $ \f ->+      catchJust @oldtag @sum $ f <=< preview (Generic._Ctor' @ctor @sum)+    {-# INLINE catchJust_ #-}++-- | Lift one layer in a monad transformer stack.+instance+  ( HasThrow tag e m, MonadTrans t, Monad (t m) )+  => HasThrow tag e (Lift (t m))+  where+    throw_ :: forall a. Proxy# tag -> e -> Lift (t m) a+    throw_ tag = coerce @(e -> t m a) $ lift . throw_ tag+    {-# INLINE throw_ #-}++-- | Lift one layer in a monad transformer stack.+instance+  ( HasCatch tag e m, MonadTransControl t, Monad (t m) )+  => HasCatch tag e (Lift (t m))+  where+    catch_ :: forall a.+      Proxy# tag+      -> Lift (t m) a+      -> (e -> Lift (t m) a)+      -> Lift (t m) a+    catch_ tag = coerce @(t m a -> (e -> t m a) -> t m a) $ \m h ->+      liftWith (\run -> catch_ tag (run m) (run . h)) >>= restoreT . pure+    {-# INLINE catch_ #-}+    catchJust_ :: forall a b.+      Proxy# tag+      -> (e -> Maybe b)+      -> Lift (t m) a+      -> (b -> Lift (t m) a)+      -> Lift (t m) a+    catchJust_ tag =+      coerce @((e -> Maybe b) -> t m a -> (b -> t m a) -> t m a) $ \f m h ->+        liftWith (\run -> catchJust_ tag f (run m) (run . h)) >>= restoreT . pure+    {-# INLINE catchJust_ #-}+++-- | Compose two accessors.+deriving via ((t2 :: (* -> *) -> * -> *) ((t1 :: (* -> *) -> * -> *) m))+  instance+  ( forall x. Coercible (m x) (t2 (t1 m) x)+  , Monad m, HasThrow tag e (t2 (t1 m)) )+  => HasThrow tag e ((t2 :.: t1) m)+++-- | Compose two accessors.+deriving via ((t2 :: (* -> *) -> * -> *) ((t1 :: (* -> *) -> * -> *) m))+  instance+  ( forall x. Coercible (m x) (t2 (t1 m) x)+  , Monad m, HasCatch tag e (t2 (t1 m)) )+  => HasCatch tag e ((t2 :.: t1) m)
+ src/Capability/Reader.hs view
@@ -0,0 +1,18 @@+-- | Defines a capability type class for a reader effect. A reader provides an+-- environment, say an initialization context or a configuration. The+-- environment held in the reader effect can be changed (with 'local') within+-- the scope of a sub-computation. Contrary to the "Capability.State", such+-- a change is local, and does not persist when the 'local' call ends.++module Capability.Reader+  ( -- * Interface+    module Capability.Reader.Internal.Class+    -- * Strategies+  , module Capability.Reader.Internal.Strategies+    -- ** Modifiers+  , module Capability.Accessors+  ) where++import Capability.Accessors+import Capability.Reader.Internal.Class+import Capability.Reader.Internal.Strategies
+ src/Capability/Reader/Internal/Class.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeInType #-}++{-# OPTIONS_HADDOCK hide #-}++module Capability.Reader.Internal.Class+  ( HasReader(..)+  , ask+  , asks+  , local+  , reader+  , magnify+  ) where++import Data.Coerce (Coercible, coerce)+import GHC.Exts (Proxy#, proxy#)++-- | Reader capability+--+-- An instance should fulfill the following laws.+-- At this point these laws are not definitive,+-- see <https://github.com/haskell/mtl/issues/5>.+--+-- prop> k <*> ask @t = ask @t <**> k+-- prop> ask @t *> m = m = m <* ask @t+-- prop> local @t f (ask @t) = fmap f (ask @t)+-- prop> local @t f . local @t g = local @t (g . f)+-- prop> local @t f (pure x) = pure x+-- prop> local @t f (m >>= \x -> k x) = local @t f m >>= \x -> local @t f (k x)+-- prop> reader @t f = f <$> ask @t+class Monad m+  => HasReader (tag :: k) (r :: *) (m :: * -> *) | tag m -> r+  where+    -- | For technical reasons, this method needs an extra proxy argument.+    -- You only need it if you are defining new instances of 'HasReader'.+    -- Otherwise, you will want to use 'ask'.+    -- See 'ask' for more documentation.+    ask_ :: Proxy# tag -> m r+    -- | For technical reasons, this method needs an extra proxy argument.+    -- You only need it if you are defining new instances of 'HasReader'.+    -- Otherwise, you will want to use 'local'.+    -- See 'local' for more documentation.+    local_ :: Proxy# tag -> (r -> r) -> m a -> m a+    -- | For technical reasons, this method needs an extra proxy argument.+    -- You only need it if you are defining new instances of 'HasReader'.+    -- Otherwise, you will want to use 'reader'.+    -- See 'reader' for more documentation.+    reader_ :: Proxy# tag -> (r -> a) -> m a++-- | @ask \@tag@+-- retrieves the environment of the reader capability @tag@.+ask :: forall tag r m. HasReader tag r m => m r+ask = ask_ (proxy# @_ @tag)+{-# INLINE ask #-}++-- | @asks \@tag@+-- retrieves the image by @f@ of the environment+-- of the reader capability @tag@.+--+-- prop> asks @tag f = f <$> ask @tag+asks :: forall tag r m a. HasReader tag r m => (r -> a) -> m a+asks f = f <$> ask @tag+{-# INLINE asks #-}++-- | @local \@tag f m@+-- runs the monadic action @m@ in a modified environment @e' = f e@,+-- where @e@ is the environment of the reader capability @tag@.+-- Symbolically: @return e = ask \@tag@.+local :: forall tag r m a. HasReader tag r m => (r -> r) -> m a -> m a+local = local_ (proxy# @_ @tag)+{-# INLINE local #-}++-- | @reader \@tag act@+-- lifts a purely environment-dependent action @act@ to a monadic action+-- in an arbitrary monad @m@ with capability @HasReader@.+--+-- It happens to coincide with @asks@: @reader = asks@.+reader :: forall tag r m a. HasReader tag r m => (r -> a) -> m a+reader = reader_ (proxy# @_ @tag)+{-# INLINE reader #-}++-- | Execute the given reader action on a sub-component of the current context+-- as defined by the given transformer @t@.+--+-- See 'Capability.State.zoom'.+--+-- This function is experimental and subject to change.+-- See <https://github.com/tweag/capability/issues/46>.+magnify :: forall outertag innertag t outer inner m a.+  ( forall x. Coercible (t m x) (m x)+  , forall m'. HasReader outertag outer m'+    => HasReader innertag inner (t m')+  , HasReader outertag outer m )+  => (forall m'. HasReader innertag inner m' => m' a) -> m a+magnify m = coerce @(t m a) m+{-# INLINE magnify #-}
+ src/Capability/Reader/Internal/Strategies.hs view
@@ -0,0 +1,224 @@+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeInType #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE UndecidableInstances #-}++{-# OPTIONS_GHC -Wno-orphans #-}+{-# OPTIONS_GHC -Wno-simplifiable-class-constraints #-}++{-# OPTIONS_HADDOCK hide #-}++module Capability.Reader.Internal.Strategies+  ( MonadReader(..)+  , ReadStatePure(..)+  , ReadState(..)+  ) where++import Capability.Accessors+import Capability.Reader.Internal.Class+import Capability.State.Internal.Class+import Control.Lens (over, view)+import Control.Monad.Catch (MonadMask, bracket)+import Control.Monad.IO.Class (MonadIO)+import Control.Monad.Primitive (PrimMonad)+import qualified Control.Monad.Reader.Class as Reader+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Control (MonadTransControl(..))+import Data.Coerce (Coercible, coerce)+import qualified Data.Generics.Product.Fields as Generic+import qualified Data.Generics.Product.Positions as Generic+import GHC.Exts (Proxy#)++-- | Derive 'HasReader' from @m@'s 'Control.Monad.Reader.Class.MonadReader'+-- instance.+newtype MonadReader (m :: * -> *) (a :: *) = MonadReader (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)++instance Reader.MonadReader r m => HasReader tag r (MonadReader m) where+  ask_ _ = coerce @(m r) Reader.ask+  {-# INLINE ask_ #-}+  local_+    :: forall a. Proxy# tag -> (r -> r) -> MonadReader m a -> MonadReader m a+  local_ _ = coerce @((r -> r) -> m a -> m a) Reader.local+  {-# INLINE local_ #-}+  reader_ :: forall a. Proxy# tag -> (r -> a) -> MonadReader m a+  reader_ _ = coerce @((r -> a) -> m a) Reader.reader+  {-# INLINE reader_ #-}++-- | Convert a /pure/ state monad into a reader monad.+--+-- /Pure/ meaning that the monad stack does not allow catching exceptions.+-- Otherwise, an exception occurring in the action passed to 'local' could cause+-- the context to remain modified outside of the call to 'local'. E.g.+--+-- > local @tag (const r') (throw MyException)+-- > `catch` \MyException -> ask @tag+--+-- returns @r'@ instead of the previous value.+--+-- Note, that no @MonadIO@ instance is provided, as this would allow catching+-- exceptions.+--+-- See 'ReadState'.+newtype ReadStatePure (m :: * -> *) (a :: *) = ReadStatePure (m a)+  deriving (Functor, Applicative, Monad)++instance HasState tag r m => HasReader tag r (ReadStatePure m) where+  ask_ _ = coerce @(m r) $ get @tag+  {-# INLINE ask_ #-}+  local_ :: forall a.+    Proxy# tag -> (r -> r) -> ReadStatePure m a -> ReadStatePure m a+  local_ _ f = coerce @(m a -> m a) $ \m -> do+    r <- state @tag $ \r -> (r, f r)+    m <* put @tag r+  {-# INLINE local_ #-}+  reader_ :: forall a. Proxy# tag -> (r -> a) -> ReadStatePure m a+  reader_ _ = coerce @((r -> a) -> m a) $ gets @tag+  {-# INLINE reader_ #-}++-- | Convert a state monad into a reader monad.+--+-- Use this if the monad stack allows catching exceptions.+--+-- See 'ReadStatePure'.+newtype ReadState (m :: * -> *) (a :: *) = ReadState (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)++instance+  (HasState tag r m, MonadMask m)+  => HasReader tag r (ReadState m)+  where+    ask_ _ = coerce @(m r) $ get @tag+    {-# INLINE ask_ #-}+    local_ :: forall a.+      Proxy# tag -> (r -> r) -> ReadState m a -> ReadState m a+    local_ _ f = coerce @(m a -> m a) $ \action ->+      let+        setAndSave = state @tag $ \r -> (r, f r)+        restore r = put @tag r+      in+      bracket setAndSave restore $ \_ -> action+    {-# INLINE local_ #-}+    reader_ :: forall a. Proxy# tag -> (r -> a) -> ReadState m a+    reader_ _ = coerce @((r -> a) -> m a) $ gets @tag+    {-# INLINE reader_ #-}++-- | Convert the environment using safe coercion.+instance+  ( Coercible from to, HasReader tag from m+  , forall x y. Coercible x y => Coercible (m x) (m y) )+  => HasReader tag to (Coerce to m)+  where+    ask_ tag = coerce @(m from) $ ask_ tag+    {-# INLINE ask_ #-}+    local_+      :: forall a. Proxy# tag -> (to -> to) -> Coerce to m a -> Coerce to m a+    local_ tag = coerce @((from -> from) -> m a -> m a) $ local_ tag+    {-# INLINE local_ #-}+    reader_ :: forall a. Proxy# tag -> (to -> a) -> Coerce to m a+    reader_ tag = coerce @((from -> a) -> m a) $ reader_ tag+    {-# INLINE reader_ #-}++-- | Rename the tag.+instance HasReader oldtag r m => HasReader newtag r (Rename oldtag m) where+  ask_ _ = coerce @(m r) $ ask @oldtag+  {-# INLINE ask_ #-}+  local_ :: forall a.+    Proxy# newtag -> (r -> r) -> Rename oldtag m a -> Rename oldtag m a+  local_ _ = coerce @((r -> r) -> m a -> m a) $ local @oldtag+  {-# INLINE local_ #-}+  reader_ :: forall a. Proxy# newtag -> (r -> a) -> Rename oldtag m a+  reader_ _ = coerce @((r -> a) -> m a) $ reader @oldtag+  {-# INLINE reader_ #-}+++-- | Zoom in on the record field @field@ of type @v@+-- in the environment @record@.+instance+  -- The constraint raises @-Wsimplifiable-class-constraints@.+  -- This could be avoided by instead placing @HasField'@s constraints here.+  -- Unfortunately, it uses non-exported symbols from @generic-lens@.+  ( tag ~ field, Generic.HasField' field record v, HasReader oldtag record m )+  => HasReader tag v (Field field oldtag m)+  where+    ask_ _ = coerce @(m v) $+      asks @oldtag $ view (Generic.field' @field)+    {-# INLINE ask_ #-}+    local_ :: forall a.+      Proxy# tag+      -> (v -> v)+      -> Field field oldtag m a+      -> Field field oldtag m a+    local_ _ = coerce @((v -> v) -> m a -> m a) $+      local @oldtag . over (Generic.field' @field)+    {-# INLINE local_ #-}+    reader_ :: forall a.+      Proxy# tag+      -> (v -> a)+      -> Field field oldtag m a+    reader_ _ f = coerce @(m a) $+      reader @oldtag $ f . view (Generic.field' @field)+    {-# INLINE reader_ #-}++-- | Zoom in on the field at position @pos@ of type @v@+-- in the environment @struct@.+instance+  -- The constraint raises @-Wsimplifiable-class-constraints@.+  -- This could be avoided by instead placing @HasPosition'@s constraints here.+  -- Unfortunately, it uses non-exported symbols from @generic-lens@.+  ( tag ~ pos, Generic.HasPosition' pos struct v, HasReader oldtag struct m )+  => HasReader tag v (Pos pos oldtag m)+  where+    ask_ _ = coerce @(m v) $+      asks @oldtag $ view (Generic.position' @pos)+    {-# INLINE ask_ #-}+    local_ :: forall a.+      Proxy# tag+      -> (v -> v)+      -> Pos pos oldtag m a+      -> Pos pos oldtag m a+    local_ _ = coerce @((v -> v) -> m a -> m a) $+      local @oldtag . over (Generic.position' @pos)+    {-# INLINE local_ #-}+    reader_ :: forall a.+      Proxy# tag+      -> (v -> a)+      -> Pos pos oldtag m a+    reader_ _ f = coerce @(m a) $+      reader @oldtag $ f . view (Generic.position' @pos)+    {-# INLINE reader_ #-}++-- | Lift one layer in a monad transformer stack.+instance (HasReader tag r m, MonadTransControl t, Monad (t m))+  => HasReader tag r (Lift (t m))+  where+    ask_ _ = coerce $ lift @t @m $ ask @tag @r+    {-# INLINE ask_ #-}+    local_+      :: forall a. Proxy# tag -> (r -> r) -> Lift (t m) a -> Lift (t m) a+    local_ _ f = coerce @(t m a -> t m a) $+      \m -> liftWith (\run -> local @tag f $ run m) >>= restoreT . pure+    {-# INLINE local_ #-}+    reader_ :: forall a. Proxy# tag -> (r -> a) -> Lift (t m) a+    reader_ _ = coerce @((r -> a) -> t m a) $ lift . reader @tag+    {-# INLINE reader_ #-}++-- | Compose two accessors.+deriving via ((t2 :: (* -> *) -> * -> *) ((t1 :: (* -> *) -> * -> *) m))+  instance+  ( forall x. Coercible (m x) (t2 (t1 m) x)+  , Monad m, HasReader tag r (t2 (t1 m)) )+  => HasReader tag r ((t2 :.: t1) m)
+ src/Capability/State.hs view
@@ -0,0 +1,22 @@+-- | Defines a capability type class for a state effect. A state capability+-- provides a state which can be retrieved with 'get' and set with 'put'. As an+-- analogy, each state capability is equivalent to making one @IORef@ available+-- in an @IO@ computation (except, of course, that a state capability does not+-- have to be provided by @IO@).+--+-- This is a very expressive capability. It is often preferable to restrict to+-- less powerful capabilities such as "Capability.Reader", "Capability.Writer",+-- or "Capability.Stream".++module Capability.State+  ( -- * Interface+    module Capability.State.Internal.Class+    -- * Strategies+  , module Capability.State.Internal.Strategies+    -- ** Modifiers+  , module Capability.Accessors+  ) where++import Capability.Accessors+import Capability.State.Internal.Class+import Capability.State.Internal.Strategies
+ src/Capability/State/Internal/Class.hs view
@@ -0,0 +1,125 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeInType #-}++{-# OPTIONS_HADDOCK hide #-}++module Capability.State.Internal.Class+  ( HasState(..)+  , get+  , put+  , state+  , modify+  , modify'+  , gets+  , zoom+  ) where++import Data.Coerce (Coercible, coerce)+import GHC.Exts (Proxy#, proxy#)++-- | State capability+--+-- An instance should fulfill the following laws.+-- At this point these laws are not definitive,+-- see <https://github.com/haskell/mtl/issues/5>.+--+-- prop> get @t >>= \s1 -> get @t >>= \s2 -> pure (s1, s2) = get @t >>= \s -> pure (s, s)+-- prop> get @t >>= \_ -> put @t s = put @t s+-- prop> put @t s1 >> put @t s2 = put @t s2+-- prop> put @t s >> get @t = put @t s >> pure s+-- prop> state @t f = get @t >>= \s -> let (a, s') = f s in put @t s' >> pure a+class Monad m+  => HasState (tag :: k) (s :: *) (m :: * -> *) | tag m -> s+  where+    -- | For technical reasons, this method needs an extra proxy argument.+    -- You only need it if you are defining new instances of 'HasState.+    -- Otherwise, you will want to use 'get'.+    -- See 'get' for more documentation.+    get_ :: Proxy# tag -> m s+    -- | For technical reasons, this method needs an extra proxy argument.+    -- You only need it if you are defining new instances of 'HasState.+    -- Otherwise, you will want to use 'put'.+    -- See 'put' for more documentation.+    put_ :: Proxy# tag -> s -> m ()+    -- | For technical reasons, this method needs an extra proxy argument.+    -- You only need it if you are defining new instances of 'HasState.+    -- Otherwise, you will want to use 'state'.+    -- See 'state' for more documentation.+    state_ :: Proxy# tag -> (s -> (a, s)) -> m a++-- | @get \@tag@+-- retrieve the current state of the state capability @tag@.+get :: forall tag s m. HasState tag s m => m s+get = get_ (proxy# @_ @tag)+{-# INLINE get #-}++-- | @put \@tag s@+-- replace the current state of the state capability @tag@ with @s@.+put :: forall tag s m. HasState tag s m => s -> m ()+put = put_ (proxy# @_ @tag)+{-# INLINE put #-}++-- | @state \@tag f@+-- lifts a pure state computation @f@ to a monadic action in an arbitrary+-- monad @m@ with capability @HasState@.+--+-- Given the current state @s@ of the state capability @tag@+-- and @(a, s') = f s@, update the state to @s'@ and return @a@.+state :: forall tag s m a. HasState tag s m => (s -> (a, s)) -> m a+state = state_ (proxy# @_ @tag)+{-# INLINE state #-}++-- | @modify \@tag f@+-- given the current state @s@ of the state capability @tag@+-- and @s' = f s@, updates the state of the capability @tag@ to @s'@.+modify :: forall tag s m. HasState tag s m => (s -> s) -> m ()+modify f = state @tag $ \s -> ((), f s)+{-# INLINE modify #-}++-- | Same as 'modify' but strict in the new state.+modify' :: forall tag s m. HasState tag s m => (s -> s) -> m ()+modify' f = do+  s' <- get @tag+  put @tag $! f s'+{-# INLINE modify' #-}++-- | @gets \@tag f@+-- retrieves the image, by @f@ of the current state+-- of the state capability @tag@.+--+-- prop> gets @tag f = f <$> get @tag+gets :: forall tag s m a. HasState tag s m => (s -> a) -> m a+gets f = do+  s <- get @tag+  pure (f s)+{-# INLINE gets #-}++-- | Execute the given state action on a sub-component of the current state+-- as defined by the given transformer @t@.+--+-- Example:+--+-- > zoom @"foobar" @"foo" @(Field "foo" "foobar") foo+-- >   :: HasState "foobar" FooBar m => m ()+-- >+-- > foo :: HasState "foo" Int m => m ()+-- > data FooBar = FooBar { foo :: Int, bar :: String }+--+-- This function is experimental and subject to change.+-- See <https://github.com/tweag/capability/issues/46>.+zoom :: forall outertag innertag t outer inner m a.+  ( forall x. Coercible (t m x) (m x)+  , forall m'. HasState outertag outer m'+    => HasState innertag inner (t m')+  , HasState outertag outer m )+  => (forall m'. HasState innertag inner m' => m' a) -> m a+zoom m = coerce @(t m a) m+{-# INLINE zoom #-}
+ src/Capability/State/Internal/Strategies.hs view
@@ -0,0 +1,216 @@+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeInType #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE UndecidableInstances #-}++{-# OPTIONS_GHC -Wno-orphans #-}+{-# OPTIONS_GHC -Wno-simplifiable-class-constraints #-}++{-# OPTIONS_HADDOCK hide #-}++module Capability.State.Internal.Strategies+  ( MonadState(..)+  , ReaderIORef(..)+  , ReaderRef(..)+  ) where++import Capability.Accessors+import Capability.Reader.Internal.Class+import Capability.State.Internal.Class+import Control.Lens (set, view)+import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.Primitive (PrimMonad)+import qualified Control.Monad.State.Class as State+import Control.Monad.Trans.Class (MonadTrans, lift)+import Data.Coerce (Coercible, coerce)+import qualified Data.Generics.Product.Fields as Generic+import qualified Data.Generics.Product.Positions as Generic+import Data.IORef+import Data.Mutable+import GHC.Exts (Proxy#)++-- | Derive 'HasState' from @m@'s+-- 'Control.Monad.State.Class.MonadState' instance.+newtype MonadState (m :: * -> *) (a :: *) = MonadState (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)++instance State.MonadState s m => HasState tag s (MonadState m) where+  get_ _ = coerce @(m s) State.get+  {-# INLINE get_ #-}+  put_ _ = coerce @(s -> m ()) State.put+  {-# INLINE put_ #-}+  state_ :: forall a. Proxy# tag -> (s -> (a, s)) -> MonadState m a+  state_ _ = coerce @((s -> (a, s)) -> m a) State.state+  {-# INLINE state_ #-}++-- | Convert the state using safe coercion.+instance+  ( Coercible from to, HasState tag from m+  , forall x y. Coercible x y => Coercible (m x) (m y) )+  => HasState tag to (Coerce to m)+  where+    get_ tag = coerce @(m from) $ get_ tag+    {-# INLINE get_ #-}+    put_ tag = coerce @(from -> m ()) $ put_ tag+    {-# INLINE put_ #-}+    state_ :: forall a. Proxy# tag -> (to -> (a, to)) -> Coerce to m a+    state_ tag = coerce @((from -> (a, from)) -> m a) $ state_ tag+    {-# INLINE state_ #-}++-- | Rename the tag.+instance HasState oldtag s m => HasState newtag s (Rename oldtag m) where+  get_ _ = coerce @(m s) $ get @oldtag+  {-# INLINE get_ #-}+  put_ _ = coerce @(s -> m ()) $ put @oldtag+  {-# INLINE put_ #-}+  state_ :: forall a. Proxy# newtag -> (s -> (a, s)) -> Rename oldtag m a+  state_ _ = coerce @((s -> (a, s)) -> m a) $ state @oldtag+  {-# INLINE state_ #-}++-- | Zoom in on the record field @field@ of type @v@ in the state @record@.+instance+  -- The constraint raises @-Wsimplifiable-class-constraints@.+  -- This could be avoided by instead placing @HasField'@s constraints here.+  -- Unfortunately, it uses non-exported symbols from @generic-lens@.+  ( tag ~ field, Generic.HasField' field record v, HasState oldtag record m )+  => HasState tag v (Field field oldtag m)+  where+    get_ _ = coerce @(m v) $+      gets @oldtag $ view (Generic.field' @field)+    {-# INLINE get_ #-}+    put_ _ = coerce @(v -> m ()) $+      modify @oldtag . set (Generic.field' @field @record)+    {-# INLINE put_ #-}+    state_ :: forall a.+      Proxy# tag+      -> (v -> (a, v))+      -> Field field oldtag m a+    state_ _ = coerce @((v -> (a, v)) -> m a) $+      state @oldtag . Generic.field' @field @_ @_ @((,) a)+    {-# INLINE state_ #-}++-- | Zoom in on the field at position @pos@ of type @v@ in the state @struct@.+instance+  -- The constraint raises @-Wsimplifiable-class-constraints@.+  -- This could be avoided by instead placing @HasPosition'@s constraints here.+  -- Unfortunately, it uses non-exported symbols from @generic-lens@.+  ( tag ~ pos, Generic.HasPosition' pos struct v, HasState oldtag struct m )+  => HasState tag v (Pos pos oldtag m)+  where+    get_ _ = coerce @(m v) $+      gets @oldtag $ view (Generic.position' @pos)+    {-# INLINE get_ #-}+    put_ _ = coerce @(v -> m ()) $+      modify @oldtag . set (Generic.position' @pos @struct)+    {-# INLINE put_ #-}+    state_ :: forall a.+      Proxy# tag+      -> (v -> (a, v))+      -> Pos pos oldtag m a+    state_ _ = coerce @((v -> (a, v)) -> m a) $+      state @oldtag . Generic.position' @pos @_ @_ @((,) a)+    {-# INLINE state_ #-}++-- | Lift one layer in a monad transformer stack.+instance (HasState tag s m, MonadTrans t, Monad (t m))+  => HasState tag s (Lift (t m))+  where+    get_ _ = coerce $ lift @t @m $ get @tag @s+    {-# INLINE get_ #-}+    put_ _ = coerce $ lift @t @m . put @tag @s+    {-# INLINE put_ #-}+    state_ :: forall a. Proxy# tag -> (s -> (a, s)) -> Lift (t m) a+    state_ _ = coerce $ lift @t @m . state @tag @s @m @a+    {-# INLINE state_ #-}++-- | Compose two accessors.+deriving via ((t2 :: (* -> *) -> * -> *) ((t1 :: (* -> *) -> * -> *) m))+  instance+  ( forall x. Coercible (m x) (t2 (t1 m) x)+  , Monad m, HasState tag s (t2 (t1 m)) )+  => HasState tag s ((t2 :.: t1) m)++-- | Derive a state monad from a reader over an 'Data.IORef.IORef'.+--+-- Example:+--+-- > newtype MyState m a = MyState (ReaderT (IORef Int) m a)+-- >   deriving (Functor, Applicative, Monad)+-- >   deriving HasState "foo" Int via+-- >     ReaderIORef (MonadReader (ReaderT (IORef Int) m))+--+-- See 'ReaderRef' for a more generic strategy.+newtype ReaderIORef m a = ReaderIORef (m a)+  deriving (Functor, Applicative, Monad)++instance+  (HasReader tag (IORef s) m, MonadIO m)+  => HasState tag s (ReaderIORef m)+  where+    get_ _ = ReaderIORef $ do+      ref <- ask @tag+      liftIO $ readIORef ref+    {-# INLINE get_ #-}+    put_ _ v = ReaderIORef $ do+      ref <- ask @tag+      liftIO $ writeIORef ref v+    {-# INLINE put_ #-}+    state_ _ f = ReaderIORef $ do+      ref <- ask @tag+      liftIO $ atomicModifyIORef' ref (swap . f)+      where+        swap (a, b) = (b, a)+    {-# INLINE state_ #-}++-- | Derive a state monad from a reader over a mutable reference.+--+-- Mutable references are available in a 'Control.Monad.Primitive.PrimMonad'.+-- The corresponding 'Control.Monad.Primitive.PrimState' has to match the+-- 'Data.Mutable.MCState' of the reference. This constraint makes a stand-alone+-- deriving clause necessary.+--+-- Example:+--+-- > newtype MyState m a = MyState (ReaderT (IORef Int) m a)+-- >   deriving (Functor, Applicative, Monad)+-- > deriving via ReaderRef (MonadReader (ReaderT (IORef Int) m))+-- >   instance (PrimMonad m, PrimState m ~ PrimState IO)+-- >   => HasState "foo" Int (MyState m)+--+-- See 'ReaderIORef' for a specialized version over 'Data.IORef.IORef'.+newtype ReaderRef m (a :: *) = ReaderRef (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)++instance+  ( MutableRef ref, RefElement ref ~ s+  , HasReader tag ref m, PrimMonad m, PrimState m ~ MCState ref )+  => HasState tag s (ReaderRef m)+  where+    get_ _ = ReaderRef $ do+      ref <- ask @tag+      readRef ref+    {-# INLINE get_ #-}+    put_ _ v = ReaderRef $ do+      ref <- ask @tag+      writeRef ref v+    {-# INLINE put_ #-}+    state_ _ f = ReaderRef $ do+      ref <- ask @tag+      s <- readRef ref+      let (a, s') = f s+      writeRef ref s'+      pure a+    {-# INLINE state_ #-}
+ src/Capability/Stream.hs view
@@ -0,0 +1,109 @@+-- | Defines a capability for computations that produce a stream of values+-- as part of their execution.+--+-- Programs producing streams of data are common. Examples: emitting events on+-- input, or emitting events whenever certain conditions are observed. Streams+-- are similar to Python generators.+--+-- The 'HasStream' capability enables separating the logic responsible for+-- emitting events from that responsible for collecting or handling them.+--+-- This can be thought of as a writer capability of a list of values @HasWriter+-- tag [v]@ with @\\x -> tell \@tag [x]@ as a primitive operation. However, that+-- implementation would be inefficient.+--+-- For example using the 'Streaming.Prelude.Stream' instance, a producer defined+-- using this capability can be consumed efficiently in a streaming fashion.++{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeInType #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE UndecidableInstances #-}++module Capability.Stream+  ( -- * Interface+    HasStream(..)+  , yield+    -- * Strategies+  , StreamStack(..)+  , StreamDList(..)+    -- ** Modifiers+  , module Capability.Accessors+  ) where++import Capability.Accessors+import Capability.State+import Capability.Writer+import Control.Monad.IO.Class (MonadIO)+import Control.Monad.Primitive (PrimMonad)+import Control.Monad.Trans.Class (MonadTrans, lift)+import Data.Coerce (Coercible, coerce)+import Data.DList (DList)+import qualified Data.DList as DList+import GHC.Exts (Proxy#, proxy#)+import Streaming+import qualified Streaming.Prelude as S++-- | Streaming capability.+--+-- An instance does not need to fulfill any additional laws+-- besides the monad laws.+class Monad m+  => HasStream (tag :: k) (a :: *) (m :: * -> *) | tag m -> a+  where+    -- | For technical reasons, this method needs an extra proxy argument.+    -- You only need it if you are defining new instances of 'HasReader'.+    -- Otherwise, you will want to use 'yield'.+    -- See 'yield' for more documentation.+    yield_ :: Proxy# tag -> a -> m ()++-- | @yield \@tag a@+-- emits @a@ in the stream capability @tag@.+yield :: forall tag a m. HasStream tag a m => a -> m ()+yield = yield_ (proxy# @_ @tag)+{-# INLINE yield #-}++-- | Accumulate streamed values in a reverse order list.+newtype StreamStack m (a :: *) = StreamStack (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)+instance HasState tag [a] m => HasStream tag a (StreamStack m) where+  yield_ _ a = coerce @(m ()) $ modify' @tag (a:)+  {-# INLINE yield_ #-}++-- | Accumulate streamed values in forward order in a difference list.+newtype StreamDList m (a :: *) = StreamDList (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)+instance HasWriter tag (DList a) m => HasStream tag a (StreamDList m) where+  yield_ _ = coerce @(a -> m ()) $ tell @tag . DList.singleton+  {-# INLINE yield_ #-}++instance Monad m => HasStream tag a (S.Stream (Of a) m) where+  yield_ _ = S.yield+  {-# INLINE yield_ #-}++-- | Lift one layer in a monad transformer stack.+instance (HasStream tag a m, MonadTrans t, Monad (t m))+  => HasStream tag a (Lift (t m))+  where+    yield_ _ = coerce @(a -> t m ()) $ lift . yield @tag+    {-# INLINE yield_ #-}++-- | Compose two accessors.+deriving via ((t2 :: (* -> *) -> * -> *) ((t1 :: (* -> *) -> * -> *) m))+  instance+  ( forall x. Coercible (m x) (t2 (t1 m) x)+  , Monad m, HasStream tag a (t2 (t1 m)) )+  => HasStream tag a ((t2 :.: t1) m)
+ src/Capability/Writer.hs view
@@ -0,0 +1,162 @@+-- | Defines a capability type class for writer effects. A writer program can+-- output values with 'tell'. The values output by two consecutive+-- sub-computation are combined using a monoid's @mappend@.+--+-- The interface of 'HasWriter' follows that of+-- 'Control.Monad.Writer.Class.MonadWriter'. However, this module does not+-- include a strategy to provide a @HasWriter@ capability from a @MonadWriter@+-- instance. It is generally a bad idea to use monads such as+-- 'Control.Monad.Writer.Strict.WriterT', as they tend to leak space, as+-- described in this+-- <https://blog.infinitenegativeutility.com/2016/7/writer-monads-and-space-leaks+-- blog post> by Getty Ritter.+--+-- Instead, you should use the 'WriterLog' strategy that implements the writer+-- monad on a state monad. There is no downside, as using 'HasWriter' instead of+-- 'HasState' directly ensures your code adheres to the writer monad interface+-- and does not misuse the underlying state monad.++{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeInType #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE UndecidableInstances #-}++{-# OPTIONS_GHC -Wno-simplifiable-class-constraints #-}++module Capability.Writer+  ( -- * Interface+    HasWriter(..)+  , writer+  , tell+  , listen+  , pass+    -- * Strategies+  , WriterLog(..)+    -- ** Modifiers+  , module Capability.Accessors+  ) where++import Capability.Accessors+import Capability.State+import Control.Monad.IO.Class (MonadIO)+import Control.Monad.Primitive (PrimMonad)+import Data.Coerce (Coercible, coerce)+import GHC.Exts (Proxy#, proxy#)++-- | Writer capability+--+-- An instance should fulfill the following laws.+-- At this point these laws are not definitive,+-- see <https://github.com/haskell/mtl/issues/5>.+--+-- prop> listen @t (pure a) = pure (a, mempty)+-- prop> listen @t (tell @t w) = tell @t w >> pure (w, w)+-- prop> listen @t (m >>= k) = listen @t m >>= \(a, w1) -> listen @t (k a) >>= \(b, w2) -> pure (b, w1 `mappend` w2)+-- prop> pass @t (tell @t w >> pure (a, f)) = tell @t (f w) >> pure a+-- prop> writer @t (a, w) = tell @t w >> pure a+class (Monoid w, Monad m)+  => HasWriter (tag :: k) (w :: *) (m :: * -> *) | tag m -> w+  where+    -- | For technical reasons, this method needs an extra proxy argument.+    -- You only need it if you are defining new instances of 'HasReader'.+    -- Otherwise, you will want to use 'writer'.+    -- See 'writer' for more documentation.+    writer_ :: Proxy# tag -> (a, w) -> m a+    -- | For technical reasons, this method needs an extra proxy argument.+    -- You only need it if you are defining new instances of 'HasReader'.+    -- Otherwise, you will want to use 'tell'.+    -- See 'tell' for more documentation.+    tell_ :: Proxy# tag -> w -> m ()+    -- | For technical reasons, this method needs an extra proxy argument.+    -- You only need it if you are defining new instances of 'HasReader'.+    -- Otherwise, you will want to use 'listen'.+    -- See 'listen' for more documentation.+    listen_ :: Proxy# tag -> m a -> m (a, w)+    -- | For technical reasons, this method needs an extra proxy argument.+    -- You only need it if you are defining new instances of 'HasReader'.+    -- Otherwise, you will want to use 'pass'.+    -- See 'pass' for more documentation.+    pass_ :: Proxy# tag -> m (a, w -> w) -> m a++-- | @writer \@tag (a, w)@+-- lifts a pure writer action @(a, w)@ to a monadic action in an arbitrary+-- monad @m@ with capability @HasWriter@.+--+-- Appends @w@ to the output of the writer capability @tag@+-- and returns the value @a@.+writer :: forall tag w m a. HasWriter tag w m => (a, w) -> m a+writer = writer_ (proxy# @_ @tag)+{-# INLINE writer #-}++-- | @tell \@tag w@+-- appends @w@ to the output of the writer capability @tag@.+tell :: forall tag w m. HasWriter tag w m => w -> m ()+tell = tell_ (proxy# @_ @tag)+{-# INLINE tell #-}++-- | @listen \@tag m@+-- executes the action @m@ and returns the output of @m@+-- in the writer capability @tag@ along with result of @m@.+-- Appends the output of @m@ to the output of the writer capability @tag@.+listen :: forall tag w m a. HasWriter tag w m => m a -> m (a, w)+listen = listen_ (proxy# @_ @tag)+{-# INLINE listen #-}++-- | @pass \@tag m@+-- executes the action @m@. Assuming @m@ returns @(a, f)@ and appends+-- @w@ to the output of the writer capability @tag@.+-- @pass \@tag m@ instead appends @w' = f w@ to the output and returns @a@.+pass :: forall tag w m a. HasWriter tag w m => m (a, w -> w) -> m a+pass = pass_ (proxy# @_ @tag)+{-# INLINE pass #-}++-- | Compose two accessors.+deriving via ((t2 :: (* -> *) -> * -> *) ((t1 :: (* -> *) -> * -> *) m))+  instance+  ( forall x. Coercible (m x) (t2 (t1 m) x)+  , Monad m, HasWriter tag w (t2 (t1 m)) )+  => HasWriter tag w ((t2 :.: t1) m)++newtype WriterLog m a = WriterLog (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)++instance (Monoid w, HasState tag w m)+  => HasWriter tag w (WriterLog m)+  where+    writer_ tag (a, w) = tell_ tag w >> pure a+    {-# INLINE writer_ #-}+    tell_ _ w = coerce @(m ()) $ modify' @tag (<> w)+    {-# INLINE tell_ #-}+    listen_ :: forall a. Proxy# tag -> WriterLog m a -> WriterLog m (a, w)+    listen_ _ m = coerce @(m (a, w)) $ do+      w0 <- get @tag+      put @tag mempty+      a <- coerce m+      w <- get @tag+      put @tag $! w0 <> w+      pure (a, w)+    {-# INLINE listen_ #-}+    pass_ :: forall a. Proxy# tag -> WriterLog m (a, w -> w) -> WriterLog m a+    pass_ _ m = coerce @(m a) $ do+      w0 <- get @tag+      put @tag mempty+      (a, f) <- coerce @_ @(m (a, w -> w)) m+      w <- get @tag+      put @tag $! w0 <> f w+      pure a+    {-# INLINE pass_ #-}
+ src/Capability/Writer/Discouraged.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE UndecidableInstances #-}++{-# OPTIONS_GHC -Wno-orphans #-}+{-# OPTIONS_GHC -Wno-simplifiable-class-constraints #-}++-- | Defines discouraged instances of writer monad capabilities.++module Capability.Writer.Discouraged+  ( ) where++import Capability.Accessors+import Capability.Writer+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Unlift (MonadTransUnlift, Unlift(..), askUnlift)+import Data.Coerce (coerce)+import GHC.Exts (Proxy#)++-- | Lift one layer in a monad transformer stack.+--+-- Note, that if the 'HasWriter' instance is based on 'HasState', then it is+-- more efficient to apply 'Lift' to the underlying state capability. E.g.+-- you should favour+--+-- > deriving (HasWriter tag w) via+-- >   WriterLog (Lift (SomeTrans (MonadState SomeStateMonad)))+--+-- over+--+-- > deriving (HasWriter tag w) via+-- >   Lift (SomeTrans (WriterLog (MonadState SomeStateMonad)))+instance+  -- MonadTransUnlift constraint requires -Wno-simplifiable-class-constraints+  (HasWriter tag w m, MonadTransUnlift t, Monad (t m))+  => HasWriter tag w (Lift (t m))+  where+    writer_ :: forall a. Proxy# tag -> (a, w) -> Lift (t m) a+    writer_ _ = coerce @((a, w) -> t m a) $ lift . writer @tag+    {-# INLINE writer_ #-}+    tell_ _ = coerce @(w -> t m ()) $ lift . tell @tag+    {-# INLINE tell_ #-}+    listen_ :: forall a. Proxy# tag -> Lift (t m) a -> Lift (t m) (a, w)+    listen_ _ = coerce @(t m a -> t m (a, w)) $ \m -> do+      u <- askUnlift+      lift $ listen @tag $ unlift u m+    {-# INLINE listen_ #-}+    pass_ :: forall a. Proxy# tag -> Lift (t m) (a, w -> w) -> Lift (t m) a+    pass_ _ = coerce @(t m (a, w -> w) -> t m a) $ \m -> do+      u <- askUnlift+      lift $ pass @tag $ unlift u m+    {-# INLINE pass_ #-}