packages feed

polysemy 1.1.0.0 → 1.2.0.0

raw patch · 26 files changed

+1378/−158 lines, 26 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Polysemy.Fixpoint: data Fixpoint m a
- Polysemy.Internal.Fixpoint: data Fixpoint m a
+ Polysemy: data Final m z a
+ Polysemy: embedFinal :: (Member (Final m) r, Functor m) => m a -> Sem r a
+ Polysemy: embedToFinal :: (Member (Final m) r, Functor m) => Sem (Embed m : r) a -> Sem r a
+ Polysemy: raiseUnder :: forall e2 e1 r a. Sem (e1 : r) a -> Sem (e1 : (e2 : r)) a
+ Polysemy: raiseUnder2 :: forall e2 e3 e1 r a. Sem (e1 : r) a -> Sem (e1 : (e2 : (e3 : r))) a
+ Polysemy: raiseUnder3 :: forall e2 e3 e4 e1 r a. Sem (e1 : r) a -> Sem (e1 : (e2 : (e3 : (e4 : r)))) a
+ Polysemy: runFinal :: Monad m => Sem '[Final m] a -> m a
+ Polysemy: subsume :: Member e r => Sem (e : r) a -> Sem r a
+ Polysemy.Async: asyncToIOFinal :: Member (Final IO) r => Sem (Async : r) a -> Sem r a
+ Polysemy.AtomicState: atomicStateToIO :: forall s r a. Member (Embed IO) r => s -> Sem (AtomicState s : r) a -> Sem r (s, a)
+ Polysemy.Error: errorToIOFinal :: (Typeable e, Member (Final IO) r) => Sem (Error e : r) a -> Sem r (Either e a)
+ Polysemy.Final: [WithWeavingToFinal] :: ThroughWeavingToFinal m z a -> Final m z a
+ Polysemy.Final: bindS :: (a -> n b) -> Sem (WithStrategy m f n) (f a -> m (f b))
+ Polysemy.Final: embedFinal :: (Member (Final m) r, Functor m) => m a -> Sem r a
+ Polysemy.Final: embedToFinal :: (Member (Final m) r, Functor m) => Sem (Embed m : r) a -> Sem r a
+ Polysemy.Final: finalToFinal :: forall m1 m2 r a. Member (Final m2) r => (forall x. m1 x -> m2 x) -> (forall x. m2 x -> m1 x) -> Sem (Final m1 : r) a -> Sem r a
+ Polysemy.Final: getInitialStateS :: forall m f n. Sem (WithStrategy m f n) (f ())
+ Polysemy.Final: getInspectorS :: forall m f n. Sem (WithStrategy m f n) (Inspector f)
+ Polysemy.Final: interpretFinal :: forall m e r a. Member (Final m) r => (forall x n. e n x -> Strategic m n x) -> Sem (e : r) a -> Sem r a
+ Polysemy.Final: liftS :: Functor m => m a -> Strategic m n a
+ Polysemy.Final: newtype Final m z a
+ Polysemy.Final: pureS :: Applicative m => a -> Strategic m n a
+ Polysemy.Final: runFinal :: Monad m => Sem '[Final m] a -> m a
+ Polysemy.Final: runS :: n a -> Sem (WithStrategy m f n) (m (f a))
+ Polysemy.Final: type Strategic m n a = forall f. Functor f => Sem (WithStrategy m f n) (m (f a))
+ Polysemy.Final: type WithStrategy m f n = '[Strategy m f n]
+ Polysemy.Final: type ThroughWeavingToFinal m z a = forall f. Functor f => f () -> (forall x. f (z x) -> m (f x)) -> (forall x. f x -> Maybe x) -> m (f a)
+ Polysemy.Final: withStrategicToFinal :: Member (Final m) r => Strategic m (Sem r) a -> Sem r a
+ Polysemy.Final: withWeavingToFinal :: forall m r a. Member (Final m) r => ThroughWeavingToFinal m (Sem r) a -> Sem r a
+ Polysemy.Fixpoint: fixpointToFinal :: forall m r a. (Member (Final m) r, MonadFix m) => Sem (Fixpoint : r) a -> Sem r a
+ Polysemy.Fixpoint: newtype Fixpoint m a
+ Polysemy.Internal: subsume :: Member e r => Sem (e : r) a -> Sem r a
+ Polysemy.Internal.Fixpoint: newtype Fixpoint m a
+ Polysemy.Internal.Strategy: [GetInitialState] :: Strategy m f n z (f ())
+ Polysemy.Internal.Strategy: [GetInspector] :: Strategy m f n z (Inspector f)
+ Polysemy.Internal.Strategy: [HoistInterpretation] :: (a -> n b) -> Strategy m f n z (f a -> m (f b))
+ Polysemy.Internal.Strategy: bindS :: (a -> n b) -> Sem (WithStrategy m f n) (f a -> m (f b))
+ Polysemy.Internal.Strategy: data Strategy m f n z a
+ Polysemy.Internal.Strategy: getInitialStateS :: forall m f n. Sem (WithStrategy m f n) (f ())
+ Polysemy.Internal.Strategy: getInspectorS :: forall m f n. Sem (WithStrategy m f n) (Inspector f)
+ Polysemy.Internal.Strategy: liftS :: Functor m => m a -> Strategic m n a
+ Polysemy.Internal.Strategy: pureS :: Applicative m => a -> Strategic m n a
+ Polysemy.Internal.Strategy: runS :: n a -> Sem (WithStrategy m f n) (m (f a))
+ Polysemy.Internal.Strategy: runStrategy :: Functor f => Sem '[Strategy m f n] a -> f () -> (forall x. f (n x) -> m (f x)) -> (forall x. f x -> Maybe x) -> a
+ Polysemy.Internal.Strategy: type Strategic m n a = forall f. Functor f => Sem (WithStrategy m f n) (m (f a))
+ Polysemy.Internal.Strategy: type WithStrategy m f n = '[Strategy m f n]
+ Polysemy.Internal.Union: injWeaving :: forall e r m a. Member e r => Weaving e m a -> Union r m a
+ Polysemy.Internal.Writer: [Listen] :: forall o m a. m a -> Writer o m (o, a)
+ Polysemy.Internal.Writer: [Pass] :: m (o -> o, a) -> Writer o m a
+ Polysemy.Internal.Writer: [Tell] :: o -> Writer o m ()
+ Polysemy.Internal.Writer: data Writer o m a
+ Polysemy.Internal.Writer: listen :: forall o_awlQ r_awoe a_XwlT. MemberWithError (Writer o_awlQ) r_awoe => Sem r_awoe a_XwlT -> Sem r_awoe (o_awlQ, a_XwlT)
+ Polysemy.Internal.Writer: pass :: forall o_awlU r_awog a_awlV. MemberWithError (Writer o_awlU) r_awog => Sem r_awog (o_awlU -> o_awlU, a_awlV) -> Sem r_awog a_awlV
+ Polysemy.Internal.Writer: runWriterSTMAction :: forall o r a. (Member (Final IO) r, Monoid o) => (o -> STM ()) -> Sem (Writer o : r) a -> Sem r a
+ Polysemy.Internal.Writer: tell :: forall o_awlO r_awoc. MemberWithError (Writer o_awlO) r_awoc => o_awlO -> Sem r_awoc ()
+ Polysemy.Internal.Writer: writerToEndoWriter :: (Monoid o, Member (Writer (Endo o)) r) => Sem (Writer o : r) a -> Sem r a
+ Polysemy.NonDet: instance GHC.Base.Functor (Polysemy.NonDet.NonDetState r)
+ Polysemy.Output: outputToIOMonoid :: forall o m r a. (Monoid m, Member (Embed IO) r) => (o -> m) -> Sem (Output o : r) a -> Sem r (m, a)
+ Polysemy.Output: outputToIOMonoidAssocR :: forall o m r a. (Monoid m, Member (Embed IO) r) => (o -> m) -> Sem (Output o : r) a -> Sem r (m, a)
+ Polysemy.Resource: resourceToIOFinal :: Member (Final IO) r => Sem (Resource : r) a -> Sem r a
+ Polysemy.State: stateToIO :: forall s r a. Member (Embed IO) r => s -> Sem (State s : r) a -> Sem r (s, a)
+ Polysemy.Writer: runWriterTVar :: (Monoid o, Member (Final IO) r) => TVar o -> Sem (Writer o : r) a -> Sem r a
+ Polysemy.Writer: writerToEndoWriter :: (Monoid o, Member (Writer (Endo o)) r) => Sem (Writer o : r) a -> Sem r a
+ Polysemy.Writer: writerToIOAssocRFinal :: (Monoid o, Member (Final IO) r) => Sem (Writer o : r) a -> Sem r (o, a)
+ Polysemy.Writer: writerToIOFinal :: (Monoid o, Member (Final IO) r) => Sem (Writer o : r) a -> Sem r (o, a)
- Polysemy.Async: async :: forall r_arEI a_Xrse. MemberWithError Async r_arEI => Sem r_arEI a_Xrse -> Sem r_arEI (Async (Maybe a_Xrse))
+ Polysemy.Async: async :: forall r_atpS a_Xtdq. MemberWithError Async r_atpS => Sem r_atpS a_Xtdq -> Sem r_atpS (Async (Maybe a_Xtdq))
- Polysemy.Async: await :: forall r_arEK a_arse. MemberWithError Async r_arEK => Async a_arse -> Sem r_arEK a_arse
+ Polysemy.Async: await :: forall r_atpU a_atdq. MemberWithError Async r_atpU => Async a_atdq -> Sem r_atpU a_atdq
- Polysemy.AtomicState: atomicState' :: Member (AtomicState s) r => (s -> (s, a)) -> Sem r a
+ Polysemy.AtomicState: atomicState' :: forall s a r. Member (AtomicState s) r => (s -> (s, a)) -> Sem r a
- Polysemy.AtomicState: runAtomicStateIORef :: Member (Embed IO) r => IORef s -> Sem (AtomicState s : r) a -> Sem r a
+ Polysemy.AtomicState: runAtomicStateIORef :: forall s r a. Member (Embed IO) r => IORef s -> Sem (AtomicState s : r) a -> Sem r a
- Polysemy.Error: catch :: forall e_asuz r_aswu a_asuB. MemberWithError (Error e_asuz) r_aswu => Sem r_aswu a_asuB -> (e_asuz -> Sem r_aswu a_asuB) -> Sem r_aswu a_asuB
+ Polysemy.Error: catch :: forall e_aukD r_aumy a_aukF. MemberWithError (Error e_aukD) r_aumy => Sem r_aumy a_aukF -> (e_aukD -> Sem r_aumy a_aukF) -> Sem r_aumy a_aukF
- Polysemy.Error: throw :: forall e_asuw r_asws a_asuy. MemberWithError (Error e_asuw) r_asws => e_asuw -> Sem r_asws a_asuy
+ Polysemy.Error: throw :: forall e_aukA r_aumw a_aukC. MemberWithError (Error e_aukA) r_aumw => e_aukA -> Sem r_aumw a_aukC
- Polysemy.Fail: failToEmbed :: forall m a r. (Member (Embed m) r, MonadFail m) => Sem (Fail : r) a -> Sem r a
+ Polysemy.Fail: failToEmbed :: forall m r a. (Member (Embed m) r, MonadFail m) => Sem (Fail : r) a -> Sem r a
- Polysemy.Input: input :: forall i_azyN r_azzG. MemberWithError (Input i_azyN) r_azzG => Sem r_azzG i_azyN
+ Polysemy.Input: input :: forall i_aEt6 r_aEtZ. MemberWithError (Input i_aEt6) r_aEtZ => Sem r_aEtZ i_aEt6
- Polysemy.Internal.Union: inj :: forall r e a m. (Functor m, Member e r) => e m a -> Union r m a
+ Polysemy.Internal.Union: inj :: forall e r m a. (Functor m, Member e r) => e m a -> Union r m a
- Polysemy.Internal.Union: prj :: forall e r a m. Member e r => Union r m a -> Maybe (Weaving e m a)
+ Polysemy.Internal.Union: prj :: forall e r m a. Member e r => Union r m a -> Maybe (Weaving e m a)
- Polysemy.Output: output :: forall o_ayml r_aynb. MemberWithError (Output o_ayml) r_aynb => o_ayml -> Sem r_aynb ()
+ Polysemy.Output: output :: forall o_aCXr r_aCYh. MemberWithError (Output o_aCXr) r_aCYh => o_aCXr -> Sem r_aCYh ()
- Polysemy.Reader: ask :: forall i_azVx r_azX4. MemberWithError (Reader i_azVx) r_azX4 => Sem r_azX4 i_azVx
+ Polysemy.Reader: ask :: forall i_aEPQ r_aERn. MemberWithError (Reader i_aEPQ) r_aERn => Sem r_aERn i_aEPQ
- Polysemy.Reader: local :: forall i_azVz r_azX5 a_azVB. MemberWithError (Reader i_azVz) r_azX5 => (i_azVz -> i_azVz) -> Sem r_azX5 a_azVB -> Sem r_azX5 a_azVB
+ Polysemy.Reader: local :: forall i_aEPS r_aERo a_aEPU. MemberWithError (Reader i_aEPS) r_aERo => (i_aEPS -> i_aEPS) -> Sem r_aERo a_aEPU -> Sem r_aERo a_aEPU
- Polysemy.Resource: bracket :: forall r_avGB a_XvEo c_XvEq b_avEp. MemberWithError Resource r_avGB => Sem r_avGB a_XvEo -> (a_XvEo -> Sem r_avGB c_XvEq) -> (a_XvEo -> Sem r_avGB b_avEp) -> Sem r_avGB b_avEp
+ Polysemy.Resource: bracket :: forall r_azQ0 a_XzNN c_XzNP b_azNO. MemberWithError Resource r_azQ0 => Sem r_azQ0 a_XzNN -> (a_XzNN -> Sem r_azQ0 c_XzNP) -> (a_XzNN -> Sem r_azQ0 b_azNO) -> Sem r_azQ0 b_azNO
- Polysemy.Resource: bracketOnError :: forall r_avGF a_XvEs c_XvEu b_avEt. MemberWithError Resource r_avGF => Sem r_avGF a_XvEs -> (a_XvEs -> Sem r_avGF c_XvEu) -> (a_XvEs -> Sem r_avGF b_avEt) -> Sem r_avGF b_avEt
+ Polysemy.Resource: bracketOnError :: forall r_azQ4 a_XzNR c_XzNT b_azNS. MemberWithError Resource r_azQ4 => Sem r_azQ4 a_XzNR -> (a_XzNR -> Sem r_azQ4 c_XzNT) -> (a_XzNR -> Sem r_azQ4 b_azNS) -> Sem r_azQ4 b_azNS
- Polysemy.State: get :: forall s_ax5b r_ax6E. MemberWithError (State s_ax5b) r_ax6E => Sem r_ax6E s_ax5b
+ Polysemy.State: get :: forall s_aBu2 r_aBvv. MemberWithError (State s_aBu2) r_aBvv => Sem r_aBvv s_aBu2
- Polysemy.State: put :: forall s_ax5d r_ax6F. MemberWithError (State s_ax5d) r_ax6F => s_ax5d -> Sem r_ax6F ()
+ Polysemy.State: put :: forall s_aBu4 r_aBvw. MemberWithError (State s_aBu4) r_aBvw => s_aBu4 -> Sem r_aBvw ()
- Polysemy.Trace: trace :: forall r_aBdJ. MemberWithError Trace r_aBdJ => String -> Sem r_aBdJ ()
+ Polysemy.Trace: trace :: forall r_aGkx. MemberWithError Trace r_aGkx => String -> Sem r_aGkx ()
- Polysemy.Writer: listen :: forall o_aBHB r_aBJZ a_XBHE. MemberWithError (Writer o_aBHB) r_aBJZ => Sem r_aBJZ a_XBHE -> Sem r_aBJZ (o_aBHB, a_XBHE)
+ Polysemy.Writer: listen :: forall o_awlQ r_awoe a_XwlT. MemberWithError (Writer o_awlQ) r_awoe => Sem r_awoe a_XwlT -> Sem r_awoe (o_awlQ, a_XwlT)
- Polysemy.Writer: pass :: forall o_aBHF r_aBK1 a_aBHG. MemberWithError (Writer o_aBHF) r_aBK1 => Sem r_aBK1 (o_aBHF -> o_aBHF, a_aBHG) -> Sem r_aBK1 a_aBHG
+ Polysemy.Writer: pass :: forall o_awlU r_awog a_awlV. MemberWithError (Writer o_awlU) r_awog => Sem r_awog (o_awlU -> o_awlU, a_awlV) -> Sem r_awog a_awlV
- Polysemy.Writer: tell :: forall o_aBHz r_aBJX. MemberWithError (Writer o_aBHz) r_aBJX => o_aBHz -> Sem r_aBJX ()
+ Polysemy.Writer: tell :: forall o_awlO r_awoc. MemberWithError (Writer o_awlO) r_awoc => o_awlO -> Sem r_awoc ()

