packages feed

cleff 0.2.0.0 → 0.2.1.0

raw patch · 11 files changed

+261/−48 lines, 11 filesdep −transformersPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies removed: transformers

API changes (from Hackage documentation)

- Cleff.Internal.Monad: instance Data.Typeable.Internal.Typeable e => GHC.Show.Show (Cleff.Internal.Monad.InternalHandler e)
- Cleff.Internal.Monad: instance GHC.Base.Monoid a => GHC.Base.Monoid (Cleff.Internal.Monad.Eff es a)
- Cleff.Internal.Monad: instance GHC.Base.Semigroup a => GHC.Base.Semigroup (Cleff.Internal.Monad.Eff es a)
- Cleff.Internal.Monad: instance GHC.Classes.Eq (Cleff.Internal.Monad.HandlerPtr e)
- Cleff.Internal.Monad: instance GHC.Classes.Ord (Cleff.Internal.Monad.HandlerPtr e)
+ Cleff.Fresh: runFreshAtomicCounter :: Eff (Fresh Int : es) ~> Eff es
+ Cleff.Input: bindInput :: Input i' :> es => (i' -> Eff es i) -> Eff (Input i : es) ~> Eff es
+ Cleff.Input: inputToReader :: Eff (Input i : es) ~> Eff (Reader i : es)
+ Cleff.Input: mapInput :: Input i' :> es => (i' -> i) -> Eff (Input i : es) ~> Eff es
+ Cleff.Internal.Instances: instance Control.Monad.Zip.MonadZip (Cleff.Internal.Monad.Eff es)
+ Cleff.Internal.Instances: instance Data.String.IsString a => Data.String.IsString (Cleff.Internal.Monad.Eff es a)
+ Cleff.Internal.Instances: instance GHC.Base.Monoid a => GHC.Base.Monoid (Cleff.Internal.Monad.Eff es a)
+ Cleff.Internal.Instances: instance GHC.Base.Semigroup a => GHC.Base.Semigroup (Cleff.Internal.Monad.Eff es a)
+ Cleff.Internal.Instances: instance GHC.Enum.Bounded a => GHC.Enum.Bounded (Cleff.Internal.Monad.Eff es a)
+ Cleff.Internal.Instances: instance GHC.Float.Floating a => GHC.Float.Floating (Cleff.Internal.Monad.Eff es a)
+ Cleff.Internal.Instances: instance GHC.Num.Num a => GHC.Num.Num (Cleff.Internal.Monad.Eff es a)
+ Cleff.Internal.Instances: instance GHC.Real.Fractional a => GHC.Real.Fractional (Cleff.Internal.Monad.Eff es a)
+ Cleff.Output: bindOutput :: Output o' :> es => (o -> Eff es o') -> Eff (Output o : es) ~> Eff es
+ Cleff.Output: mapOutput :: Output o' :> es => (o -> o') -> Eff (Output o : es) ~> Eff es
+ Cleff.State: runStateIORef :: IOE :> es => IORef s -> Eff (State s : es) a -> Eff es a
+ Cleff.State: runStateMVar :: IOE :> es => MVar s -> Eff (State s : es) a -> Eff es a
+ Cleff.State: runStateTVar :: IOE :> es => TVar s -> Eff (State s : es) a -> Eff es a
- Cleff.Error: catchError :: Error e_afrW :> es_afwU => Eff es_afwU a_afrV -> (e_afrW -> Eff es_afwU a_afrV) -> Eff es_afwU a_afrV
+ Cleff.Error: catchError :: Error e_afOB :> es_afTz => Eff es_afTz a_afOA -> (e_afOB -> Eff es_afTz a_afOA) -> Eff es_afTz a_afOA
- Cleff.Error: throwError :: Error e_afrR :> es_afwX => e_afrR -> Eff es_afwX a_afrT
+ Cleff.Error: throwError :: Error e_afOw :> es_afTC => e_afOw -> Eff es_afTC a_afOy
- Cleff.Fresh: fresh :: Fresh u_ajnI :> es_ajor => Eff es_ajor u_ajnI
+ Cleff.Fresh: fresh :: Fresh u_akl5 :> es_aklO => Eff es_aklO u_akl5
- Cleff.Input: input :: Input i_aj9F :> es_ajao => Eff es_ajao i_aj9F
+ Cleff.Input: input :: Input i_ajZM :> es_ak0v => Eff es_ak0v i_ajZM
- Cleff.Mask: bracket :: Mask :> es_ahol => Eff es_ahol a_Xhlm -> (a_Xhlm -> Eff es_ahol c_Xhlo) -> (a_Xhlm -> Eff es_ahol b_ahln) -> Eff es_ahol b_ahln
+ Cleff.Mask: bracket :: Mask :> es_ahLc => Eff es_ahLc a_XhId -> (a_XhId -> Eff es_ahLc c_XhIf) -> (a_XhId -> Eff es_ahLc b_ahIe) -> Eff es_ahLc b_ahIe
- Cleff.Mask: bracketOnError :: Mask :> es_ahoh => Eff es_ahoh a_Xhlq -> (a_Xhlq -> Eff es_ahoh c_Xhls) -> (a_Xhlq -> Eff es_ahoh b_ahlr) -> Eff es_ahoh b_ahlr
+ Cleff.Mask: bracketOnError :: Mask :> es_ahL8 => Eff es_ahL8 a_XhIh -> (a_XhIh -> Eff es_ahL8 c_XhIj) -> (a_XhIh -> Eff es_ahL8 b_ahIi) -> Eff es_ahL8 b_ahIi
- Cleff.Mask: mask :: Mask :> es_ahor => ((~>) (Eff es_ahor) (Eff es_ahor) -> Eff es_ahor a_ahlh) -> Eff es_ahor a_ahlh
+ Cleff.Mask: mask :: Mask :> es_ahLi => ((~>) (Eff es_ahLi) (Eff es_ahLi) -> Eff es_ahLi a_ahI8) -> Eff es_ahLi a_ahI8
- Cleff.Mask: uninterruptibleMask :: Mask :> es_ahop => ((~>) (Eff es_ahop) (Eff es_ahop) -> Eff es_ahop a_ahlj) -> Eff es_ahop a_ahlj
+ Cleff.Mask: uninterruptibleMask :: Mask :> es_ahLg => ((~>) (Eff es_ahLg) (Eff es_ahLg) -> Eff es_ahLg a_ahIa) -> Eff es_ahLg a_ahIa
- Cleff.Output: output :: Output o_akhC :> es_akio => o_akhC -> Eff es_akio ()
+ Cleff.Output: output :: Output o_alnz :> es_alol => o_alnz -> Eff es_alol ()
- Cleff.Reader: ask :: Reader r_ai0V :> es_ai2y => Eff es_ai2y r_ai0V
+ Cleff.Reader: ask :: Reader r_ainM :> es_aipp => Eff es_aipp r_ainM
- Cleff.Reader: local :: Reader r_ai0X :> es_ai2v => (r_ai0X -> r_ai0X) -> Eff es_ai2v a_ai0Z -> Eff es_ai2v a_ai0Z
+ Cleff.Reader: local :: Reader r_ainO :> es_aipm => (r_ainO -> r_ainO) -> Eff es_aipm a_ainQ -> Eff es_aipm a_ainQ
- Cleff.State: get :: State s_aixs :> es_aizE => Eff es_aizE s_aixs
+ Cleff.State: get :: State s_aiUI :> es_aiWU => Eff es_aiWU s_aiUI
- Cleff.State: put :: State s_aixu :> es_aizC => s_aixu -> Eff es_aizC ()
+ Cleff.State: put :: State s_aiUK :> es_aiWS => s_aiUK -> Eff es_aiWS ()
- Cleff.State: state :: State s_aixw :> es_aizA => (s_aixw -> (a_aixx, s_aixw)) -> Eff es_aizA a_aixx
+ Cleff.State: state :: State s_aiUM :> es_aiWQ => (s_aiUM -> (a_aiUN, s_aiUM)) -> Eff es_aiWQ a_aiUN
- Cleff.Trace: trace :: Trace :> es_akyz => String -> Eff es_akyz ()
+ Cleff.Trace: trace :: Trace :> es_alKz => String -> Eff es_alKz ()
- Cleff.Writer: listen :: Writer w_ajxB :> es_ajz2 => Eff es_ajz2 a_XjxB -> Eff es_ajz2 (a_XjxB, w_ajxB)
+ Cleff.Writer: listen :: Writer w_akDy :> es_akEZ => Eff es_akEZ a_XkDy -> Eff es_akEZ (a_XkDy, w_akDy)
- Cleff.Writer: tell :: Writer w_ajxx :> es_ajz4 => w_ajxx -> Eff es_ajz4 ()
+ Cleff.Writer: tell :: Writer w_akDu :> es_akF1 => w_akDu -> Eff es_akF1 ()