Files

ChangeLog.md view
@@ -1,10 +1,45 @@ # Changelog for polysemy ++## 1.2.0.0 (2019-09-04)++### Breaking Changes++- All `lower-` interpreters have been deprecated, in favor of corresponding+    `-Final` interpreters.+- `runFixpoint` and `runFixpointM` have been deprecated in favor of+    `fixpointToFinal`.+- The semantics for `runNonDet` when `<|>` is used inside a higher-order action+    of another effect has been changed.+- Type variables for certain internal functions, `failToEmbed`, and+    `atomicState'` have been rearranged.++## Other changes++- Added `Final` effect, an effect for embedding higher-order actions in the+    final monad of the effect stack. Any interpreter should use this instead of+    requiring to be provided an explicit lowering function to the final monad.+- Added `Strategy` environment for use together with `Final`+- Added `asyncToIOFinal`, a better alternative of `lowerAsync`+- Added `errorToIOFinal`, a better alternative of `lowerError`+- Added `fixpointToFinal`, a better alternative of `runFixpoint` and `runFixpointM`+- Added `resourceToIOFinal`, a better alternative of `lowerResource`+- Added `outputToIOMonoid` and `outputToIOMonoidAssocR`+- Added `stateToIO`+- Added `atomicStateToIO`+- Added `runWriterTVar`, `writerToIOFinal`, and `writerToIOAssocRFinal`+- Added `writerToEndoWriter`+- Added `subsume` operation+- Exposed `raiseUnder`/`2`/`3` in `Polysemy`++ ## 1.1.0.0 (2019-08-15)  ### Breaking Changes -- `MonadFail` is now implemented in terms of `Fail`, instead of `NonDet`(thanks to @KingoftheHomeless)+- `MonadFail` is now implemented in terms of `Fail`, instead of `NonDet` (thanks to @KingoftheHomeless)+- `LastMember` has been removed. `withLowerToIO` and all interpreters that make use of it+    now only requires `Member (Embed IO) r` (thanks to @KingoftheHomeless) - `State` and `Writer` now have better strictness semantics  ### Other Changes@@ -12,7 +47,9 @@ - Added `AtomicState` effect (thanks to @KingoftheHomeless) - Added `Fail` effect (thanks to @KingoftheHomeless) - Added `runOutputSem` (thanks to @cnr)+- Added `modify'`, a strict variant of `modify` (thanks to @KingoftheHomeless) - Added right-associative variants of `runOutputMonoid` and `runWriter` (thanks to @KingoftheHomeless)+- Added `runOutputMonoidIORef` and `runOutputMonoidTVar` (thanks to @KingoftheHomeless) - Improved `Fixpoint` so it won't always diverge (thanks to @KingoftheHomeless) - `makeSem` will now complain if `DataKinds` isn't enabled (thanks to @pepegar) 
README.md view
@@ -67,7 +67,10 @@ <sup><a name="fn1">1</a></sup>: Unfortunately this is not true in GHC 8.6.3, but will be true in GHC 8.10.1. +## Tutorial +Raghu Kaippully wrote a beginner friendly [tutorial](https://haskell-explained.gitlab.io/blog/posts/2019/07/28/polysemy-is-cool-part-1/index.html).+ ## Examples  Make sure you read the [Necessary Language@@ -153,7 +156,13 @@             _             -> writeTTY input >> writeTTY "no exceptions"  main :: IO (Either CustomException ())-main = (runM .@ lowerResource .@@ lowerError @CustomException) . teletypeToIO $ program+main =+    runFinal+  . embedToFinal @IO+  . resourceToIOFinal+  . errorToIOFinal @CustomException+  . teletypeToIO+  $ program ```  Easy.
polysemy.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: ed739126c69520676b38ca047f46cbddc31813120ceb38b36fe7ac3c0012a606+-- hash: a8b3a81d8983405247d7e8cb0010669caf9e8f62f85762990fadbeccdafc6094  name:           polysemy-version:        1.1.0.0+version:        1.2.0.0 synopsis:       Higher-order, low-boilerplate, zero-cost free monads. description:    Please see the README on GitHub at <https://github.com/isovector/polysemy#readme> category:       Language@@ -47,6 +47,7 @@       Polysemy.Error       Polysemy.Fail       Polysemy.Fail.Type+      Polysemy.Final       Polysemy.Fixpoint       Polysemy.Input       Polysemy.Internal@@ -57,10 +58,12 @@       Polysemy.Internal.Forklift       Polysemy.Internal.Kind       Polysemy.Internal.NonDet+      Polysemy.Internal.Strategy       Polysemy.Internal.Tactics       Polysemy.Internal.TH.Common       Polysemy.Internal.TH.Effect       Polysemy.Internal.Union+      Polysemy.Internal.Writer       Polysemy.IO       Polysemy.NonDet       Polysemy.Output@@ -118,6 +121,7 @@       BracketSpec       DoctestSpec       FailSpec+      FinalSpec       FixpointSpec       FusionSpec       HigherOrderSpec
src/Polysemy.hs view
@@ -7,14 +7,28 @@   -- * Running Sem   , run   , runM+  , runFinal    -- * Interoperating With Other Monads+  -- ** Embed   , Embed (..)   , embed+  , embedToFinal+  -- ** Final+  -- | For advanced uses of 'Final', including creating your own interpreters+  -- that make use of it, see "Polysemy.Final"+  , Final+  , embedFinal      -- * Lifting   , raise+  , raiseUnder+  , raiseUnder2+  , raiseUnder3 +    -- * Trivial Interpretation+  , subsume+     -- * Creating New Effects     -- | Effects should be defined as a GADT (enable @-XGADTs@), with kind @(*     -- -> *) -> * -> *@. Every primitive action in the effect should be its@@ -27,8 +41,8 @@     --   ReadLine  :: Console m String     -- @     ---    -- Notice that the @a@ parameter gets instataniated at the /desired return-    -- type/ of the actions. Writing a line returns a '()', but reading one+    -- Notice that the @a@ parameter gets instantiated at the /desired return/+    -- /type/ of the actions. Writing a line returns a @()@, but reading one     -- returns 'String'.     --     -- By enabling @-XTemplateHaskell@, we can use the 'makeSem' function@@ -124,4 +138,5 @@ import Polysemy.Internal.Kind import Polysemy.Internal.TH.Effect import Polysemy.Internal.Tactics+import Polysemy.Final 
src/Polysemy/Async.hs view
@@ -10,11 +10,13 @@      -- * Interpretations   , asyncToIO+  , asyncToIOFinal   , lowerAsync   ) where  import qualified Control.Concurrent.Async as A import           Polysemy+import           Polysemy.Final   @@ -32,13 +34,23 @@ makeSem ''Async  --------------------------------------------------------------------------------- | A more flexible --- though less performant ---  version of 'lowerAsync'.+-- | A more flexible --- though less performant ---+-- version of 'asyncToIOFinal'. -- -- This function is capable of running 'Async' effects anywhere within an--- effect stack, without relying on an explicit function to lower it into 'IO'.+-- effect stack, without relying on 'Final' to lower it into 'IO'. -- Notably, this means that 'Polysemy.State.State' effects will be consistent -- in the presence of 'Async'. --+-- 'asyncToIO' is __unsafe__ if you're using 'await' inside higher-order actions+-- of other effects interpreted after 'Async'.+-- See <https://github.com/polysemy-research/polysemy/issues/205 Issue #205>.+--+-- Prefer 'asyncToIOFinal' unless you need to run pure, stateful interpreters+-- after the interpreter for 'Async'.+-- (Pure interpreters are interpreters that aren't expressed in terms of+-- another effect or monad; for example, 'Polysemy.State.runState'.)+-- -- @since 1.0.0.0 asyncToIO     :: Member (Embed IO) r@@ -57,9 +69,37 @@     )  m {-# INLINE asyncToIO #-} +------------------------------------------------------------------------------+-- | Run an 'Async' effect in terms of 'A.async' through final 'IO'.+--+-- /Beware/: Effects that aren't interpreted in terms of 'IO'+-- will have local state semantics in regards to 'Async' effects+-- interpreted this way. See 'Final'.+--+-- Notably, unlike 'asyncToIO', this is not consistent with+-- 'Polysemy.State.State' unless 'Polysemy.State.runStateIORef' is used.+-- State that seems like it should be threaded globally throughout 'Async'+-- /will not be./+--+-- Use 'asyncToIO' instead if you need to run+-- pure, stateful interpreters after the interpreter for 'Async'.+-- (Pure interpreters are interpreters that aren't expressed in terms of+-- another effect or monad; for example, 'Polysemy.State.runState'.)+--+-- @since 1.2.0.0+asyncToIOFinal :: Member (Final IO) r+               => Sem (Async ': r) a+               -> Sem r a+asyncToIOFinal = interpretFinal $ \case+  Async m -> do+    ins <- getInspectorS+    m'  <- runS m+    liftS $ A.async (inspect ins <$> m')+  Await a -> liftS (A.wait a)+{-# INLINE asyncToIOFinal #-}  --------------------------------------------------------------------------------- | Run an 'Async' effect via in terms of 'A.async'.+-- | Run an 'Async' effect in terms of 'A.async'. -- -- @since 1.0.0.0 lowerAsync@@ -80,4 +120,4 @@         Await a -> pureT =<< embed (A.wait a)     )  m {-# INLINE lowerAsync #-}-+{-# DEPRECATED lowerAsync "Use 'asyncToIOFinal' instead" #-}
src/Polysemy/AtomicState.hs view
@@ -15,6 +15,7 @@     -- * Interpretations   , runAtomicStateIORef   , runAtomicStateTVar+  , atomicStateToIO   , atomicStateToState   ) where @@ -26,6 +27,10 @@  import Data.IORef +------------------------------------------------------------------------------+-- | A variant of 'State' that supports atomic operations.+--+-- @since 1.1.0.0 data AtomicState s m a where   AtomicState :: (s -> (s, a)) -> AtomicState s m a   AtomicGet   :: AtomicState s m s@@ -46,9 +51,10 @@ ----------------------------------------------------------------------------- -- | A variant of 'atomicState' in which the computation is strict in the new -- state and return value.-atomicState' :: Member (AtomicState s) r-              => (s -> (s, a))-              -> Sem r a+atomicState' :: forall s a r+              . Member (AtomicState s) r+             => (s -> (s, a))+             -> Sem r a atomicState' f = do   -- KingoftheHomeless: return value needs to be forced due to how   -- 'atomicModifyIORef' is implemented: the computation@@ -87,7 +93,8 @@ ------------------------------------------------------------------------------ -- | Run an 'AtomicState' effect by transforming it into atomic operations -- over an 'IORef'.-runAtomicStateIORef :: Member (Embed IO) r+runAtomicStateIORef :: forall s r a+                     . Member (Embed IO) r                     => IORef s                     -> Sem (AtomicState s ': r) a                     -> Sem r a@@ -110,6 +117,35 @@     return a   AtomicGet -> embed $ readTVarIO tvar {-# INLINE runAtomicStateTVar #-}++--------------------------------------------------------------------+-- | Run an 'AtomicState' effect in terms of atomic operations+-- in 'IO'.+--+-- Internally, this simply creates a new 'IORef', passes it to+-- 'runAtomicStateIORef', and then returns the result and the final value+-- of the 'IORef'.+--+-- /Beware/: As this uses an 'IORef' internally,+-- all other effects will have local+-- state semantics in regards to 'AtomicState' effects+-- interpreted this way.+-- For example, 'Polysemy.Error.throw' and 'Polysemy.Error.catch' will+-- never revert 'atomicModify's, even if 'Polysemy.Error.runError' is used+-- after 'atomicStateToIO'.+--+-- @since 1.2.0.0+atomicStateToIO :: forall s r a+                 . Member (Embed IO) r+                => s+                -> Sem (AtomicState s ': r) a+                -> Sem r (s, a)+atomicStateToIO s sem = do+  ref <- embed $ newIORef s+  res <- runAtomicStateIORef ref sem+  end <- embed $ readIORef ref+  return (end, res)+{-# INLINE atomicStateToIO #-}  ------------------------------------------------------------------------------ -- | Transform an 'AtomicState' effect to a 'State' effect, discarding
src/Polysemy/Error.hs view
@@ -13,6 +13,7 @@     -- * Interpretations   , runError   , mapError+  , errorToIOFinal   , lowerError   ) where @@ -22,6 +23,7 @@ import           Data.Bifunctor (first) import           Data.Typeable import           Polysemy+import           Polysemy.Final import           Polysemy.Internal import           Polysemy.Internal.Union @@ -131,6 +133,48 @@   ------------------------------------------------------------------------------+-- | Run an 'Error' effect as an 'IO' 'X.Exception' through final 'IO'. This+-- interpretation is significantly faster than 'runError'.+--+-- /Beware/: Effects that aren't interpreted in terms of 'IO'+-- will have local state semantics in regards to 'Error' effects+-- interpreted this way. See 'Final'.+--+-- @since 1.2.0.0+errorToIOFinal+    :: ( Typeable e+       , Member (Final IO) r+       )+    => Sem (Error e ': r) a+    -> Sem r (Either e a)+errorToIOFinal sem = withStrategicToFinal @IO $ do+  m' <- runS (runErrorAsExcFinal sem)+  s  <- getInitialStateS+  pure $+    either+      ((<$ s) . Left . unwrapExc)+      (fmap Right)+    <$> X.try m'+{-# INLINE errorToIOFinal #-}++runErrorAsExcFinal+    :: forall e r a+    .  ( Typeable e+       , Member (Final IO) r+       )+    => Sem (Error e ': r) a+    -> Sem r a+runErrorAsExcFinal = interpretFinal $ \case+  Throw e   -> pure $ X.throwIO $ WrappedExc e+  Catch m h -> do+    m' <- runS m+    h' <- bindS h+    s  <- getInitialStateS+    pure $ X.catch m' $ \(se :: WrappedExc e) ->+      h' (unwrapExc se <$ s)+{-# INLINE runErrorAsExcFinal #-}++------------------------------------------------------------------------------ -- | Run an 'Error' effect as an 'IO' 'X.Exception'. This interpretation is -- significantly faster than 'runError', at the cost of being less flexible. --@@ -151,6 +195,7 @@     . X.try     . (lower .@ runErrorAsExc) {-# INLINE lowerError #-}+{-# DEPRECATED lowerError "Use 'errorToIOFinal' instead" #-}   -- TODO(sandy): Can we use the new withLowerToIO machinery for this?@@ -171,4 +216,3 @@     embed $ X.catch (runIt t) $ \(se :: WrappedExc e) ->       runIt $ h $ unwrapExc se <$ is {-# INLINE runErrorAsExc #-}-
src/Polysemy/Fail.hs view
@@ -47,7 +47,7 @@  ------------------------------------------------------------------------------ -- | Run a 'Fail' effect in terms of an underlying 'MonadFail' instance.-failToEmbed :: forall m a r+failToEmbed :: forall m r a              . (Member (Embed m) r, MonadFail m)             => Sem (Fail ': r) a             -> Sem r a
+ src/Polysemy/Final.hs view
@@ -0,0 +1,260 @@+{-# LANGUAGE TemplateHaskell #-}+module Polysemy.Final+  (+    -- * Effect+    Final(..)+  , ThroughWeavingToFinal++    -- * Actions+  , withWeavingToFinal+  , withStrategicToFinal+  , embedFinal++    -- * Combinators for Interpreting to the Final Monad+  , interpretFinal++    -- * Strategy+    -- | Strategy is a domain-specific language very similar to @Tactics@+    -- (see 'Polysemy.Tactical'), and is used to describe how higher-order+    -- effects are threaded down to the final monad.+    --+    -- Much like @Tactics@, computations can be run and threaded+    -- through the use of 'runS' and 'bindS', and first-order constructors+    -- may use 'pureS'. In addition, 'liftS' may be used to+    -- lift actions of the final monad.+    --+    -- Unlike @Tactics@, the final return value within a 'Strategic'+    -- must be a monadic value of the target monad+    -- with the functorial state wrapped inside of it.+  , Strategic+  , WithStrategy+  , pureS+  , liftS+  , runS+  , bindS+  , getInspectorS+  , getInitialStateS++    -- * Interpretations+  , runFinal+  , finalToFinal++  -- * Interpretations for Other Effects+  , embedToFinal+  ) where++import Polysemy.Internal+import Polysemy.Internal.Combinators+import Polysemy.Internal.Union+import Polysemy.Internal.Strategy+import Polysemy.Internal.TH.Effect++-----------------------------------------------------------------------------+-- | This represents a function which produces+-- an action of the final monad @m@ given:+--+--   * The initial effectful state at the moment the action+--     is to be executed.+--+--   * A way to convert @z@ (which is typically @'Sem' r@) to @m@ by+--     threading the effectful state through.+--+--   * An inspector that is able to view some value within the+--     effectful state if the effectful state contains any values.+--+-- A @'Polysemy.Internal.Union.Weaving'@ provides these components,+-- hence the name 'ThroughWeavingToFinal'.+--+-- @since 1.2.0.0+type ThroughWeavingToFinal m z a =+     forall f+   . Functor f+  => f ()+  -> (forall x. f (z x) -> m (f x))+  -> (forall x. f x -> Maybe x)+  -> m (f a)++-----------------------------------------------------------------------------+-- | An effect for embedding higher-order actions in the final target monad+-- of the effect stack.+--+-- This is very useful for writing interpreters that interpret higher-order+-- effects in terms of the final monad.+--+-- 'Final' is more powerful than 'Embed', but is also less flexible+-- to interpret (compare 'Polysemy.Embed.runEmbedded' with 'finalToFinal').+-- If you only need the power of 'embed', then you should use 'Embed' instead.+--+-- /Beware/: 'Final' actions are interpreted as actions of the final monad,+-- and the effectful state visible to+-- 'withWeavingToFinal' \/ 'withStrategicToFinal'+-- \/ 'interpretFinal'+-- is that of /all interpreters run in order to produce the final monad/.+--+-- This means that any interpreter built using 'Final' will /not/+-- respect local/global state semantics based on the order of+-- interpreters run. You should signal interpreters that make use of+-- 'Final' by adding a @-'Final'@ suffix to the names of these.+--+-- State semantics of effects that are /not/+-- interpreted in terms of the final monad will always+-- appear local to effects that are interpreted in terms of the final monad.+--+-- State semantics between effects that are interpreted in terms of the final monad+-- depend on the final monad. For example, if the final monad is a monad transformer+-- stack, then state semantics will depend on the order monad transformers are stacked.+--+-- @since 1.2.0.0+newtype Final m z a where+  WithWeavingToFinal+    :: ThroughWeavingToFinal m z a+    -> Final m z a++makeSem_ ''Final++-----------------------------------------------------------------------------+-- | Allows for embedding higher-order actions of the final monad+-- by providing the means of explicitly threading effects through @'Sem' r@+-- to the final monad.+--+-- Consider using 'withStrategicToFinal' instead,+-- which provides a more user-friendly interface, but is also slightly weaker.+--+-- You are discouraged from using 'withWeavingToFinal' directly+-- in application code, as it ties your application code directly to+-- the final monad.+--+-- @since 1.2.0.0+withWeavingToFinal+  :: forall m r a+   . Member (Final m) r+  => ThroughWeavingToFinal m (Sem r) a+  -> Sem r a+++-----------------------------------------------------------------------------+-- | 'withWeavingToFinal' admits an implementation of 'embed'.+--+-- Just like 'embed', you are discouraged from using this in application code.+--+-- @since 1.2.0.0+embedFinal :: (Member (Final m) r, Functor m) => m a -> Sem r a+embedFinal m = withWeavingToFinal $ \s _ _ -> (<$ s) <$> m+{-# INLINE embedFinal #-}++-----------------------------------------------------------------------------+-- | Allows for embedding higher-order actions of the final monad+-- by providing the means of explicitly threading effects through @'Sem' r@+-- to the final monad. This is done through the use of the 'Strategic'+-- environment, which provides 'runS' and 'bindS'.+--+-- You are discouraged from using 'withStrategicToFinal' in application code,+-- as it ties your application code directly to the final monad.+--+-- @since 1.2.0.0+withStrategicToFinal :: Member (Final m) r+                     => Strategic m (Sem r) a+                     -> Sem r a+withStrategicToFinal strat = withWeavingToFinal (runStrategy strat)+{-# INLINE withStrategicToFinal #-}++------------------------------------------------------------------------------+-- | Like 'interpretH', but may be used to+-- interpret higher-order effects in terms of the final monad.+--+-- 'interpretFinal' requires less boilerplate than using 'interpretH'+-- together with 'withStrategicToFinal' \/ 'withWeavingToFinal',+-- but is also less powerful.+-- 'interpretFinal' does not provide any means of executing actions+-- of @'Sem' r@ as you interpret each action, and the provided interpreter+-- is automatically recursively used to process higher-order occurences of+-- @'Sem' (e ': r)@ to @'Sem' r@.+--+-- If you need greater control of how the effect is interpreted,+-- use 'interpretH' together with 'withStrategicToFinal' \/+-- 'withWeavingToFinal' instead.+--+-- /Beware/: Effects that aren't interpreted in terms of the final+-- monad will have local state semantics in regards to effects+-- interpreted using 'interpretFinal'. See 'Final'.+--+-- @since 1.2.0.0+interpretFinal+    :: forall m e r a+     . Member (Final m) r+    => (forall x n. e n x -> Strategic m n x)+       -- ^ A natural transformation from the handled effect to the final monad.+    -> Sem (e ': r) a+    -> Sem r a+interpretFinal n =+  let+    go :: Sem (e ': r) x -> Sem r x+    go = hoistSem $ \u -> case decomp u of+      Right (Weaving e s wv ex ins) ->+        injWeaving $+          Weaving+            (WithWeavingToFinal (runStrategy (n e)))+            s+            (go . wv)+            ex+            ins+      Left g -> hoist go g+    {-# INLINE go #-}+  in+    go+{-# INLINE interpretFinal #-}++------------------------------------------------------------------------------+-- | Lower a 'Sem' containing only a single lifted, final 'Monad' into that+-- monad.+--+-- If you also need to process an @'Embed' m@ effect, use this together with+-- 'embedToFinal'.+--+-- @since 1.2.0.0+runFinal :: Monad m => Sem '[Final m] a -> m a+runFinal = usingSem $ \u -> case extract u of+  Weaving (WithWeavingToFinal wav) s wv ex ins ->+    ex <$> wav s (runFinal . wv) ins+{-# INLINE runFinal #-}++------------------------------------------------------------------------------+-- | Given natural transformations between @m1@ and @m2@, run a @'Final' m1@+-- effect by transforming it into a @'Final' m2@ effect.+--+-- @since 1.2.0.0+finalToFinal :: forall m1 m2 r a+              . Member (Final m2) r+             => (forall x. m1 x -> m2 x)+             -> (forall x. m2 x -> m1 x)+             -> Sem (Final m1 ': r) a+             -> Sem r a+finalToFinal to from =+  let+    go :: Sem (Final m1 ': r) x -> Sem r x+    go = hoistSem $ \u -> case decomp u of+      Right (Weaving (WithWeavingToFinal wav) s wv ex ins) ->+        injWeaving $+          Weaving+            (WithWeavingToFinal $ \s' wv' ins' ->+              to $ wav s' (from . wv') ins'+            )+            s+            (go . wv)+            ex+            ins+      Left g -> hoist go g+    {-# INLINE go #-}+  in+    go+{-# INLINE finalToFinal #-}++------------------------------------------------------------------------------+-- | Transform an @'Embed' m@ effect into a @'Final' m@ effect+--+-- @since 1.2.0.0+embedToFinal :: (Member (Final m) r, Functor m)+             => Sem (Embed m ': r) a+             -> Sem r a+embedToFinal = interpret $ \(Embed m) -> embedFinal m+{-# INLINE embedToFinal #-}
src/Polysemy/Fixpoint.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE AllowAmbiguousTypes, TemplateHaskell #-}  module Polysemy.Fixpoint   ( -- * Effect@@ -12,11 +12,15 @@ import Data.Maybe  import Polysemy+import Polysemy.Final import Polysemy.Internal.Fixpoint ---------------------------------------------------------------------------------- | Run a 'Fixpoint' effect purely.+-----------------------------------------------------------------------------+-- | Run a 'Fixpoint' effect in terms of a final 'MonadFix' instance. --+-- If you need to run a 'Fixpoint' effect purely, use this together with+-- @'Final' 'Data.Functor.Identity.Identity'@.+-- -- __Note__: This is subject to the same traps as 'MonadFix' instances for -- monads with failure: this will throw an exception if you try to recursively use -- the result of a failed computation in an action whose effect may be observed@@ -24,12 +28,14 @@ -- -- For example, the following program will throw an exception upon evaluating the -- final state:+-- -- @ -- bad :: (Int, Either () Int) -- bad =---    'run'---  . 'runFixpoint' 'run'---  . 'Polysemy.State.runLazyState' @Int 1+--    'Data.Functor.Identity.runIdentity'+--  . 'runFinal'+--  . 'fixpointToFinal' \@'Data.Functor.Identity.Identity'+--  . 'Polysemy.State.runLazyState' \@Int 1 --  . 'Polysemy.Error.runError' --  $ mdo --   'Polysemy.State.put' a@@ -37,16 +43,40 @@ --   return a -- @ ----- 'runFixpoint' also operates under the assumption that any effectful+-- 'fixpointToFinal' also operates under the assumption that any effectful -- state which can't be inspected using 'Polysemy.Inspector' can't contain any--- values. This is true for all interpreters featured in this package,+-- values. For example, the effectful state for 'Polysemy.Error.runError' is+-- @'Either' e a@. The inspector for this effectful state only fails if the+-- effectful state is a @'Left'@ value, which therefore doesn't contain any+-- values of @a@.+--+-- This assumption holds true for all interpreters featured in this package, -- and is presumably always true for any properly implemented interpreter.--- 'runFixpoint' may throw an exception if it is used together with an+-- 'fixpointToFinal' may throw an exception if it is used together with an -- interpreter that uses 'Polysemy.Internal.Union.weave' improperly. ----- If 'runFixpoint' throws an exception for you, and it can't+-- If 'fixpointToFinal' throws an exception for you, and it can't -- be due to any of the above, then open an issue over at the -- GitHub repository for polysemy.+--+-- @since 1.2.0.0+fixpointToFinal :: forall m r a+                 . (Member (Final m) r, MonadFix m)+                => Sem (Fixpoint ': r) a+                -> Sem r a+fixpointToFinal = interpretFinal @m $+  \(Fixpoint f) -> do+    f'  <- bindS f+    s   <- getInitialStateS+    ins <- getInspectorS+    pure $ mfix $ \fa -> f' $+      fromMaybe (bomb "fixpointToFinal") (inspect ins fa) <$ s+{-# INLINE fixpointToFinal #-}++------------------------------------------------------------------------------+-- | Run a 'Fixpoint' effect purely.+--+-- __Note__: 'runFixpoint' is subject to the same caveats as 'fixpointToFinal'. runFixpoint     :: (∀ x. Sem r x -> x)     -> Sem (Fixpoint ': r) a@@ -60,11 +90,14 @@       lower . runFixpoint lower . c $         fromMaybe (bomb "runFixpoint") (inspect ins fa) <$ s {-# INLINE runFixpoint #-}+{-# DEPRECATED runFixpoint "Use 'fixpointToFinal' together with \+                           \'Data.Functor.Identity.Identity' instead" #-} + ------------------------------------------------------------------------------ -- | Run a 'Fixpoint' effect in terms of an underlying 'MonadFix' instance. ----- __Note__: 'runFixpointM' is subject to the same caveats as 'runFixpoint'.+-- __Note__: 'runFixpointM' is subject to the same caveats as 'fixpointToFinal'. runFixpointM     :: ( MonadFix m        , Member (Embed m) r@@ -81,3 +114,4 @@       lower . runFixpointM lower . c $         fromMaybe (bomb "runFixpointM") (inspect ins fa) <$ s {-# INLINE runFixpointM #-}+{-# DEPRECATED runFixpointM "Use 'fixpointToFinal' instead" #-}
src/Polysemy/Internal.hs view
@@ -18,6 +18,7 @@   , raiseUnder   , raiseUnder2   , raiseUnder3+  , subsume   , Embed (..)   , usingSem   , liftSem@@ -60,15 +61,19 @@ -- interpretations (and others that you might add) may be used interchangably -- without needing to write any newtypes or 'Monad' instances. The only -- change needed to swap interpretations is to change a call from--- 'Polysemy.Error.runError' to 'Polysemy.Error.lowerError'.+-- 'Polysemy.Error.runError' to 'Polysemy.Error.errorToIOFinal'. -- -- The effect stack @r@ can contain arbitrary other monads inside of it. These -- monads are lifted into effects via the 'Embed' effect. Monadic values can be -- lifted into a 'Sem' via 'embed'. --+-- Higher-order actions of another monad can be lifted into higher-order actions+-- of 'Sem' via the 'Polysemy.Final' effect, which is more powerful+-- than 'Embed', but also less flexible to interpret.+-- -- A 'Sem' can be interpreted as a pure value (via 'run') or as any--- traditional 'Monad' (via 'runM'). Each effect @E@ comes equipped with some--- interpreters of the form:+-- traditional 'Monad' (via 'runM' or 'Polysemy.runFinal').+-- Each effect @E@ comes equipped with some interpreters of the form: -- -- @ -- runE :: 'Sem' (E ': r) a -> 'Sem' r a@@ -126,8 +131,9 @@ -- behaviour over other effects later in the chain. -- -- After all of your effects are handled, you'll be left with either--- a @'Sem' '[] a@ or a @'Sem' '[ 'Embed' m ] a@ value, which can be--- consumed respectively by 'run' and 'runM'.+-- a @'Sem' '[] a@, a @'Sem' '[ 'Embed' m ] a@, or a @'Sem' '[ 'Polysemy.Final' m ] a@+-- value, which can be consumed respectively by 'run', 'runM', and+-- 'Polysemy.runFinal'. -- -- ==== Examples --@@ -272,7 +278,7 @@   mzero = empty   mplus = (<|>) --- | TODO: @since _+-- | @since 1.1.0.0 instance (Member Fail r) => MonadFail (Sem r) where   fail = send . Fail   {-# INLINE fail #-}@@ -303,6 +309,7 @@ hoistSem nat (Sem m) = Sem $ \k -> m $ \u -> k $ nat u {-# INLINE hoistSem #-} + ------------------------------------------------------------------------------ -- | Introduce an effect into 'Sem'. Analogous to -- 'Control.Monad.Class.Trans.lift' in the mtl ecosystem@@ -314,6 +321,30 @@ ------------------------------------------------------------------------------ -- | Like 'raise', but introduces a new effect underneath the head of the -- list.+--+-- 'raiseUnder' can be used in order to turn transformative interpreters+-- into reinterpreters. This is especially useful if you're writing an interpreter+-- which introduces an intermediary effect, and then want to use an existing+-- interpreter on that effect.+--+-- For example, given:+--+-- @+-- fooToBar :: 'Member' Bar r => 'Sem' (Foo ': r) a -> 'Sem' r a+-- runBar   :: 'Sem' (Bar ': r) a -> 'Sem' r a+-- @+--+-- You can write:+--+-- @+-- runFoo :: 'Sem' (Foo ': r) a -> 'Sem' r a+-- runFoo =+--     runBar     -- Consume Bar+--   . fooToBar   -- Interpret Foo in terms of the new Bar+--   . 'raiseUnder' -- Introduces Bar under Foo+-- @+--+-- @since 1.2.0.0 raiseUnder :: ∀ e2 e1 r a. Sem (e1 ': r) a -> Sem (e1 ': e2 ': r) a raiseUnder = hoistSem $ hoist raiseUnder . weakenUnder   where@@ -327,6 +358,8 @@ ------------------------------------------------------------------------------ -- | Like 'raise', but introduces two new effects underneath the head of the -- list.+--+-- @since 1.2.0.0 raiseUnder2 :: ∀ e2 e3 e1 r a. Sem (e1 ': r) a -> Sem (e1 ': e2 ': e3 ': r) a raiseUnder2 = hoistSem $ hoist raiseUnder2 . weakenUnder2   where@@ -340,6 +373,8 @@ ------------------------------------------------------------------------------ -- | Like 'raise', but introduces two new effects underneath the head of the -- list.+--+-- @since 1.2.0.0 raiseUnder3 :: ∀ e2 e3 e4 e1 r a. Sem (e1 ': r) a -> Sem (e1 ': e2 ': e3 ': e4 ': r) a raiseUnder3 = hoistSem $ hoist raiseUnder3 . weakenUnder3   where@@ -349,6 +384,20 @@     {-# INLINE weakenUnder3 #-} {-# INLINE raiseUnder3 #-} +------------------------------------------------------------------------------+-- | Interprets an effect in terms of another identical effect.+--+-- This is useful for defining interpreters that use 'Polysemy.reinterpretH'+-- without immediately consuming the newly introduced effect.+-- Using such an interpreter recursively may result in duplicate effects,+-- which may then be eliminated using 'subsume'.+--+-- @since 1.2.0.0+subsume :: Member e r => Sem (e ': r) a -> Sem r a+subsume = hoistSem $ \u -> hoist subsume $ case decomp u of+  Right w -> injWeaving w+  Left  g -> g+{-# INLINE subsume #-}  ------------------------------------------------------------------------------ -- | Embed an effect into a 'Sem'. This is used primarily via@@ -415,6 +464,11 @@ -- just for initialization. This can result in rather surprising behavior. For -- a version of '.@' that won't duplicate work, see the @.\@!@ operator in -- <http://hackage.haskell.org/package/polysemy-zoo/docs/Polysemy-IdempotentLowering.html polysemy-zoo>.+--+-- Interpreters using 'Polysemy.Final' may be composed normally, and+-- avoid the work duplication issue. For that reason, you're encouraged to use+-- @-'Polysemy.Final'@ interpreters instead of @lower-@ interpreters whenever+-- possible. (.@)     :: Monad m     => (∀ x. Sem r x -> m x)
src/Polysemy/Internal/Fixpoint.hs view
@@ -4,13 +4,14 @@  ------------------------------------------------------------------------------ -- | An effect for providing 'Control.Monad.Fix.mfix'.-data Fixpoint m a where+newtype Fixpoint m a where   Fixpoint :: (a -> m a) -> Fixpoint m a   --------------------------------------------------------------------------------- | The error used in 'Polysemy.Fixpoint.runFixpoint' and--- 'Polysemy.Fixpoint.runFixpointM' when the result of a failed computation+-- | The error used in 'Polysemy.Fixpoint.fixpointToFinal',+-- 'Polysemy.Fixpoint.runFixpoint' and 'Polysemy.Fixpoint.runFixpointM'+-- when the result of a failed computation -- is recursively used and somehow visible. You may use this for your own -- 'Fixpoint' interpreters. The argument should be the name of the interpreter. bomb :: String -> a
src/Polysemy/Internal/Forklift.hs view
@@ -9,6 +9,7 @@ import qualified Control.Concurrent.Async as A import           Control.Concurrent.Chan.Unagi import           Control.Concurrent.MVar+import           Control.Exception import           Polysemy.Internal import           Polysemy.Internal.Union @@ -66,7 +67,7 @@   res <- embed $ A.async $ do     a <- action (runViaForklift inchan)                 (putMVar signal ())-    putMVar signal ()+          `finally` (putMVar signal ())     pure a    let me = do
+ src/Polysemy/Internal/Strategy.hs view
@@ -0,0 +1,131 @@+{-# OPTIONS_HADDOCK not-home #-}++module Polysemy.Internal.Strategy where++import Polysemy.Internal+import Polysemy.Internal.Combinators+import Polysemy.Internal.Tactics (Inspector(..))++++data Strategy m f n z a where+  GetInitialState     :: Strategy m f n z (f ())+  HoistInterpretation :: (a -> n b) -> Strategy m f n z (f a -> m (f b))+  GetInspector        :: Strategy m f n z (Inspector f)+++------------------------------------------------------------------------------+-- | 'Strategic' is an environment in which you're capable of explicitly+-- threading higher-order effect states to the final monad.+-- This is a variant of @Tactics@ (see 'Polysemy.Tactical'), and usage+-- is extremely similar.+--+-- @since 1.2.0.0+type Strategic m n a = forall f. Functor f => Sem (WithStrategy m f n) (m (f a))+++------------------------------------------------------------------------------+-- | @since 1.2.0.0+type WithStrategy m f n = '[Strategy m f n]+++------------------------------------------------------------------------------+-- | Internal function to process Strategies in terms of+-- 'Polysemy.Final.withWeavingToFinal'.+--+-- @since 1.2.0.0+runStrategy :: Functor f+            => Sem '[Strategy m f n] a+            -> f ()+            -> (forall x. f (n x) -> m (f x))+            -> (forall x. f x -> Maybe x)+            -> a+runStrategy sem = \s wv ins -> run $ interpret+  (\case+    GetInitialState       -> pure s+    HoistInterpretation f -> pure $ \fa -> wv (f <$> fa)+    GetInspector          -> pure (Inspector ins)+  ) sem+{-# INLINE runStrategy #-}+++------------------------------------------------------------------------------+-- | Get a natural transformation capable of potentially inspecting values+-- inside of @f@. Binding the result of 'getInspectorS' produces a function that+-- can sometimes peek inside values returned by 'bindS'.+--+-- This is often useful for running callback functions that are not managed by+-- polysemy code.+--+-- See also 'Polysemy.getInspectorT'+--+-- @since 1.2.0.0+getInspectorS :: forall m f n. Sem (WithStrategy m f n) (Inspector f)+getInspectorS = send (GetInspector @m @f @n)+{-# INLINE getInspectorS #-}+++------------------------------------------------------------------------------+-- | Get the stateful environment of the world at the moment the+-- @Strategy@ is to be run.+--+-- Prefer 'pureS', 'liftS', 'runS', or 'bindS' instead of using this function+-- directly.+--+-- @since 1.2.0.0+getInitialStateS :: forall m f n. Sem (WithStrategy m f n) (f ())+getInitialStateS = send (GetInitialState @m @f @n)+{-# INLINE getInitialStateS #-}+++------------------------------------------------------------------------------+-- | Embed a value into 'Strategic'.+--+-- @since 1.2.0.0+pureS :: Applicative m => a -> Strategic m n a+pureS a = pure . (a <$) <$> getInitialStateS+{-# INLINE pureS #-}+++------------------------------------------------------------------------------+-- | Lifts an action of the final monad into 'Strategic'.+--+-- /Note/: you don't need to use this function if you already have a monadic+-- action with the functorial state threaded into it, by the use of+-- 'runS' or 'bindS'.+-- In these cases, you need only use 'pure' to embed the action into the+-- 'Strategic' environment.+--+-- @since 1.2.0.0+liftS :: Functor m => m a -> Strategic m n a+liftS m = do+  s <- getInitialStateS+  pure $ fmap (<$ s) m+{-# INLINE liftS #-}+++------------------------------------------------------------------------------+-- | Lifts a monadic action into the stateful environment, in terms+-- of the final monad.+-- The stateful environment will be the same as the one that the @Strategy@+-- is initially run in.+--+-- Use 'bindS'  if you'd prefer to explicitly manage your stateful environment.+--+-- @since 1.2.0.0+runS :: n a -> Sem (WithStrategy m f n) (m (f a))+runS na = bindS (const na) <*> getInitialStateS+{-# INLINE runS #-}+++------------------------------------------------------------------------------+-- | Embed a kleisli action into the stateful environment, in terms of the final+-- monad. You can use 'bindS' to get an effect parameter of the form @a -> n b@+-- into something that can be used after calling 'runS' on an effect parameter+-- @n a@.+--+-- @since 1.2.0.0+bindS :: (a -> n b) -> Sem (WithStrategy m f n) (f a -> m (f b))+bindS = send . HoistInterpretation+{-# INLINE bindS #-}+
src/Polysemy/Internal/Union.hs view
@@ -20,6 +20,7 @@   , hoist   -- * Building Unions   , inj+  , injWeaving   , weaken   -- * Using Unions   , decomp@@ -227,18 +228,23 @@  ------------------------------------------------------------------------------ -- | Lift an effect @e@ into a 'Union' capable of holding it.-inj :: forall r e a m. (Functor m , Member e r) => e m a -> Union r m a-inj e = Union (finder @_ @r @e) $+inj :: forall e r m a. (Functor m , Member e r) => e m a -> Union r m a+inj e = injWeaving $   Weaving e (Identity ())             (fmap Identity . runIdentity)             runIdentity             (Just . runIdentity) {-# INLINE inj #-} +------------------------------------------------------------------------------+-- | Lift a @'Weaving' e@ into a 'Union' capable of holding it.+injWeaving :: forall e r m a. Member e r => Weaving e m a -> Union r m a+injWeaving = Union (finder @_ @r @e)+{-# INLINE injWeaving #-}  ------------------------------------------------------------------------------ -- | Attempt to take an @e@ effect out of a 'Union'.-prj :: forall e r a m+prj :: forall e r m a      . ( Member e r        )     => Union r m a
+ src/Polysemy/Internal/Writer.hs view
@@ -0,0 +1,158 @@+{-# LANGUAGE BangPatterns, TemplateHaskell, TupleSections #-}+{-# OPTIONS_HADDOCK not-home #-}+module Polysemy.Internal.Writer where++import Control.Concurrent.STM+import Control.Exception+import Control.Monad++import Data.Semigroup++import Polysemy+import Polysemy.Final+++------------------------------------------------------------------------------+-- | An effect capable of emitting and intercepting messages.+data Writer o m a where+  Tell   :: o -> Writer o m ()+  Listen :: ∀ o m a. m a -> Writer o m (o, a)+  Pass   :: m (o -> o, a) -> Writer o m a++makeSem ''Writer++-- TODO(KingoftheHomeless): Research if this is more or less efficient than+-- using 'reinterpretH' + 'subsume'++-----------------------------------------------------------------------------+-- | Transform a @'Writer' o@ effect into a  @'Writer' ('Endo' o)@ effect,+-- right-associating all uses of '<>' for @o@.+--+-- This can be used together with 'raiseUnder' in order to create+-- @-AssocR@ variants out of regular 'Writer' interpreters.+--+-- @since 1.2.0.0+writerToEndoWriter+    :: (Monoid o, Member (Writer (Endo o)) r)+    => Sem (Writer o ': r) a+    -> Sem r a+writerToEndoWriter = interpretH $ \case+      Tell o   -> tell (Endo (o <>)) >>= pureT+      Listen m -> do+        m' <- writerToEndoWriter <$> runT m+        raise $ do+          (o, fa) <- listen m'+          return $ (,) (appEndo o mempty) <$> fa+      Pass m -> do+        ins <- getInspectorT+        m'  <- writerToEndoWriter <$> runT m+        raise $ pass $ do+          t <- m'+          let+            f' =+              maybe+                id+                (\(f, _) (Endo oo) -> let !o' = f (oo mempty) in Endo (o' <>))+                (inspect ins t)+          return (f', fmap snd t)+{-# INLINE writerToEndoWriter #-}+++-- TODO(KingoftheHomeless): Make this mess more palatable+--+-- 'interpretFinal' is too weak for our purposes, so we+-- use 'interpretH' + 'withWeavingToFinal'.++------------------------------------------------------------------------------+-- | A variant of 'Polysemy.Writer.runWriterTVar' where an 'STM' action is+-- used instead of a 'TVar' to commit 'tell's.+runWriterSTMAction :: forall o r a+                          . (Member (Final IO) r, Monoid o)+                         => (o -> STM ())+                         -> Sem (Writer o ': r) a+                         -> Sem r a+runWriterSTMAction write = interpretH $ \case+  Tell o -> do+    t <- embedFinal $ atomically (write o)+    pureT t+  Listen m -> do+    m' <- runT m+    -- Using 'withWeavingToFinal' instead of 'withStrategicToFinal'+    -- here allows us to avoid using two additional 'embedFinal's in+    -- order to create the TVars.+    raise $ withWeavingToFinal $ \s wv _ -> mask $ \restore -> do+      -- See below to understand how this works+      tvar   <- newTVarIO mempty+      switch <- newTVarIO False+      fa     <-+        restore (wv (runWriterSTMAction (write' tvar switch) m' <$ s))+          `onException` commit tvar switch id+      o      <- commit tvar switch id+      return $ (fmap . fmap) (o, ) fa+  Pass m -> do+    m'  <- runT m+    ins <- getInspectorT+    raise $ withWeavingToFinal $ \s wv ins' -> mask $ \restore -> do+      tvar   <- newTVarIO mempty+      switch <- newTVarIO False+      t      <-+        restore (wv (runWriterSTMAction (write' tvar switch) m' <$ s))+          `onException` commit tvar switch id+      _      <- commit tvar switch+        (maybe id fst $ ins' t >>= inspect ins)+      return $ (fmap . fmap) snd t++  where+    {- KingoftheHomeless:+      'write'' is used by the argument computation to a 'listen' or 'pass'+      in order to 'tell', rather than directly using the 'write'.+      This is because we need to temporarily store its+      'tell's seperately in order for the 'listen'/'pass' to work+      properly. Once the 'listen'/'pass' completes, we 'commit' the+      changes done to the local tvar globally through 'write'.++      'commit' is protected by 'mask'+'onException'. Combine this+      with the fact that the 'withWeavingToFinal' can't be interrupted+      by pure errors emitted by effects (since these will be+      represented as part of the functorial state), and we+      guarantee that no writes will be lost if the argument computation+      fails for whatever reason.++      The argument computation to a 'listen'/'pass' may also spawn+      asynchronous computations which do 'tell's of their own.+      In order to make sure these 'tell's won't be lost once a+      'listen'/'pass' completes, a switch is used to+      control which tvar 'write'' writes to. The switch is flipped+      atomically together with commiting the writes of the local tvar+      as part of 'commit'. Once the switch is flipped,+      any asynchrounous computations spawned by the argument+      computation will write to the global tvar instead of the local+      tvar (which is no longer relevant), and thus no writes will be+      lost.+    -}+    write' :: TVar o+           -> TVar Bool+           -> o+           -> STM ()+    write' tvar switch = \o -> do+      useGlobal <- readTVar switch+      if useGlobal then+        write o+      else do+        s <- readTVar tvar+        writeTVar tvar $! s <> o++    commit :: TVar o+           -> TVar Bool+           -> (o -> o)+           -> IO o+    commit tvar switch f = atomically $ do+      o <- readTVar tvar+      let !o' = f o+      -- Likely redundant, but doesn't hurt.+      alreadyCommited <- readTVar switch+      unless alreadyCommited $+        write o'+      writeTVar switch True+      return o'+{-# INLINE runWriterSTMAction #-}
src/Polysemy/NonDet.hs view
@@ -13,14 +13,63 @@  import Control.Applicative import Control.Monad.Trans.Maybe-import Data.Maybe+ import Polysemy import Polysemy.Error import Polysemy.Internal import Polysemy.Internal.NonDet import Polysemy.Internal.Union +------------------------------------------------------------------------------+-- | Run a 'NonDet' effect in terms of some underlying 'Alternative' @f@.+runNonDet :: Alternative f => Sem (NonDet ': r) a -> Sem r (f a)+runNonDet = runNonDetC . runNonDetInC+{-# INLINE runNonDet #-} +------------------------------------------------------------------------------+-- | Run a 'NonDet' effect in terms of an underlying 'Maybe'+--+-- Unlike 'runNonDet', uses of '<|>' will not execute the+-- second branch at all if the first option succeeds.+--+-- @since 1.1.0.0+runNonDetMaybe :: Sem (NonDet ': r) a -> Sem r (Maybe a)+runNonDetMaybe (Sem sem) = Sem $ \k -> runMaybeT $ sem $ \u ->+  case decomp u of+    Right (Weaving e s wv ex _) ->+      case e of+        Empty -> empty+        Choose left right ->+          MaybeT $ usingSem k $ runMaybeT $ fmap ex $ do+              MaybeT (runNonDetMaybe (wv (left <$ s)))+          <|> MaybeT (runNonDetMaybe (wv (right <$ s)))+    Left x -> MaybeT $+      k $ weave (Just ())+          (maybe (pure Nothing) runNonDetMaybe)+          id+          x+{-# INLINE runNonDetMaybe #-}++------------------------------------------------------------------------------+-- | Transform a 'NonDet' effect into an @'Error' e@ effect,+-- through providing an exception that 'empty' may be mapped to.+--+-- This allows '<|>' to handle 'throw's of the @'Error' e@ effect.+--+-- @since 1.1.0.0+nonDetToError :: Member (Error e) r+              => e+              -> Sem (NonDet ': r) a+              -> Sem r a+nonDetToError (e :: e) = interpretH $ \case+  Empty -> throw e+  Choose left right -> do+    left'  <- nonDetToError e <$> runT left+    right' <- nonDetToError e <$> runT right+    raise (left' `catch` \(_ :: e) -> right')+{-# INLINE nonDetToError #-}++ -------------------------------------------------------------------------------- -- This stuff is lifted from 'fused-effects'. Thanks guys! runNonDetC :: (Alternative f, Applicative m) => NonDetC m a -> m (f a)@@ -56,63 +105,49 @@     a (\ a' -> unNonDetC (f a') cons)   {-# INLINE (>>=) #-} ----------------------------------------------------------------------------------- | Run a 'NonDet' effect in terms of some underlying 'Alternative' @f@.-runNonDet :: Alternative f => Sem (NonDet ': r) a -> Sem r (f a)-runNonDet = runNonDetC . runNonDetInC-{-# INLINE runNonDet #-}- runNonDetInC :: Sem (NonDet ': r) a -> NonDetC (Sem r) a runNonDetInC = usingSem $ \u ->   case decomp u of-    Left x  -> NonDetC $ \cons nil -> do-      z <- liftSem $ weave [()]-                     (fmap concat . traverse runNonDet)-                     -- TODO(sandy): Is this the right semantics?-                     listToMaybe-                     x-      foldr cons nil z+    Left x  -> consC $ fmap getNonDetState $+      liftSem $ weave (NonDetState (Just ((), empty)))+                  distribNonDetC+                  -- TODO(KingoftheHomeless): Is THIS the right semantics?+                  (fmap fst . getNonDetState)+                  x     Right (Weaving Empty _ _ _ _) -> empty     Right (Weaving (Choose left right) s wv ex _) -> fmap ex $       runNonDetInC (wv (left <$ s)) <|> runNonDetInC (wv (right <$ s)) {-# INLINE runNonDetInC #-} ---------------------------------------------------------------------------------- | Run a 'NonDet' effect in terms of an underlying 'Maybe'+-- This choice of functorial state is inspired from the+-- MonadBaseControl instance for 'ListT' from 'list-t'. ----- Unlike 'runNonDet', uses of '<|>' will not execute the--- second branch at all if the first option succeeds.-runNonDetMaybe :: Sem (NonDet ': r) a -> Sem r (Maybe a)-runNonDetMaybe (Sem sem) = Sem $ \k -> runMaybeT $ sem $ \u ->-  case decomp u of-    Right (Weaving e s wv ex _) ->-      case e of-        Empty -> empty-        Choose left right ->-          MaybeT $ usingSem k $ runMaybeT $ fmap ex $ do-              MaybeT (runNonDetMaybe (wv (left <$ s)))-          <|> MaybeT (runNonDetMaybe (wv (right <$ s)))-    Left x -> MaybeT $-      k $ weave (Just ())-          (maybe (pure Nothing) runNonDetMaybe)-          id-          x-{-# INLINE runNonDetMaybe #-}+-- TODO(KingoftheHomeless):+-- Is there a different representation of this which doesn't require+-- 'unconsC' in 'distribNonDetC'?+newtype NonDetState r a = NonDetState {+  getNonDetState :: Maybe (a, NonDetC (Sem r) a)+  } deriving (Functor) ---------------------------------------------------------------------------------- | Transform a 'NonDet' effect into an @'Error' e@ effect,--- through providing an exception that 'empty' may be mapped to.------ This allows '<|>' to handle 'throw's of the @'Error' e@ effect.-nonDetToError :: Member (Error e) r-              => e-              -> Sem (NonDet ': r) a-              -> Sem r a-nonDetToError (e :: e) = interpretH $ \case-  Empty -> throw e-  Choose left right -> do-    left'  <- nonDetToError e <$> runT left-    right' <- nonDetToError e <$> runT right-    raise (left' `catch` \(_ :: e) -> right')-{-# INLINE nonDetToError #-}+-- KingoftheHomeless: The performance of this could be improved+-- if we weren't forced to use unconsC, which causes this to have+-- potentially O(n^2) behaviour.+distribNonDetC :: NonDetState r (Sem (NonDet ': r) a) -> Sem r (NonDetState r a)+distribNonDetC = \case+  NonDetState (Just (a, r)) ->+    fmap NonDetState $ unconsC $ runNonDetInC a <|> (r >>= runNonDetInC)+  _ ->+    pure (NonDetState Nothing)+{-# INLINE distribNonDetC #-}++-- O(n)+unconsC :: NonDetC (Sem r) a -> Sem r (Maybe (a, NonDetC (Sem r) a))+unconsC (NonDetC n) = n (\a r -> pure (Just (a, consC r))) (pure Nothing)+{-# INLINE unconsC #-}++consC :: Sem r (Maybe (a, NonDetC (Sem r) a)) -> NonDetC (Sem r) a+consC m = NonDetC $ \cons nil -> m >>= \case+  Just (a, r) -> cons a (unNonDetC r cons nil)+  _           -> nil+{-# INLINE consC #-}+
src/Polysemy/Output.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE BangPatterns, TemplateHaskell #-}  module Polysemy.Output   ( -- * Effect@@ -13,6 +13,8 @@   , runOutputMonoidAssocR   , runOutputMonoidIORef   , runOutputMonoidTVar+  , outputToIOMonoid+  , outputToIOMonoidAssocR   , ignoreOutput   , runOutputBatched   , runOutputSem@@ -75,6 +77,8 @@ -- -- You should always use this instead of 'runOutputMonoid' if the monoid -- is a list, such as 'String'.+--+-- @since 1.1.0.0 runOutputMonoidAssocR     :: forall o m r a      . Monoid m@@ -83,12 +87,14 @@     -> Sem r (m, a) runOutputMonoidAssocR f =     fmap (first (`appEndo` mempty))-  . runOutputMonoid (\a -> Endo (f a <>))+  . runOutputMonoid (\o -> let !o' = f o in Endo (o' <>)) {-# INLINE runOutputMonoidAssocR #-}  ------------------------------------------------------------------------------ -- | Run an 'Output' effect by transforming it into atomic operations -- over an 'IORef'.+--+-- @since 1.1.0.0 runOutputMonoidIORef     :: forall o m r a      . (Monoid m, Member (Embed IO) r)@@ -97,12 +103,14 @@     -> Sem (Output o ': r) a     -> Sem r a runOutputMonoidIORef ref f = interpret $ \case-  Output o -> embed $ atomicModifyIORef' ref (\s -> (s <> f o, ()))+  Output o -> embed $ atomicModifyIORef' ref (\s -> let !o' = f o in (s <> o', ())) {-# INLINE runOutputMonoidIORef #-}  ------------------------------------------------------------------------------ -- | Run an 'Output' effect by transforming it into atomic operations -- over a 'TVar'.+--+-- @since 1.1.0.0 runOutputMonoidTVar     :: forall o m r a      . (Monoid m, Member (Embed IO) r)@@ -115,6 +123,64 @@     s <- readTVar tvar     writeTVar tvar $! s <> f o {-# INLINE runOutputMonoidTVar #-}+++--------------------------------------------------------------------+-- | Run an 'Output' effect in terms of atomic operations+-- in 'IO'.+--+-- Internally, this simply creates a new 'IORef', passes it to+-- 'runOutputMonoidIORef', and then returns the result and the final value+-- of the 'IORef'.+--+-- /Beware/: As this uses an 'IORef' internally,+-- all other effects will have local+-- state semantics in regards to 'Output' effects+-- interpreted this way.+-- For example, 'Polysemy.Error.throw' and 'Polysemy.Error.catch' will+-- never revert 'output's, even if 'Polysemy.Error.runError' is used+-- after 'outputToIOMonoid'.+--+-- @since 1.2.0.0+outputToIOMonoid+  :: forall o m r a+   . (Monoid m, Member (Embed IO) r)+  => (o -> m)+  -> Sem (Output o ': r) a+  -> Sem r (m, a)+outputToIOMonoid f sem = do+  ref <- embed $ newIORef mempty+  res <- runOutputMonoidIORef ref f sem+  end <- embed $ readIORef ref+  return (end, res)++------------------------------------------------------------------------------+-- | Like 'outputToIOMonoid', but right-associates uses of '<>'.+--+-- This asymptotically improves performance if the time complexity of '<>' for+-- the 'Monoid' depends only on the size of the first argument.+--+-- You should always use this instead of 'outputToIOMonoid' if the monoid+-- is a list, such as 'String'.+--+-- /Beware/: As this uses an 'IORef' internally,+-- all other effects will have local+-- state semantics in regards to 'Output' effects+-- interpreted this way.+-- For example, 'Polysemy.Error.throw' and 'Polysemy.Error.catch' will+-- never revert 'output's, even if 'Polysemy.Error.runError' is used+-- after 'outputToIOMonoidAssocR'.+--+-- @since 1.2.0.0+outputToIOMonoidAssocR+  :: forall o m r a+   . (Monoid m, Member (Embed IO) r)+  => (o -> m)+  -> Sem (Output o ': r) a+  -> Sem r (m, a)+outputToIOMonoidAssocR f =+    (fmap . first) (`appEndo` mempty)+  . outputToIOMonoid (\o -> let !o' = f o in Endo (o' <>))  ------------------------------------------------------------------------------ -- | Run an 'Output' effect by ignoring it.
src/Polysemy/Resource.hs view
@@ -12,12 +12,14 @@      -- * Interpretations   , runResource-  , lowerResource+  , resourceToIOFinal   , resourceToIO+  , lowerResource   ) where  import qualified Control.Exception as X import           Polysemy+import           Polysemy.Final   ------------------------------------------------------------------------------@@ -72,9 +74,44 @@     -> Sem r a onException act end = bracketOnError (pure ()) (const end) (const act) +------------------------------------------------------------------------------+-- | Run a 'Resource' effect in terms of 'X.bracket' through final 'IO'+--+-- /Beware/: Effects that aren't interpreted in terms of 'IO'+-- will have local state semantics in regards to 'Resource' effects+-- interpreted this way. See 'Final'.+--+-- Notably, unlike 'resourceToIO', this is not consistent with+-- 'Polysemy.State.State' unless 'Polysemy.State.runStateInIORef' is used.+-- State that seems like it should be threaded globally throughout 'bracket's+-- /will not be./+--+-- Use 'resourceToIO' instead if you need to run+-- pure, stateful interpreters after the interpreter for 'Resource'.+-- (Pure interpreters are interpreters that aren't expressed in terms of+-- another effect or monad; for example, 'Polysemy.State.runState'.)+--+-- @since 1.2.0.0+resourceToIOFinal :: Member (Final IO) r+                  => Sem (Resource ': r) a+                  -> Sem r a+resourceToIOFinal = interpretFinal $ \case+  Bracket alloc dealloc use -> do+    a <- runS  alloc+    d <- bindS dealloc+    u <- bindS use+    pure $ X.bracket a d u +  BracketOnError alloc dealloc use -> do+    a <- runS  alloc+    d <- bindS dealloc+    u <- bindS use+    pure $ X.bracketOnError a d u+{-# INLINE resourceToIOFinal #-}++ --------------------------------------------------------------------------------- | Run a 'Resource' effect via in terms of 'X.bracket'.+-- | Run a 'Resource' effect in terms of 'X.bracket'. -- -- @since 1.0.0.0 lowerResource@@ -106,6 +143,7 @@      embed $ X.bracketOnError (run_it a) (run_it . d) (run_it . u) {-# INLINE lowerResource #-}+{-# DEPRECATED lowerResource "Use 'resourceToIOFinal' instead" #-}   ------------------------------------------------------------------------------@@ -148,7 +186,7 @@   --------------------------------------------------------------------------------- | A more flexible --- though less safe ---  version of 'lowerResource'.+-- | A more flexible --- though less safe ---  version of 'resourceToIOFinal' -- -- This function is capable of running 'Resource' effects anywhere within an -- effect stack, without relying on an explicit function to lower it into 'IO'.@@ -159,7 +197,7 @@ -- by effects _already handled_ in your effect stack, or in 'IO' code run -- directly inside of 'bracket'. It is not safe against exceptions thrown -- explicitly at the main thread. If this is not safe enough for your use-case,--- use 'lowerResource' instead.+-- use 'resourceToIOFinal' instead. -- -- This function creates a thread, and so should be compiled with @-threaded@. --
src/Polysemy/State.hs view
@@ -17,6 +17,7 @@   , runLazyState   , evalLazyState   , runStateIORef+  , stateToIO      -- * Interoperation with MTL   , hoistStateIntoStateT@@ -122,6 +123,39 @@   Put s -> embed $ writeIORef ref s {-# INLINE runStateIORef #-} +--------------------------------------------------------------------+-- | Run an 'State' effect in terms of operations+-- in 'IO'.+--+-- Internally, this simply creates a new 'IORef', passes it to+-- 'runStateIORef', and then returns the result and the final value+-- of the 'IORef'.+--+-- /Note/: This is not safe in a concurrent setting, as 'modify' isn't atomic.+-- If you need operations over the state to be atomic,+-- use 'Polysemy.AtomicState.atomicStateToIO' instead.+--+-- /Beware/: As this uses an 'IORef' internally,+-- all other effects will have local+-- state semantics in regards to 'State' effects+-- interpreted this way.+-- For example, 'Polysemy.Error.throw' and 'Polysemy.Error.catch' will+-- never revert 'put's, even if 'Polysemy.Error.runError' is used+-- after 'stateToIO'.+--+-- @since 1.2.0.0+stateToIO+    :: forall s r a+     . Member (Embed IO) r+    => s+    -> Sem (State s ': r) a+    -> Sem r (s, a)+stateToIO s sem = do+  ref <- embed $ newIORef s+  res <- runStateIORef ref sem+  end <- embed $ readIORef ref+  return (end, res)+{-# INLINE stateToIO #-}  ------------------------------------------------------------------------------ -- | Hoist a 'State' effect into a 'S.StateT' monad transformer. This can be
src/Polysemy/Writer.hs view
@@ -1,5 +1,4 @@-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TupleSections   #-}+{-# LANGUAGE TupleSections #-}  module Polysemy.Writer   ( -- * Effect@@ -14,26 +13,27 @@     -- * Interpretations   , runWriter   , runWriterAssocR+  , runWriterTVar+  , writerToIOFinal+  , writerToIOAssocRFinal+  , writerToEndoWriter      -- * Interpretations for Other Effects   , outputToWriter   ) where +import Control.Concurrent.STM+ import Data.Bifunctor (first)+import Data.Semigroup  import Polysemy import Polysemy.Output import Polysemy.State +import Polysemy.Internal.Writer ---------------------------------------------------------------------------------- | An effect capable of emitting and intercepting messages.-data Writer o m a where-  Tell   :: o -> Writer o m ()-  Listen :: ∀ o m a. m a -> Writer o m (o, a)-  Pass   :: m (o -> o, a) -> Writer o m a -makeSem ''Writer  ------------------------------------------------------------------------------ -- | @since 0.7.0.0@@ -89,36 +89,77 @@ -- -- You should always use this instead of 'runWriter' if the monoid -- is a list, such as 'String'.+--+-- @since 1.1.0.0 runWriterAssocR     :: Monoid o     => Sem (Writer o ': r) a     -> Sem r (o, a) runWriterAssocR =-  let-    go :: forall o r a-        . Monoid o-       => Sem (Writer o ': r) a-       -> Sem r (o -> o, a)-    go =-        runState id-      . reinterpretH-      (\case-          Tell o -> do-            modify' @(o -> o) (. (o <>)) >>= pureT-          Listen m -> do-            mm <- runT m-            -- TODO(sandy): this is stupid-            (oo, fa) <- raise $ go mm-            modify' @(o -> o) (. oo)-            pure $ fmap (oo mempty, ) fa-          Pass m -> do-            mm <- runT m-            (o, t) <- raise $ runWriterAssocR mm-            ins <- getInspectorT-            let f = maybe id fst (inspect ins t)-            modify' @(o -> o) (. (f o <>))-            pure (fmap snd t)-      )-    {-# INLINE go #-}-  in fmap (first ($ mempty)) . go+    (fmap . first) (`appEndo` mempty)+  . runWriter+  . writerToEndoWriter+  . raiseUnder {-# INLINE runWriterAssocR #-}++--------------------------------------------------------------------+-- | Transform a 'Writer' effect into atomic operations+-- over a 'TVar' through final 'IO'.+--+-- @since 1.2.0.0+runWriterTVar :: (Monoid o, Member (Final IO) r)+              => TVar o+              -> Sem (Writer o ': r) a+              -> Sem r a+runWriterTVar tvar = runWriterSTMAction $ \o -> do+  s <- readTVar tvar+  writeTVar tvar $! s <> o+{-# INLINE runWriterTVar #-}+++--------------------------------------------------------------------+-- | Run a 'Writer' effect by transforming it into atomic operations+-- through final 'IO'.+--+-- Internally, this simply creates a new 'TVar', passes it to+-- 'runWriterTVar', and then returns the result and the final value+-- of the 'TVar'.+--+-- /Beware/: Effects that aren't interpreted in terms of 'IO'+-- will have local state semantics in regards to 'Writer' effects+-- interpreted this way. See 'Final'.+--+-- @since 1.2.0.0+writerToIOFinal :: (Monoid o, Member (Final IO) r)+                => Sem (Writer o ': r) a+                -> Sem r (o, a)+writerToIOFinal sem = do+  tvar <- embedFinal $ newTVarIO mempty+  res  <- runWriterTVar tvar sem+  end  <- embedFinal $ readTVarIO tvar+  return (end, res)+{-# INLINE writerToIOFinal #-}++--------------------------------------------------------------------+-- | Like 'writerToIOFinal'. but right-associates uses of '<>'.+--+-- This asymptotically improves performance if the time complexity of '<>'+-- for the 'Monoid' depends only on the size of the first argument.+--+-- You should always use this instead of 'writerToIOFinal' if the monoid+-- is a list, such as 'String'.+--+-- /Beware/: Effects that aren't interpreted in terms of 'IO'+-- will have local state semantics in regards to 'Writer' effects+-- interpreted this way. See 'Final'.+--+-- @since 1.2.0.0+writerToIOAssocRFinal :: (Monoid o, Member (Final IO) r)+                      => Sem (Writer o ': r) a+                      -> Sem r (o, a)+writerToIOAssocRFinal =+    (fmap . first) (`appEndo` mempty)+  . writerToIOFinal+  . writerToEndoWriter+  . raiseUnder+{-# INLINE writerToIOAssocRFinal #-}
test/AsyncSpec.hs view
@@ -2,7 +2,6 @@  module AsyncSpec where -import Control.Concurrent import Control.Concurrent.MVar import Control.Monad import Polysemy
+ test/FinalSpec.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE RecursiveDo #-}+module FinalSpec where++import Test.Hspec++import Data.Either+import Data.IORef++import Polysemy+import Polysemy.Async+import Polysemy.Error+import Polysemy.Fixpoint+import Polysemy.Trace+import Polysemy.State+++data Node a = Node a (IORef (Node a))++mkNode :: (Member (Embed IO) r, Member Fixpoint r)+       => a+       -> Sem r (Node a)+mkNode a = mdo+  let nd = Node a p+  p <- embed $ newIORef nd+  return nd++linkNode :: Member (Embed IO) r+         => Node a+         -> Node a+         -> Sem r ()+linkNode (Node _ r) b =+  embed $ writeIORef r b++readNode :: Node a -> a+readNode (Node a _) = a++follow :: Member (Embed IO) r+       => Node a+       -> Sem r (Node a)+follow (Node _ ref) = embed $ readIORef ref++test1 :: IO (Either Int (String, Int, Maybe Int))+test1 = do+  ref <- newIORef "abra"+  runFinal+    . embedToFinal @IO+    . runStateIORef ref -- Order of these interpreters don't matter+    . errorToIOFinal+    . fixpointToFinal @IO+    . asyncToIOFinal+     $ do+     n1 <- mkNode 1+     n2 <- mkNode 2+     linkNode n2 n1+     aw <- async $ do+       linkNode n1 n2+       modify (++"hadabra")+       n2' <- follow n2+       throw (readNode n2')+     m <- await aw `catch` (\s -> return $ Just s)+     n1' <- follow n1+     s <- get+     return (s, readNode n1', m)++test2 :: IO ([String], Either () ())+test2 =+    runFinal+  . runTraceList+  . errorToIOFinal+  . asyncToIOFinal+  $ do+  fut <- async $ do+    trace "Global state semantics?"+  catch @() (trace "What's that?" *> throw ()) (\_ -> return ())+  _ <- await fut+  trace "Nothing at all."++++spec :: Spec+spec = do+  describe "Final on IO" $ do+    it "should terminate successfully, with no exceptions,\+        \ and have global state semantics on State." $ do+      res1 <- test1+      res1 `shouldSatisfy` isRight+      case res1 of+        Right (s, i, j) -> do+          i `shouldBe` 2+          j `shouldBe` Just 1+          s `shouldBe` "abrahadabra"+        _ -> pure ()++    it "should treat trace with local state semantics" $ do+      res2 <- test2+      res2 `shouldBe` (["Nothing at all."], Right ())
test/FixpointSpec.hs view
@@ -2,7 +2,8 @@ {-# LANGUAGE RecursiveDo #-} module FixpointSpec where -import Control.Exception (try, evaluate)+import Data.Functor.Identity+import Control.Exception (evaluate) import Control.Monad.Fix  import Polysemy@@ -29,8 +30,9 @@  test1 :: (String, (Int, ())) test1 =-    run-  . runFixpoint run+    runIdentity+  . runFinal+  . fixpointToFinal @Identity   . runOutputMonoid (show @Int)   . runFinalState 1   $ do@@ -42,8 +44,9 @@  test2 :: Either [Int] [Int] test2 =-    run-  . runFixpoint run+    runIdentity+  . runFinal+  . fixpointToFinal @Identity   . runError   $ mdo   a <- throw (2 : a) `catch` (\e -> return (1 : e))@@ -51,8 +54,9 @@  test3 :: Either () (Int, Int) test3 =-    run-  . runFixpoint run+    runIdentity+  . runFinal+  . fixpointToFinal @Identity   . runError   . runLazyState @Int 1   $ mdo@@ -62,8 +66,9 @@  test4 :: (Int, Either () Int) test4 =-    run-  . runFixpoint run+    runIdentity+  . runFinal+  . fixpointToFinal @Identity   . runLazyState @Int 1   . runError   $ mdo@@ -73,7 +78,7 @@   spec :: Spec-spec = parallel $ describe "runFixpoint" $ do+spec = parallel $ describe "fixpointToFinal on Identity" $ do   it "should work with runState" $ do     test1 `shouldBe` ("12",  (2, ()))   it "should work with runError" $ do@@ -88,10 +93,10 @@  bombMessage :: String bombMessage =-  "runFixpoint: Internal computation failed.\-              \ This is likely because you have tried to recursively use\-              \ the result of a failed computation in an action\-              \ whose effect may be observed even though the computation failed.\-              \ It's also possible that you're using an interpreter\-              \ that uses 'weave' improperly.\-              \ See documentation for more information."+  "fixpointToFinal: Internal computation failed.\+                  \ This is likely because you have tried to recursively use\+                  \ the result of a failed computation in an action\+                  \ whose effect may be observed even though the computation failed.\+                  \ It's also possible that you're using an interpreter\+                  \ that uses 'weave' improperly.\+                  \ See documentation for more information."
test/OutputSpec.hs view
@@ -9,6 +9,7 @@ import Polysemy import Polysemy.Async import Polysemy.Output+import Polysemy.Final  import Test.Hspec @@ -47,8 +48,11 @@     it "should commit writes of asynced computations" $       let io = do             ref <- newIORef ""-            (runM .@ lowerAsync) . runOutputMonoidIORef ref (show @Int) $-              test1+            runFinal+              . embedToFinal @IO+              . asyncToIOFinal+              . runOutputMonoidIORef ref (show @Int)+              $ test1             readIORef ref       in do         res <- io@@ -58,8 +62,11 @@     it "should commit writes of asynced computations" $       let io = do             ref <- newTVarIO ""-            (runM .@ lowerAsync) . runOutputMonoidTVar ref (show @Int) $-              test1+            runFinal+              . embedToFinal @IO+              . asyncToIOFinal+              . runOutputMonoidTVar ref (show @Int)+              $ test1             readTVarIO ref       in do         res <- io
test/WriterSpec.hs view
@@ -8,8 +8,6 @@ import Control.Concurrent.STM import Control.Exception (evaluate) -import Data.IORef- import Polysemy import Polysemy.Async import Polysemy.Error@@ -55,6 +53,56 @@ test3 :: (String, (String, ())) test3 = run . runWriter $ listen (tell "and hear") +test4 :: IO (String, String)+test4 = do+  tvar <- newTVarIO ""+  (listened, _) <- runFinal . asyncToIOFinal . runWriterTVar tvar $ do+    tell "message "+    listen $ do+      tell "has been"+      a <- async $ tell " received"+      await a+  end <- readTVarIO tvar+  return (end, listened)++test5 :: IO (String, String)+test5 = do+  tvar <- newTVarIO ""+  lock <- newEmptyMVar+  (listened, a) <- runFinal . asyncToIOFinal . runWriterTVar tvar $ do+    tell "message "+    listen $ do+      tell "has been"+      a <- async $ do+        embedFinal $ takeMVar lock+        tell " received"+      return a+  putMVar lock ()+  _ <- A.wait a+  end <- readTVarIO tvar+  return (end, listened)++test6 :: Sem '[Error (A.Async (Maybe ())), Final IO] String+test6 = do+  tvar <- embedFinal $ newTVarIO ""+  lock <- embedFinal $ newEmptyMVar+  let+      inner = do+        tell "message "+        fmap snd $ listen @String $ do+          tell "has been"+          a <- async $ do+            embedFinal $ takeMVar lock+            tell " received"+          throw a+  asyncToIOFinal (runWriterTVar tvar inner) `catch` \a ->+    embedFinal $ do+      putMVar lock ()+      (_ :: Maybe ()) <- A.wait a+      readTVarIO tvar+++ spec :: Spec spec = do   describe "writer" $ do@@ -87,3 +135,24 @@         evaluate (run t2) `shouldThrow` errorCall "strict"         runM t3           `shouldThrow` errorCall "strict"         evaluate (run t3) `shouldThrow` errorCall "strict"++  describe "runWriterTVar" $ do+    it "should listen and commit asyncs spawned and awaited upon in a listen \+       \block" $ do+      (end, listened) <- test4+      end `shouldBe` "message has been received"+      listened `shouldBe` "has been received"++    it "should commit writes of asyncs spawned inside a listen block even if \+       \the block has finished." $ do+      (end, listened) <- test5+      end `shouldBe` "message has been received"+      listened `shouldBe` "has been"+++    it "should commit writes of asyncs spawned inside a listen block even if \+       \the block failed for any reason." $ do+      Right end1 <- runFinal . errorToIOFinal $ test6+      Right end2 <- runFinal . runError $ test6+      end1 `shouldBe` "message has been received"+      end2 `shouldBe` "message has been received"