Files

CHANGELOG.md view
@@ -1,5 +1,16 @@ # Changelog for `cleff` +## 0.2.1.0 (2022-02-13)++### Added++- Lifted convenience instances of `Bounded`, `Num`, `Fractional`, `Floating` and `IsString` for `Eff`+- `MonadZip` instance from the `MonadComprehensions` extension for `Eff`+- `freshEnumToState` and `runFreshAtomicCounter` for `Fresh`+- `inputToReader`, `mapInput` and `bindInput` for `Input`+- `mapOutput` and `bindOutput` for `Output`+- `runStateIORef`, `runStateMVar` and `runStateTVar` for `State`+ ## 0.2.0.0 (2022-02-06)  ### Changed
cleff.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           cleff-version:        0.2.0.0+version:        0.2.1.0 synopsis:       Fast and concise extensible effects description:    Please see the README on GitHub at <https://github.com/re-xyr/cleff#readme> category:       Control, Effect, Language@@ -46,6 +46,7 @@       Cleff.Internal.Any       Cleff.Internal.Base       Cleff.Internal.Effect+      Cleff.Internal.Instances       Cleff.Internal.Interpret       Cleff.Internal.Monad       Cleff.Internal.TH@@ -100,7 +101,6 @@     , rec-smallarray ==0.1.*     , template-haskell >=2.14 && <3     , th-abstraction >=0.2.11 && <0.5-    , transformers >=0.5 && <0.7     , transformers-base >=0.4.5 && <0.5     , unliftio >=0.2.8 && <0.3   if flag(dynamic-ioe)@@ -161,7 +161,6 @@     , rec-smallarray ==0.1.*     , template-haskell >=2.14 && <3     , th-abstraction >=0.2.11 && <0.5-    , transformers >=0.5 && <0.7     , transformers-base >=0.4.5 && <0.5     , unliftio >=0.2.8 && <0.3   if flag(dynamic-ioe)@@ -230,7 +229,6 @@     , rec-smallarray ==0.1.*     , template-haskell >=2.14 && <3     , th-abstraction >=0.2.11 && <0.5-    , transformers >=0.5 && <0.7     , transformers-base >=0.4.5 && <0.5     , unliftio >=0.2.8 && <0.3   if flag(dynamic-ioe)
src/Cleff.hs view
@@ -57,6 +57,7 @@  import           Cleff.Internal.Base import           Cleff.Internal.Effect+import           Cleff.Internal.Instances () import           Cleff.Internal.Interpret import           Cleff.Internal.Monad import           Cleff.Internal.TH
src/Cleff/Fresh.hs view
@@ -11,12 +11,14 @@   , -- * Operations     fresh   , -- * Interpretations-    freshIntToState, runFreshUnique+    freshIntToState, runFreshAtomicCounter, runFreshUnique   ) where  import           Cleff+import           Cleff.Internal.Base  (thisIsPureTrustMe) import           Cleff.State-import           Data.Unique (Unique, newUnique)+import           Data.Atomics.Counter (incrCounter, newCounter)+import           Data.Unique          (Unique, newUnique)  -- * Effect @@ -30,13 +32,34 @@  -- * Interpretations --- | Interpret a @'Fresh' 'Int'@ effect in terms of @'State' 'Int'@.+-- | Interpret a @'Fresh' a@ in terms of @'State' a@ for any 'Enum'. Every time 'succ' is called to generate the next+-- value.+--+-- @since 0.2.1.0+freshEnumToState :: Enum a => Eff (Fresh a ': es) ~> Eff (State a ': es)+freshEnumToState = reinterpret \case+  Fresh -> state \s -> (s, succ s)+{-# INLINE freshEnumToState #-}++-- | Interpret a @'Fresh' 'Int'@ effect in terms of @'State' 'Int'@. This is a specialized version of+-- 'freshEnumToState'. freshIntToState :: Eff (Fresh Int ': es) ~> Eff (State Int ': es)-freshIntToState = reinterpret \case-  Fresh -> state \s -> (s, s + 1)+freshIntToState = freshEnumToState {-# INLINE freshIntToState #-} --- | Interpret a @'Fresh' 'Unique'@ effect in terms of IO actions.+-- | Interpret a @'Fresh' 'Int'@ effect in terms of a 'Data.Atomics.Counter.AtomicCounter'. This is usually faster+-- than 'runFreshUnique'.+--+-- @since 0.2.1.0+runFreshAtomicCounter :: Eff (Fresh Int ': es) ~> Eff es+runFreshAtomicCounter m = thisIsPureTrustMe do+  counter <- liftIO $ newCounter minBound+  reinterpret (\case+    Fresh -> liftIO $ incrCounter 1 counter) m+{-# INLINE runFreshAtomicCounter #-}++-- | Interpret a @'Fresh' 'Unique'@ effect in terms of IO actions. This is slower than 'runFreshAtomicCounter', but it+-- won't overflow on @'maxBound' :: 'Int'@. runFreshUnique :: IOE :> es => Eff (Fresh Unique ': es) ~> Eff es runFreshUnique = interpret \case   Fresh -> liftIO newUnique
src/Cleff/Input.hs view
@@ -11,10 +11,11 @@   , -- * Operations     input, inputs   , -- * Interpretations-    runInputConst, inputToListState, runInputEff+    runInputConst, inputToListState, inputToReader, runInputEff, mapInput, bindInput   ) where  import           Cleff+import           Cleff.Reader import           Cleff.State  -- * Effect@@ -42,13 +43,37 @@ -- | Run an 'Input' effect by going through a list of values. inputToListState :: Eff (Input (Maybe i) ': es) ~> Eff (State [i] ': es) inputToListState = reinterpret \case-  Input -> get >>= \case-    []      -> pure Nothing-    x : xs' -> Just x <$ put xs'+  Input -> state \case+    []     -> (Nothing, [])+    x : xs -> (Just x, xs) {-# INLINE inputToListState #-} +-- | Run an 'Input' in terms of a 'Reader'.+--+-- @since 0.2.1.0+inputToReader :: Eff (Input i ': es) ~> Eff (Reader i ': es)+inputToReader = reinterpret \case+  Input -> ask+{-# INLINE inputToReader #-}+ -- | Run an 'Input' effect by performing a computation for each input request. runInputEff :: Eff es i -> Eff (Input i ': es) ~> Eff es runInputEff m = interpret \case   Input -> m {-# INLINE runInputEff #-}++-- | Transform an 'Input' effect into another one already in the effect stack, by a pure function.+--+-- @since 0.2.1.0+mapInput :: Input i' :> es => (i' -> i) -> Eff (Input i ': es) ~> Eff es+mapInput f = interpret \case+  Input -> f <$> input+{-# INLINE mapInput #-}++-- | Transform an 'Input' effect into another one already in the effect stack, by an effectful computation.+--+-- @since 0.2.1.0+bindInput :: Input i' :> es => (i' -> Eff es i) -> Eff (Input i ': es) ~> Eff es+bindInput f = interpret \case+  Input -> f =<< input+{-# INLINE bindInput #-}
src/Cleff/Internal/Base.hs view
@@ -143,20 +143,15 @@ #endif {-# INLINE thisIsPureTrustMe #-} --- | Extract the 'IO' computation out of an 'Eff' given no effect remains on the stack.-runEff :: Eff '[] a -> IO a-runEff m = unEff m emptyEnv-{-# INLINE runEff #-}- -- | Unwrap an 'Eff' computation with side effects into an 'IO' computation, given that all effects other than 'IOE' are -- interpreted. runIOE :: Eff '[IOE] ~> IO-runIOE = runEff . thisIsPureTrustMe+runIOE m = unEff (thisIsPureTrustMe m) emptyEnv {-# INLINE runIOE #-}  -- | Unwrap a pure 'Eff' computation into a pure value, given that all effects are interpreted. runPure :: Eff '[] a -> a-runPure = unsafeDupablePerformIO . runEff+runPure m = unsafeDupablePerformIO $ unEff m emptyEnv {-# NOINLINE runPure #-}  -- * Effect interpretation
+ src/Cleff/Internal/Instances.hs view
@@ -0,0 +1,80 @@+{-# OPTIONS_GHC -Wno-orphans #-}+-- |+-- Copyright: (c) 2021 Xy Ren+-- License: BSD3+-- Maintainer: xy.r@outlook.com+-- Stability: unstable+-- Portability: non-portable (GHC only)+--+-- This module contains lifted instances of some typeclasses for 'Eff' for convenience. They are all exported in the+-- "Cleff" module so you shouldn't need to import this module.+--+-- __This is an /internal/ module and its API may change even between minor versions.__ Therefore you should be+-- extra careful if you're to depend on this module.+module Cleff.Internal.Instances () where++import           Cleff.Internal.Monad (Eff)+import           Control.Applicative  (Applicative (liftA2))+import           Control.Monad.Zip    (MonadZip (munzip, mzipWith))+import           Data.String          (IsString (fromString))++-- | @since 0.2.1.0+instance Bounded a => Bounded (Eff es a) where+  minBound = pure minBound+  maxBound = pure maxBound++-- | @since 0.2.1.0+instance Num a => Num (Eff es a) where+  (+) = liftA2 (+)+  (-) = liftA2 (-)+  (*) = liftA2 (*)+  negate = fmap negate+  abs = fmap abs+  signum = fmap signum+  fromInteger = pure . fromInteger++-- | @since 0.2.1.0+instance Fractional a => Fractional (Eff es a) where+  (/) = liftA2 (/)+  recip = fmap recip+  fromRational = pure . fromRational++-- | @since 0.2.1.0+instance Floating a => Floating (Eff es a) where+  pi = pure pi+  exp = fmap exp+  log = fmap log+  sqrt = fmap sqrt+  (**) = liftA2 (**)+  logBase = liftA2 logBase+  sin = fmap sin+  cos = fmap cos+  tan = fmap tan+  asin = fmap asin+  acos = fmap acos+  atan = fmap atan+  sinh = fmap sinh+  cosh = fmap cosh+  tanh = fmap tanh+  asinh = fmap asinh+  acosh = fmap acosh+  atanh = fmap atanh++-- | @since 0.2.1.0+instance Semigroup a => Semigroup (Eff es a) where+  (<>) = liftA2 (<>)++-- | @since 0.2.1.0+instance Monoid a => Monoid (Eff es a) where+  mempty = pure mempty++-- | @since 0.2.1.0+instance IsString a => IsString (Eff es a) where+  fromString = pure . fromString++-- | Compatibility instance for @MonadComprehensions@.+--+-- @since 0.2.1.0+instance MonadZip (Eff es) where+  mzipWith = liftA2+  munzip x = (fst <$> x, snd <$> x)
src/Cleff/Internal/Interpret.hs view
@@ -69,21 +69,29 @@ -- However, note that this function is suited for transforming an existing interpreter into a reinterpreter; if you -- want to define a reinterpreter from scratch, you should still prefer 'reinterpret', which is both easier to use and -- more efficient.+--+-- @since 0.2.0.0 raiseUnder :: ∀ e' e es. Eff (e ': es) ~> Eff (e ': e' ': es) raiseUnder = raiseNUnder @'[e']  -- | Like 'raiseUnder', but allows introducing multiple effects. This function requires @TypeApplications@.+--+-- @since 0.2.0.0 raiseNUnder :: ∀ es' e es. KnownList es' => Eff (e ': es) ~> Eff (e ': es' ++ es) raiseNUnder = raiseNUnderN @es' @'[e]  -- | Like 'raiseUnder', but allows introducing the effect under multiple effects. This function requires -- @TypeApplications@.+--+-- @since 0.2.0.0 raiseUnderN :: ∀ e es' es. KnownList es' => Eff (es' ++ es) ~> Eff (es' ++ e ': es) raiseUnderN = raiseNUnderN @'[e] @es' @es  -- | A generalization of both 'raiseUnderN' and 'raiseNUnder', allowing introducing multiple effects under multiple -- effects. This function requires @TypeApplications@ and is subject to serious type ambiguity; you most likely will -- need to supply all three type variables explicitly.+--+-- @since 0.2.0.0 raiseNUnderN :: ∀ es'' es' es. (KnownList es', KnownList es'') => Eff (es' ++ es) ~> Eff (es' ++ (es'' ++ es)) raiseNUnderN = adjust \re -> Rec.concat   (Rec.take @es' @(es'' ++ es) re) (Rec.drop @es'' @es (Rec.drop @es' @(es'' ++ es) re))
src/Cleff/Internal/Monad.hs view
@@ -23,13 +23,12 @@  import           Cleff.Internal.Any import           Cleff.Internal.Effect-import           Control.Monad.Fix          (MonadFix)-import           Control.Monad.Trans.Reader (ReaderT (ReaderT))-import           Data.IntMap.Strict         (IntMap)-import qualified Data.IntMap.Strict         as Map-import           Data.Rec.SmallArray        (KnownList, Rec, Subset, pattern (:~:))-import qualified Data.Rec.SmallArray        as Rec-import           Type.Reflection            (Typeable, typeRep)+import           Control.Applicative   (Applicative (liftA2))+import           Control.Monad.Fix     (MonadFix (mfix))+import           Data.IntMap.Strict    (IntMap)+import qualified Data.IntMap.Strict    as Map+import           Data.Rec.SmallArray   (KnownList, Rec, Subset, pattern (:~:))+import qualified Data.Rec.SmallArray   as Rec  -- * The 'Eff' monad @@ -41,12 +40,6 @@ newtype InternalHandler e = InternalHandler   { runHandler :: ∀ es. e (Eff es) ~> Eff es } --- | @--- 'show' (handler :: 'InternalHandler' E) == "Handler E"--- @-instance Typeable e => Show (InternalHandler e) where-  showsPrec p _ = ("Handler " ++) . showsPrec p (typeRep @e)- -- | The extensible effect monad. A monad @'Eff' es@ is capable of performing any effect in the /effect stack/ @es@, -- which is a type-level list that holds all effects available. However, most of the times, for flexibility, @es@ -- should be a polymorphic type variable, and you should use the '(:>)' and '(:>>)' operators in constraints to@@ -62,9 +55,35 @@ newtype Eff es a = Eff { unEff :: Env es -> IO a }   -- ^ The effect monad receives an effect environment 'Env' that contains all effect handlers and produces an 'IO'   -- action.-  deriving newtype (Semigroup, Monoid)-  deriving (Functor, Applicative, Monad, MonadFix) via (ReaderT (Env es) IO) +instance Functor (Eff es) where+  fmap f (Eff x) = Eff (fmap f . x)+  {-# INLINE fmap #-}+  x <$ Eff y = Eff \es -> x <$ y es+  {-# INLINE (<$) #-}++instance Applicative (Eff es) where+  pure = Eff . const . pure+  {-# INLINE pure #-}+  Eff f <*> Eff x = Eff \es -> f es <*> x es+  {-# INLINE (<*>) #-}+  Eff x <*  Eff y = Eff \es -> x es <*  y es+  {-# INLINE (<*) #-}+  Eff x  *> Eff y = Eff \es -> x es  *> y es+  {-# INLINE (*>) #-}+  liftA2 f (Eff x) (Eff y) = Eff \es -> liftA2 f (x es) (y es)+  {-# INLINE liftA2 #-}++instance Monad (Eff es) where+  Eff x >>= f = Eff \es -> x es >>= \x' -> unEff (f x') es+  {-# INLINE (>>=) #-}+  (>>) = (*>)+  {-# INLINE (>>) #-}++instance MonadFix (Eff es) where+  mfix f = Eff \es -> mfix \x -> unEff (f x) es+  {-# INLINE mfix #-}+ -- * Effect environment  -- | The /effect environment/ that corresponds effects in the stack to their respective 'InternalHandler's. This@@ -81,10 +100,6 @@ -- | A pointer to 'InternalHandler' in an 'Env'. type role HandlerPtr nominal newtype HandlerPtr (e :: Effect) = HandlerPtr { unHandlerPtr :: Int }-  deriving newtype-    ( Eq  -- ^ Pointer equality.-    , Ord -- ^ An arbitrary total order on the pointers.-    )  -- | Create an empty 'Env' with no address allocated. emptyEnv :: Env '[]@@ -140,5 +155,7 @@ -- @ -- 'send' = 'sendVia' 'id' -- @+--+-- @since 0.2.0.0 sendVia :: e :> es' => (Eff es ~> Eff es') -> e (Eff es) ~> Eff es' sendVia f e = Eff \es -> unEff (f (runHandler (readEnv es) e)) es
src/Cleff/Output.hs view
@@ -11,7 +11,7 @@   , -- * Operations     output   , -- * Interpretations-    outputToListState, outputToWriter, ignoreOutput, runOutputEff+    outputToListState, outputToWriter, ignoreOutput, runOutputEff, mapOutput, bindOutput   ) where  import           Cleff@@ -30,7 +30,8 @@  -- * Interpretations --- | Run an 'Output' effect by accumulating a list.+-- | Run an 'Output' effect by accumulating a list. Note that outputs are being prepended to the head of the list, so+-- in many cases you would want to 'reverse' the result. outputToListState :: Eff (Output o ': es) ~> Eff (State [o] ': es) outputToListState = reinterpret \case   Output x -> modify (x :)@@ -53,3 +54,19 @@ runOutputEff m = interpret \case   Output x -> m x {-# INLINE runOutputEff #-}++-- | Transform an 'Output' effect into another one already in the effect stack, by a pure function.+--+-- @since 0.2.1.0+mapOutput :: Output o' :> es => (o -> o') -> Eff (Output o ': es) ~> Eff es+mapOutput f = interpret \case+  Output x -> output $ f x+{-# INLINE mapOutput #-}++-- | Transform an 'Input' effect into another one already in the effect stack, by an effectful computation.+--+-- @since 0.2.1.0+bindOutput :: Output o' :> es => (o -> Eff es o') -> Eff (Output o ': es) ~> Eff es+bindOutput f = interpret \case+  Output x -> output =<< f x+{-# INLINE bindOutput #-}
src/Cleff/State.hs view
@@ -11,15 +11,18 @@   , -- * Operations     get, put, state, gets, modify   , -- * Interpretations-    runState, zoom+    runState, runStateIORef, runStateMVar, runStateTVar, zoom   ) where  import           Cleff import           Cleff.Internal.Base+import           Control.Monad       (void) import           Data.Atomics        (atomicModifyIORefCAS) import           Data.Tuple          (swap) import           Lens.Micro          (Lens', (&), (.~), (^.))-import           UnliftIO.IORef      (newIORef, readIORef, writeIORef)+import           UnliftIO.IORef      (IORef, newIORef, readIORef, writeIORef)+import           UnliftIO.MVar       (MVar, modifyMVar, readMVar, swapMVar)+import           UnliftIO.STM        (TVar, atomically, readTVar, readTVarIO, writeTVar)  -- * Effect @@ -44,6 +47,13 @@  -- * Interpretations +handleIORef :: IOE :> es => IORef s -> Handler (State s) es+handleIORef rs = \case+  Get     -> readIORef rs+  Put s'  -> writeIORef rs s'+  State f -> liftIO $ atomicModifyIORefCAS rs (swap . f)+{-# INLINE handleIORef #-}+ -- | Run the 'State' effect. -- -- __Caveat__: The 'runState' interpreter is implemented with 'Data.IORef.IORef's and there is no way to do arbitrary@@ -58,13 +68,41 @@ runState :: s -> Eff (State s ': es) a -> Eff es (a, s) runState s m = thisIsPureTrustMe do   rs <- newIORef s-  x <- reinterpret (\case-    Get     -> readIORef rs-    Put s'  -> writeIORef rs s'-    State f -> liftIO $ atomicModifyIORefCAS rs (swap . f)) m+  x <- reinterpret (handleIORef rs) m   s' <- readIORef rs   pure (x, s') {-# INLINE runState #-}++-- | Run the 'State' effect in terms of operations on a supplied 'IORef'. The 'state' operation is atomic.+--+-- @since 0.2.1.0+runStateIORef :: IOE :> es => IORef s -> Eff (State s ': es) a -> Eff es a+runStateIORef rs = interpret $ handleIORef rs+{-# INLINE runStateIORef #-}++-- | Run the 'State' effect in terms of operations on a supplied 'MVar'.+--+-- @since 0.2.1.0+runStateMVar :: IOE :> es => MVar s -> Eff (State s ': es) a -> Eff es a+runStateMVar rs = interpret \case+  Get     -> readMVar rs+  Put s'  -> void $ swapMVar rs s'+  State f -> modifyMVar rs \s -> let (x, !s') = f s in pure (s', x)+{-# INLINE runStateMVar #-}++-- | Run the 'State' effect in terms of operations on a supplied 'TVar'.+--+-- @since 0.2.1.0+runStateTVar :: IOE :> es => TVar s -> Eff (State s ': es) a -> Eff es a+runStateTVar rs = interpret \case+  Get -> readTVarIO rs+  Put s' -> atomically $ writeTVar rs s'+  State f -> atomically do+    s <- readTVar rs+    let (x, !s') = f s+    writeTVar rs s'+    pure x+{-# INLINE runStateTVar #-}  -- | Run a 'State' effect in terms of a larger 'State' via a 'Lens''. zoom :: State t :> es => Lens' t s -> Eff (State s ': es) ~> Eff es