bluefin 0.5.1.0 → 0.5.100.0
raw patch · 33 files changed
+581/−230 lines, 33 filesdep ~bluefin-internalPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: bluefin-internal
API changes (from Hackage documentation)
+ Bluefin.Capability.Ask: ask :: forall (e :: Effects) (es :: Effects) r. e <: es => Reader r e -> Eff es r
+ Bluefin.Capability.Ask: asks :: forall (e :: Effects) (es :: Effects) r a. e <: es => Reader r e -> (r -> a) -> Eff es a
+ Bluefin.Capability.Ask: local :: forall (e1 :: Effects) (es :: Effects) r a. e1 <: es => Reader r e1 -> (r -> r) -> Eff es a -> Eff es a
+ Bluefin.Capability.Ask: runAsk :: forall r (es :: Effects) a. r -> (forall (e :: Effects). () => Ask r e -> Eff (e :& es) a) -> Eff es a
+ Bluefin.Capability.Ask: type Ask = Reader
+ Bluefin.Capability.AskCapability: askCapability :: forall (e :: Effects) (es :: Effects) h. (e <: es, Handle h) => HandleReader h e -> Eff es (h es)
+ Bluefin.Capability.AskCapability: asksCapability :: forall (e1 :: Effects) (es :: Effects) h r. (e1 <: es, Handle h) => AskCapability h e1 -> (forall (e :: Effects). () => h e -> Eff (e :& es) r) -> Eff es r
+ Bluefin.Capability.AskCapability: localCapability :: forall (e :: Effects) (es :: Effects) h r. (e <: es, Handle h) => AskCapability h e -> (h es -> h es) -> Eff es r -> Eff es r
+ Bluefin.Capability.AskCapability: runAskCapability :: forall (e1 :: Effects) (es :: Effects) h r. (e1 <: es, Handle h) => h e1 -> (forall (e :: Effects). () => HandleReader h e -> Eff (e :& es) r) -> Eff es r
+ Bluefin.Capability.AskCapability: type AskCapability = HandleReader
+ Bluefin.Capability.Await: await :: forall (e :: Effects) (es :: Effects) a. e <: es => Consume a e -> Eff es a
+ Bluefin.Capability.Await: awaitYield :: forall a (es :: Effects) r. (forall (e :: Effects). () => Await a e -> Eff (e :& es) r) -> (forall (e :: Effects). () => Yield a e -> Eff (e :& es) r) -> Eff es r
+ Bluefin.Capability.Await: eachAwait :: forall b (es :: Effects) r. (forall (e :: Effects). () => Await b e -> Eff (e :& es) r) -> Eff es b -> Eff es r
+ Bluefin.Capability.Await: takeAwait :: forall (e1 :: Effects) (es :: Effects) (e2 :: Effects) a. (e1 <: es, e2 <: es) => Int -> Await a e1 -> Yield a e2 -> Eff es ()
+ Bluefin.Capability.Await: type Await a = Consume a
+ Bluefin.Capability.JumpTo: jumpTo :: forall (e :: Effects) (es :: Effects) a. e <: es => Jump e -> Eff es a
+ Bluefin.Capability.JumpTo: type JumpTo = Jump
+ Bluefin.Capability.JumpTo: withJumpTo :: forall (es :: Effects). (forall (e :: Effects). () => JumpTo e -> Eff (e :& es) ()) -> Eff es ()
+ Bluefin.Capability.Modify: evalModify :: forall s (es :: Effects) a. s -> (forall (e :: Effects). () => Modify s e -> Eff (e :& es) a) -> Eff es a
+ Bluefin.Capability.Modify: get :: forall (e :: Effects) (es :: Effects) s. e <: es => State s e -> Eff es s
+ Bluefin.Capability.Modify: modify :: forall (e :: Effects) (es :: Effects) s. e <: es => State s e -> (s -> s) -> Eff es ()
+ Bluefin.Capability.Modify: put :: forall (e :: Effects) (es :: Effects) s. e <: es => State s e -> s -> Eff es ()
+ Bluefin.Capability.Modify: runModify :: forall s (es :: Effects) a. s -> (forall (e :: Effects). () => Modify s e -> Eff (e :& es) a) -> Eff es (a, s)
+ Bluefin.Capability.Modify: type Modify = State
+ Bluefin.Capability.Modify: withModify :: forall s (es :: Effects) a. s -> (forall (e :: Effects). () => Modify s e -> Eff (e :& es) (s -> a)) -> Eff es a
+ Bluefin.Capability.Request: connectRequests :: forall (es :: Effects) a b r. (forall (e :: Effects). () => Request a b e -> Eff (e :& es) r) -> (forall (e :: Effects). () => a -> Request b a e -> Eff (e :& es) r) -> Eff es r
+ Bluefin.Capability.Request: forEach :: forall a b (es :: Effects) r. (forall (e1 :: Effects). () => Coroutine a b e1 -> Eff (e1 :& es) r) -> (a -> Eff es b) -> Eff es r
+ Bluefin.Capability.Request: request :: forall (e1 :: Effects) (es :: Effects) a b. e1 <: es => Request a b e1 -> a -> Eff es b
+ Bluefin.Capability.Request: type Request = Coroutine
+ Bluefin.Capability.ReturnEarly: returnEarly :: forall (e :: Effects) (es :: Effects) r a. e <: es => EarlyReturn r e -> r -> Eff es a
+ Bluefin.Capability.ReturnEarly: type ReturnEarly = EarlyReturn
+ Bluefin.Capability.ReturnEarly: withReturnEarly :: forall r (es :: Effects). (forall (e :: Effects). () => EarlyReturn r e -> Eff (e :& es) r) -> Eff es r
+ Bluefin.Capability.Tell: execTell :: forall w (es :: Effects) r. Monoid w => (forall (e :: Effects). () => Tell w e -> Eff (e :& es) r) -> Eff es w
+ Bluefin.Capability.Tell: runTell :: forall w (es :: Effects) r. Monoid w => (forall (e :: Effects). () => Tell w e -> Eff (e :& es) r) -> Eff es (r, w)
+ Bluefin.Capability.Tell: tell :: forall (e :: Effects) (es :: Effects) w. e <: es => Writer w e -> w -> Eff es ()
+ Bluefin.Capability.Tell: type Tell = Writer
+ Bluefin.Capability.Throw: catch :: forall exn (es :: Effects) a. (forall (e :: Effects). () => Exception exn e -> Eff (e :& es) a) -> (exn -> Eff es a) -> Eff es a
+ Bluefin.Capability.Throw: handle :: forall exn (es :: Effects) a. (exn -> Eff es a) -> (forall (e :: Effects). () => Exception exn e -> Eff (e :& es) a) -> Eff es a
+ Bluefin.Capability.Throw: rethrowIO :: forall ex (es :: Effects) (e1 :: Effects) (e2 :: Effects) r. (e1 <: es, e2 <: es, Exception ex) => IOE e1 -> Exception ex e2 -> Eff es r -> Eff es r
+ Bluefin.Capability.Throw: throw :: forall (e :: Effects) (es :: Effects) ex a. e <: es => Exception ex e -> ex -> Eff es a
+ Bluefin.Capability.Throw: try :: forall exn (es :: Effects) a. (forall (e :: Effects). () => Exception exn e -> Eff (e :& es) a) -> Eff es (Either exn a)
+ Bluefin.Capability.Throw: type Throw = Exception
+ Bluefin.Capability.Yield: awaitYield :: forall a (es :: Effects) r. (forall (e :: Effects). () => Await a e -> Eff (e :& es) r) -> (forall (e :: Effects). () => Yield a e -> Eff (e :& es) r) -> Eff es r
+ Bluefin.Capability.Yield: catMaybes :: forall (e2 :: Effects) (es :: Effects) a r. e2 <: es => (forall (e1 :: Effects). () => Stream (Maybe a) e1 -> Eff (e1 :& es) r) -> Stream a e2 -> Eff es r
+ Bluefin.Capability.Yield: cycleToYield :: forall f (e1 :: Effects) (es :: Effects) a. (Foldable f, e1 <: es) => f a -> Yield a e1 -> Eff es ()
+ Bluefin.Capability.Yield: enumerate :: forall (e2 :: Effects) (es :: Effects) a r. e2 <: es => (forall (e1 :: Effects). () => Stream a e1 -> Eff (e1 :& es) r) -> Stream (Int, a) e2 -> Eff es r
+ Bluefin.Capability.Yield: enumerateFrom :: forall (e2 :: Effects) (es :: Effects) a r. e2 <: es => Int -> (forall (e1 :: Effects). () => Stream a e1 -> Eff (e1 :& es) r) -> Stream (Int, a) e2 -> Eff es r
+ Bluefin.Capability.Yield: forEach :: forall a b (es :: Effects) r. (forall (e1 :: Effects). () => Coroutine a b e1 -> Eff (e1 :& es) r) -> (a -> Eff es b) -> Eff es r
+ Bluefin.Capability.Yield: ignoreYield :: forall a (es :: Effects) r. (forall (e1 :: Effects). () => Stream a e1 -> Eff (e1 :& es) r) -> Eff es r
+ Bluefin.Capability.Yield: inFoldable :: forall t (e1 :: Effects) (es :: Effects) a. (Foldable t, e1 <: es) => t a -> Stream a e1 -> Eff es ()
+ Bluefin.Capability.Yield: mapMaybe :: forall (e2 :: Effects) (es :: Effects) a b r. e2 <: es => (a -> Maybe b) -> (forall (e1 :: Effects). () => Stream a e1 -> Eff (e1 :& es) r) -> Stream b e2 -> Eff es r
+ Bluefin.Capability.Yield: takeAwait :: forall (e1 :: Effects) (es :: Effects) (e2 :: Effects) a. (e1 <: es, e2 <: es) => Int -> Await a e1 -> Yield a e2 -> Eff es ()
+ Bluefin.Capability.Yield: type Yield a = Stream a
+ Bluefin.Capability.Yield: withYieldToList :: forall a (es :: Effects) r. (forall (e :: Effects). () => Stream a e -> Eff (e :& es) ([a] -> r)) -> Eff es r
+ Bluefin.Capability.Yield: yield :: forall (e1 :: Effects) (es :: Effects) a. e1 <: es => Stream a e1 -> a -> Eff es ()
+ Bluefin.Capability.Yield: yieldToList :: forall a (es :: Effects) r. (forall (e1 :: Effects). () => Stream a e1 -> Eff (e1 :& es) r) -> Eff es ([a], r)
+ Bluefin.Capability.Yield: yieldToReverseList :: forall a (es :: Effects) r. (forall (e :: Effects). () => Stream a e -> Eff (e :& es) r) -> Eff es ([a], r)
Files
- CHANGELOG.md +11/−0
- bluefin.cabal +13/−2
- src/Bluefin.hs +123/−140
- src/Bluefin/Capability.hs +40/−0
- src/Bluefin/Capability/Ask.hs +21/−0
- src/Bluefin/Capability/AskCapability.hs +29/−0
- src/Bluefin/Capability/Await.hs +22/−0
- src/Bluefin/Capability/JumpTo.hs +18/−0
- src/Bluefin/Capability/Modify.hs +17/−0
- src/Bluefin/Capability/Request.hs +89/−0
- src/Bluefin/Capability/ReturnEarly.hs +17/−0
- src/Bluefin/Capability/Tell.hs +19/−0
- src/Bluefin/Capability/Throw.hs +16/−0
- src/Bluefin/Capability/Yield.hs +35/−0
- src/Bluefin/CloneableHandle.hs +1/−2
- src/Bluefin/Compound.hs +56/−56
- src/Bluefin/Consume.hs +2/−0
- src/Bluefin/Coroutine.hs +3/−0
- src/Bluefin/DslBuilder.hs +2/−3
- src/Bluefin/DslBuilderEff.hs +2/−3
- src/Bluefin/EarlyReturn.hs +3/−0
- src/Bluefin/Exception.hs +3/−0
- src/Bluefin/GadtEffect.hs +1/−2
- src/Bluefin/HandleReader.hs +8/−5
- src/Bluefin/IO.hs +1/−1
- src/Bluefin/Jump.hs +2/−0
- src/Bluefin/Pipes.hs +6/−6
- src/Bluefin/Pipes/Prelude.hs +6/−6
- src/Bluefin/Prim.hs +2/−2
- src/Bluefin/Reader.hs +3/−1
- src/Bluefin/State.hs +3/−0
- src/Bluefin/Stream.hs +4/−1
- src/Bluefin/Writer.hs +3/−0
CHANGELOG.md view
@@ -1,3 +1,14 @@+* Covert to "capability" nomenclature++ See module "Bluefin.Capability" for a guide to the new naming. Users+ should convert to the new modules, since the old ones will be+ deprecated in the future. This is indicated by a comment in the+ documentation for each module that will undergo deprecation.++ * Add new "Capability" modules++ * Use "capability" terminology throughout documentation+ # 0.5.1.0 * Add `<:` type synonym for `:>`
bluefin.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: bluefin-version: 0.5.1.0+version: 0.5.100.0 license: MIT license-file: LICENSE author: Tom Ellis@@ -21,6 +21,17 @@ NoImplicitPrelude exposed-modules: Bluefin,+ Bluefin.Capability,+ Bluefin.Capability.Ask,+ Bluefin.Capability.AskCapability,+ Bluefin.Capability.Await,+ Bluefin.Capability.JumpTo,+ Bluefin.Capability.Modify,+ Bluefin.Capability.Request,+ Bluefin.Capability.ReturnEarly,+ Bluefin.Capability.Tell,+ Bluefin.Capability.Throw,+ Bluefin.Capability.Yield, Bluefin.Compound, Bluefin.Consume, Bluefin.Coroutine,@@ -45,6 +56,6 @@ Bluefin.System.IO, Bluefin.Writer, build-depends:- bluefin-internal >= 0.4.3.0 && < 0.6+ bluefin-internal >= 0.5.100.0 && < 0.6 hs-source-dirs: src default-language: Haskell2010
src/Bluefin.hs view
@@ -4,16 +4,16 @@ -- | Bluefin is an effect system which allows you to freely mix a -- variety of effects, including --- -- * "Bluefin.EarlyReturn", for early return- -- * "Bluefin.Exception", for exceptions+ -- * "Bluefin.Capability.ReturnEarly", for early return+ -- * "Bluefin.Capability.Throw", for exceptions -- * "Bluefin.IO", for I/O- -- * "Bluefin.State", for mutable state- -- * "Bluefin.Stream", for streams+ -- * "Bluefin.Capability.Modify", for mutable state+ -- * "Bluefin.Capability.Yield", for streams -- -- and to create your own effects in terms of existing ones -- ("Bluefin.Compound"). -- Bluefin effects are accessed explicitly through- -- value-level handles.+ -- value-level capabilities. -- * Why even use an effect system? @@ -107,10 +107,10 @@ -- question: if @let@ bindings don't interact with effects, -- because we can inline them freely, then how /can/ we perform -- effects in Haskell, and maintain control over the order in- -- which various operations occur? For a hour-long answer,+ -- which various operations occur? For an hour-long answer, -- concluding with an explanation of the development of effect -- systems, you can watch "[A History of Effect- -- systems](https://www.youtube.com/watch?v=RsTuy1jXQ6Y)" by Tom+ -- systems](https://www.youtube.com/watch?v=RsTuy1jXQ6Y)" by Bluefin author Tom -- Ellis (recorded at Zurihac 2025). -- -- The short answer is: t'Control.Monad.Monad's. @Monad@ is a@@ -140,7 +140,9 @@ -- -- which is not what we want at all: the final value would just be -- @"Initial value"@. An approach that /does/ work is to simulate- -- mutable state using an ad hoc "state passing" pattern:+ -- mutable state using an ad hoc "state passing" pattern. Here+ -- the variables @s1@ and @s2@ represent different values of the+ -- same state at different parts of program execution: -- -- @ -- let s1 = "Initial value"@@ -188,7 +190,7 @@ -- and [@mtl@](https://hackage.haskell.org/package/mtl) libraries. -- The transformer extensions of @State@ and @Either@ are -- t'Control.Monad.Trans.State.Strict.StateT' and- -- t'Control.Monad.Trans..ExceptT', and the @Mt@ extensions+ -- t'Control.Monad.Trans.ExceptT', and the @mtl@ extensions -- are t'Control.Monad.State.Strict.MonadState' and -- t'Control.Monad.Except.MonadError'. We won't go into more detail -- here because this documentation isn't a transformers or MTL@@ -397,7 +399,7 @@ -- /-- > exampleBluefin/ -- /-- 55/ -- exampleBluefin :: Int- -- exampleBluefin = runPureEff $ evalState 0 $ \\st -> do+ -- exampleBluefin = runPureEff $ evalModify 0 $ \\st -> do -- for_ [1..10] $ \\i -> do -- modify st (+ i) -- get st@@ -407,7 +409,7 @@ -- /-- > exampleEffectful/ -- /-- 55/ -- exampleEffectful :: Int- -- exampleEffectful = runPureEff $ evalState 0 $ do+ -- exampleEffectful = runPureEff $ evalModify 0 $ do -- for_ [1..10] $ \\i -> do -- modify (+ i) -- get@@ -456,97 +458,78 @@ -- * A Comparison of effect systems at a glance - -- ** Mixing effects+ -- ** IO -- |- -- - ✅ __IO__: I\/O, state via @IORef@, exceptions via @throw@/@catch@- -- - ❌ __ST__: State only- -- - ✅ __MTL__\/__fused-effects__\/__Polysemy__- -- - ✅ __Bluefin__\/__effectful__+ -- - ✅ __Mixing effects__: I\/O, state via @IORef@, exceptions via @throw@/@catch@+ -- - ❌ __Fine-grained effects__: No distinction between different effects (state, exceptions, I/O, etc.)+ -- - ❌ __Encapsulation__: Can handle exceptions, but doing so is not+ -- reflected in the type+ -- - ✅ __Resource safety__: Operations can be bracketed (see+ -- @Control.Exception.'Control.Exception.bracket'@)+ -- - ✅ __Predictable performance__+ -- - ❌ __Multishot continuations__ - -- ** Fine-grained Effects+ -- ** ST -- |- -- - ❌ __IO__: No distinction between different effects (state, exceptions, I/O, etc.)- -- - ✅ __ST__: But state only- -- - ✅ __MTL__\/__fused-effects__\/__Polysemy__: Individual effects are represented at the type level- -- - ✅ __Bluefin__\/__effectful__: Individual effects are represented at the type level+ -- - ❌ __Mixing effects__: State only+ -- - ✅ __Fine-grained effects__: But state only+ -- - ✅ __Encapsulation__: State effects handled by @runST@ are not present+ -- in the operation's type signature+ -- - ❌ __Resource safety__: State only+ -- - ✅ __Predictable performance__+ -- - ❌ __Multishot continuations__ - -- ** Encapsulation+ -- ** MTL\/fused-effects\/Polysemy -- |- --- -- - ❌ __IO__: Can handle exceptions, but doing so is not- -- reflected in the type- --- -- - ❌ __ST__: State only- --- -- - ✅ __MTL__\/__fused-effects__\/__Polysemy__: Exceptions,- -- state and other effects handled in the body of an operation+ -- - ✅ __Mixing effectns__+ -- - ✅ __Fine-grained effects__: Individual effects are represented at the type level+ -- - ✅ __Encapsulation__: Exceptions, state and other effects handled in the body of an operation -- are not present in the operation's type signature- --- -- - ✅ __Bluefin__\/__effectful__: Exceptions, state and other- -- effects handled in the body of an operation are not present- -- in the operation's type signature+ -- - ❌ __Resource safety__: Difficult to achieve resource safety for arbitrary effects+ -- - ❌ __Predictable performance__: Good performance depends critically on GHC optimization+ -- - ✅ __Multishot continuations__ - -- ** Resource Safety+ -- ** Bluefin\/effectful -- |- -- - ✅ __IO__: Operations can be bracketed (see- -- @Control.Exception.'Control.Exception.bracket'@)- --- -- - ❌ __ST__: State only- --- -- - ❌ __MTL__\/__fused-effects__\/__Polysemy__: Difficult to- -- achieve resource safety for arbitrary effects- --- -- - ✅ __Bluefin__\/__effectful__: Operations can be bracketed+ -- - ✅ __Mixing effects__+ -- - ✅ __Fine-grained effects__: Individual effects are represented at the type level+ -- - ✅ __Encapsulatio__: Exceptions, state and other+ -- effects handled in the body of an operation are not present+ -- in the operation's type signature+ -- - ✅ __Resource safety__: Operations can be bracketed -- (see e.g. @Bluefin.Eff.'Bluefin.Eff.bracket'@) because these -- effect systems wrap @IO@-- -- ** Predictable Performance-- -- |- -- - ✅ __IO__: Predictable performance- -- - ✅ __ST__: Predictable performance- --- -- - ❌ __MTL__\/__fused-effects__\/__Polysemy__: Good performance- -- depends critically on GHC optimization- --- -- - ✅ __Bluefin__\/__effectful__: Predictable performance+ -- - ✅ __Predictable performance__: Predictable performance -- because these effect systems wrap @IO@-- -- ** Multishot continuations-- -- |- -- - ❌ __IO__- -- - ❌ __ST__- -- - ✅ __MTL__\/__fused-effects__\/__Polysemy__- -- - ❌ __Bluefin__\/__effectful__+ -- - ❌ __Multishot continuations__ -- * Introduction to Bluefin -- | Bluefin is a Haskell effect system with a new style of API. -- It is distinct from prior effect systems because effects are- -- accessed explicitly through value-level handles which occur as- -- arguments to effectful operations. Handles (such as- -- 'Bluefin.State.State' handles, which allow access to mutable+ -- accessed explicitly through value-level capabilities which occur as+ -- arguments to effectful operations. Capabilities (such as+ -- 'Bluefin.Capability.Modify.Modify' capabilities, which allow access to mutable -- state) are introduced by handlers (such as- -- 'Bluefin.State.evalState', which sets the initial state).- -- Here's an example where a mutable state effect handle, @sn@, is- -- introduced by its handler, 'Bluefin.State.evalState'.+ -- 'Bluefin.Capability.Modify.evalModify', which sets the initial state).+ -- Here's an example where a mutable state effect capability, @sn@, is+ -- introduced by its handler, 'Bluefin.Capability.Modify.evalModify'. -- -- @ -- -- If @n < 10@ then add 10 to it, otherwise -- -- return it unchanged -- example1 :: Int -> Int -- example1 n = 'Bluefin.Eff.runPureEff' $- -- -- Create a new state handle, sn, and+ -- -- Create a new modify capability, sn, and -- -- initialize the value of the state to n- -- 'Bluefin.State.evalState' n $ \\sn -> do- -- n' <- 'Bluefin.State.get' sn+ -- 'Bluefin.Capability.Modify.evalModify' n $ \\sn -> do+ -- n' <- 'Bluefin.Capability.Modify.get' sn -- when (n' < 10) $- -- 'Bluefin.State.modify' sn (+ 10)+ -- 'Bluefin.Capability.Modify.modify' sn (+ 10) -- get sn -- @ --@@ -557,12 +540,12 @@ -- 12 -- @ --- -- The handle @sn@ is used in much the same way as an+ -- The capability @sn@ is used in much the same way as an -- 'Data.STRef.STRef' or 'Data.IORef.IORef'. -- ** Multiple effects of the same type - -- | A benefit of value-level effect handles is that it's simple+ -- | A benefit of value-level effect capabilities is that it's simple -- to have multiple effects of the same type in scope at the same -- time. It is simple to disambiguate them, because they are -- distinct values! By contrast, existing effect systems require@@ -577,14 +560,14 @@ -- -- to the smaller -- example2 :: (Int, Int) -> (Int, Int) -- example2 (m, n) = 'Bluefin.Eff.runPureEff' $- -- 'Bluefin.State.evalState' m $ \\sm -> do- -- evalState n $ \\sn -> do+ -- 'Bluefin.Capability.Modify.evalModify' m $ \\sm -> do+ -- evalModify n $ \\sn -> do -- do- -- n' <- 'Bluefin.State.get' sn+ -- n' <- 'Bluefin.Capability.Modify.get' sn -- m' <- get sm -- -- if n' < m'- -- then 'Bluefin.State.modify' sn (+ 10)+ -- then 'Bluefin.Capability.Modify.modify' sn (+ 10) -- else modify sm (+ 10) -- -- n' <- get sn@@ -600,11 +583,11 @@ -- (30, 13) -- @ - -- ** Exception handles+ -- ** Exception capabilities -- | Bluefin exceptions are accessed through- -- 'Bluefin.Exception.Exception' handles. An @Exception@ handle- -- is introduced by a handler, such as 'Bluefin.Exception.try',+ -- 'Bluefin.Capability.Throw.Throw' capabilities. A @Throw@ capability+ -- is introduced by a handler, such as 'Bluefin.Capability.Throw.try', -- and that handler is where the exception, if thrown, will be -- handled. This arrangement differs from normal Haskell -- exceptions in two ways. Firstly, every Bluefin exception will@@ -613,26 +596,26 @@ -- only one place – normal Haskell exceptions can be handled in a -- variety of places, and the closest handler of matching type on -- the stack will be the one that will be chosen upon- -- 'Control.Exception.throw'.+ -- @Control.Exception.'Control.Exception.throw'@. -- -- @example3@ shows how to use Bluefin to calculate the sum of -- numbers from 1 to @n@, but stop if the sum becomes bigger than- -- 20. The exception handle, @ex@, which has type @Exception+ -- 20. The throw capability, @ex@, which has type @Throw -- String e@, cannot escape the scope of its handler, @try@. If -- thrown it will be handled at that @try@, and nowhere else. -- -- @ -- example3 :: Int -> Either String Int -- example3 n = 'Bluefin.Eff.runPureEff' $- -- 'Bluefin.Exception.try' $ \\ex -> do- -- 'Bluefin.State.evalState' 0 $ \\total -> do+ -- 'Bluefin.Capability.Throw.try' $ \\ex -> do+ -- 'Bluefin.Capability.Modify.evalModify' 0 $ \\total -> do -- for_ [1..n] $ \\i -> do- -- soFar <- 'Bluefin.State.get' total+ -- soFar <- 'Bluefin.Capability.Modify.get' total -- when (soFar > 20) $ do- -- 'Bluefin.Exception.throw' ex ("Became too big: " ++ show soFar)- -- 'Bluefin.State.put' total (soFar + i)+ -- 'Bluefin.Capability.Throw.throw' ex ("Became too big: " ++ show soFar)+ -- 'Bluefin.Capability.Modify.put' total (soFar + i) --- -- 'Bluefin.State.get' total+ -- 'Bluefin.Capability.Modify.get' total -- @ -- -- @@@ -645,68 +628,68 @@ -- ** Effect scoping -- | Bluefin's use of the type system is very similar to- -- "Control.Monad.ST": it ensures that a handle can never escape+ -- "Control.Monad.ST": it ensures that a capability can never escape -- the scope of its handler. That is, once the handler has- -- finished running there is no way you can use the handle+ -- finished running there is no way you can use the capability -- anymore. For an example of a correctly-scoped function see- -- @correctlyScoped@ below. It uses Bluefin’s @State@ handle to+ -- @correctlyScoped@ below. It uses Bluefin’s @Modify@ capability to -- compute the sum of the numbers 1 to 10, before multiplying the- -- result by 20. In @correctlyScoped@ the @State@ handle is scoped- -- to its handler, @evalState@, and everything works as expected:+ -- result by 20. In @correctlyScoped@ the @Modify@ capability is scoped+ -- to its handler, @evalModify@, and everything works as expected: -- -- @ -- -- /Result: 1100/ -- correctlyScoped :: Eff es Integer -- correctlyScoped = do -- -- /Initial state 0/- -- r \<- 'Bluefin.State.evalState' 0 $ \\st -> do- -- -- The 'Bluefin.State.State' handle "st" is scoped to the- -- -- handler that introduced it, evalState,+ -- r \<- 'Bluefin.Capability.Modify.evalModify' 0 $ \\st -> do+ -- -- The 'Bluefin.Capability.Modify.Modify' handle "st" is scoped to the+ -- -- handler that introduced it, evalModify, -- -- and therefore it can only be used within -- -- this do block. -- -- -- /Add up the numbers 1 to 10/ -- for_ [1..10] $ \\i -> do- -- 'Bluefin.State.modify' st (+ i)+ -- 'Bluefin.Capability.Modify.modify' st (+ i) -- -- -- /Get the result/- -- 'Bluefin.State.get' st+ -- 'Bluefin.Capability.Modify.get' st -- -- pure (r * 20) -- @ -- -- Now let's look at an incorrectly-scoped example,- -- @incorrectlyScoped@. It attempts to pass the state handle @st@- -- out of the scope of @evalState@:+ -- @incorrectlyScoped@. It attempts to pass the modify capability @st@+ -- out of the scope of @evalModify@: -- -- @ -- incorrectlyScoped :: Eff es Integer -- incorrectlyScoped = do -- -- /Initial state 0/- -- (total, st) \<- 'Bluefin.State.evalState' 0 $ \\st -> do+ -- (total, st) \<- 'Bluefin.Capability.Modify.evalModify' 0 $ \\st -> do -- -- /Add up the numbers 1 to 10/ -- for_ [1..10] $ \\i -> do- -- 'Bluefin.State.modify' st (+ i)+ -- 'Bluefin.Capability.Modify.modify' st (+ i) -- -- -- /Get the result/- -- r <- 'Bluefin.State.get' st+ -- r <- 'Bluefin.Capability.Modify.get' st -- -- -- /Pass out the result, and try to pass the/- -- -- /'Bluefin.State.State' handle outside its scope, i.e. this/- -- -- /do block introduced by evalState/+ -- -- /'Bluefin.Capability.Modify.Modify' capability outside its scope, i.e. this/+ -- -- /do block introduced by evalModify/ -- pure (r, st) -- -- modify st (* 20) -- get st -- @ --- -- The type system prevents us from passing the @State@ handle out+ -- The type system prevents us from passing the @Modify@ capability out -- of its scope, giving this error message: -- -- @ -- • Couldn't match type ‘e0’ with ‘e’- -- Expected: (Integer, State Integer e0)- -- Actual: (Integer, State Integer e)+ -- Expected: (Integer, Modify Integer e0)+ -- Actual: (Integer, Modify Integer e) -- because type variable ‘e’ would escape its scope -- @ @@ -716,19 +699,19 @@ -- pattern which looks like -- -- @- -- (e1 \<: es, ...) -> \<Handle\> e1 -> ... -> Eff es r+ -- (e1 \<: es, ...) -> \<Capability\> e1 -> ... -> Eff es r -- @ --- -- Here @\<Handle\>@ could be, for example, @State Int@,- -- @Exception String@ or @IOE@. Consider the function below,+ -- Here @\<Capability\>@ could be, for example, @Modify Int@,+ -- @Throw String@ or @IOE@. Consider the function below, -- @incrementReadLine@. It reads integers from standard input, -- accumulates them into a state; it returns when it reads the -- input integer @0@ and it throws an exception if it encounters -- an input line it cannot parse. --- -- Firstly, let's look at the arguments, which are all handles to- -- Bluefin effects. There is a state handle, an exception handle,- -- and an IO handle, which allow modification of an @Int@ state,+ -- Firstly, let's look at the arguments, which are all capabilities.+ -- There is a modify capability, a throw capability,+ -- and an IO capability, which allow modification of an @Int@ state, -- throwing a @String@ exception, and performing @IO@ operations -- respectively. They are each tagged with a different effect -- type, @e1@, @e2@ and @e3@ respectively, which are always kept@@ -743,10 +726,10 @@ -- Finally, let's look at the constraints. They are what tie -- together the effect tags of the arguments to the effect tag of -- the result. For every argument effect tag @en@ we have a- -- constraint @en \<: es@. That tells us the that effect handle+ -- constraint @en \<: es@. That tells us the that capability -- with tag @en@ is allowed to be used within the effectful- -- computation. If we didn't have the @e1 \<: es@ constraint, for- -- example, that would tell us that the @State Int e1@ isn't+ -- computation. If the @e1 \<: es@ constraint, for+ -- example, were not required that would tell us that the @Modify Int e1@ isn't -- actually used anywhere in the computation. -- -- GHC and editor tools like HLS do a good job of inferring these@@ -755,8 +738,8 @@ -- @ -- incrementReadLine :: -- (e1 \<: es, e2 \<: es, e3 \<: es) =>- -- State Int e1 ->- -- Exception String e2 ->+ -- Modify Int e1 ->+ -- Throw String e2 -> -- IOE e3 -> -- Eff es () -- incrementReadLine state exception io = do@@ -764,20 +747,20 @@ -- line <- 'Bluefin.IO.effIO' io getLine -- i <- case 'Text.Read.readMaybe' line of -- Nothing ->- -- 'Bluefin.Exception.throw' exception ("Couldn't read: " ++ line)+ -- 'Bluefin.Capability.Throw.throw' exception ("Couldn't read: " ++ line) -- Just i -> -- pure i -- -- when (i == 0) $ -- 'Bluefin.Jump.jumpTo' break --- -- 'Bluefin.State.modify' state (+ i)+ -- 'Bluefin.Capability.Modify.modify' state (+ i) -- @ -- -- Now let's look at how we can run such a function. Each effect -- must be handled by a corresponding handler, for example- -- 'Bluefin.State.runState' for the state effect,- -- 'Bluefin.Exception.try' for the exception effect and+ -- 'Bluefin.Capability.Modify.runModify' for the state effect,+ -- 'Bluefin.Capability.Throw.try' for the exception effect and -- 'Bluefin.Eff.runEff_' for the @IO@ effect. The type signatures -- of handlers also follow a common pattern, which looks like --@@ -785,8 +768,8 @@ -- (forall e. \<Handle\> e -> Eff (e :& es) a) -> Eff es r -- @ --- -- This means that the effect @e@, corresponding to the handle- -- @\<Handle\> e@, has been handled and removed from the set of+ -- This means that the effect tag @e@, corresponding to the capability+ -- @\<Capability\> e@, has been handled and removed from the set of -- remaining effects, @es@. (The signatures for -- 'Bluefin.Eff.runEff_' and 'Bluefin.Eff.runPureEff' are slightly -- different because they remove @Eff@ itself.) Here, then, is@@ -795,8 +778,8 @@ -- @ -- runIncrementReadLine :: IO (Either String Int) -- runIncrementReadLine = 'Bluefin.Eff.runEff_' $ \\io -> do- -- 'Bluefin.Exception.try' $ \\exception -> do- -- ((), r) \<- 'Bluefin.State.runState' 0 $ \\state -> do+ -- 'Bluefin.Capability.Throw.try' $ \\exception -> do+ -- ((), r) \<- 'Bluefin.Capability.Modify.runModify' 0 $ \\state -> do -- incrementReadLine state exception io -- pure r --@@ -827,7 +810,7 @@ -- ** @effectful@ -- | The major difference between Bluefin and @effectful@ is that in- -- Bluefin effects are represented as value-level handles whereas+ -- Bluefin effects are represented as value-level capabilities whereas -- in @effectful@ they are represented only at the type level. -- @effectful@ could be described as "a well-typed implementation of -- the @ReaderT@ @IO@ pattern", and Bluefin could be described as@@ -853,15 +836,15 @@ -- | Bluefin has a similar implementation style to @effectful@. -- t'Bluefin.Eff.Eff' is an opaque wrapper around 'IO',- -- t'Bluefin.State.State' is an opaque wrapper around- -- 'Data.IORef.IORef', and 'Bluefin.Exception.throw' throws an- -- actual @IO@ exception. t'Bluefin.Coroutine.Coroutine' is+ -- t'Bluefin.Capability.Modify.Modify' is an opaque wrapper around+ -- 'Data.IORef.IORef', and 'Bluefin.Capability.Throw.throw' throws an+ -- actual @IO@ exception. t'Bluefin.Capability.Request.Request' is -- implemented simply as a function. -- -- @ -- newtype t'Bluefin.Eff.Eff' (es :: 'Bluefin.Eff.Effects') a = 'Bluefin.Internal.UnsafeMkEff' (IO a)- -- newtype t'Bluefin.State.State' s (st :: Effects) = 'Bluefin.Internal.UnsafeMkState' (IORef s)- -- newtype t'Bluefin.Coroutine.Coroutine' a b (s :: Effects) = 'Bluefin.Internal.UnsafeMkCoroutine' (a -> IO b)+ -- newtype t'Bluefin.Capability.Modify.Modify' s (st :: Effects) = 'Bluefin.Internal.UnsafeMkState' (IORef s)+ -- newtype t'Bluefin.Capability.Request.Request' a b (s :: Effects) = 'Bluefin.Internal.UnsafeMkCoroutine' (a -> IO b) -- @ -- -- The type parameters of kind t'Bluefin.Eff.Effects' are phantom@@ -896,16 +879,16 @@ -- @ -- countPositivesNegatives :: [Int] -> String -- countPositivesNegatives is = 'Bluefin.Eff.runPureEff' $- -- 'Bluefin.State.evalState' (0 :: Int) $ \\positives -> do- -- r \<- 'Bluefin.Exception.try' $ \\ex ->- -- evalState (0 :: Int) $ \\negatives -> do+ -- 'Bluefin.Capability.Modify.evalModify' (0 :: Int) $ \\positives -> do+ -- r \<- 'Bluefin.Capability.Throw.try' $ \\ex ->+ -- evalModify (0 :: Int) $ \\negatives -> do -- for_ is $ \\i -> do -- case compare i 0 of- -- GT -> 'Bluefin.State.modify' positives (+ 1)+ -- GT -> 'Bluefin.Capability.Modify.modify' positives (+ 1) -- EQ -> throw ex () -- LT -> modify negatives (+ 1) --- -- p <- 'Bluefin.State.get' positives+ -- p <- 'Bluefin.Capability.Modify.get' positives -- n <- get negatives -- -- pure $
+ src/Bluefin/Capability.hs view
@@ -0,0 +1,40 @@+module Bluefin.Capability+ ( -- * Historical commentary++ -- | Bluefin is in a transitionary phase moving away from the old+ -- terminology of "handle" and naming handles/effects based on+ -- MTL\/transformers style names+ -- (e.g. @Exception@\/@Reader@\/@Stream@) and moving towards+ -- calling these things "capabilities" and naming them after their+ -- main operation (e.g. @Throw@\/@Ask@\/@Yield@). You are+ -- encouraged to use the API beneath @Bluefin.Capability@ because+ -- that will be the supported API in the future.+ --+ -- You are encouraged to change your usage of the old modules on+ -- the left to the new modules on the right:+ --+ -- +------------------------+------------------------------------------++ -- | Old | New |+ -- +========================+==========================================++ -- | "Bluefin.Reader" | "Bluefin.Capability.Ask" |+ -- +------------------------+------------------------------------------++ -- | "Bluefin.HandleReader" | "Bluefin.Capability.AskCapability" |+ -- +------------------------+------------------------------------------++ -- | "Bluefin.Consume" | "Bluefin.Capability.Await" |+ -- +------------------------+------------------------------------------++ -- | "Bluefin.Jump" | "Bluefin.Capability.JumpTo" |+ -- +------------------------+------------------------------------------++ -- | "Bluefin.State" | "Bluefin.Capability.Modify" |+ -- +------------------------+------------------------------------------++ -- | "Bluefin.Coroutine" | "Bluefin.Capability.Request" |+ -- +------------------------+------------------------------------------++ -- | "Bluefin.EarlyReturn" | "Bluefin.Capability.ReturnEarly" |+ -- +------------------------+------------------------------------------++ -- | "Bluefin.Writer" | "Bluefin.Capability.Tell" |+ -- +------------------------+------------------------------------------++ -- | "Bluefin.Exception" | "Bluefin.Capability.Throw" |+ -- +------------------------+------------------------------------------++ -- | "Bluefin.Stream" | "Bluefin.Capability.Yield" |+ -- +------------------------+------------------------------------------++ )+where
+ src/Bluefin/Capability/Ask.hs view
@@ -0,0 +1,21 @@+module Bluefin.Capability.Ask+ ( -- | 'Ask' is Bluefin's version of the+ -- "Control.Monad.Trans.Reader" monad. 'local' allows you to+ -- locally override the @ask@ed value in a well-scoped way. The+ -- original value will be restored when you exit the @local@ block+ -- regardless of whether the exit was normal or via an exception.++ -- * Capability+ Ask,++ -- * Handlers+ runAsk,++ -- * Effectful operations+ ask,+ asks,+ local,+ )+where++import Bluefin.Internal
+ src/Bluefin/Capability/AskCapability.hs view
@@ -0,0 +1,29 @@+-- | 'AskCapability' is like t'Bluefin.Capability.Ask.Ask', generalized to+-- work for arbitrary t'Bluefin.Compound.Handle's. 'localCapability'+-- locally overrides the value of a capability in a well-scoped way. The+-- original capability will be restored when you exit the @localCapability@+-- block regardless of whether the exit was normal or via an+-- exception.+--+-- @AskCapability@ supports functionality similiar to @effectful@'s+-- [@interpose@](https://hackage.haskell.org/package/effectful-core/docs/Effectful-Dispatch-Dynamic.html#v:interpose)+-- and @polysemy@'s+-- [@intercept@](https://hackage.haskell.org/package/polysemy/docs/Polysemy.html#v:intercept),+-- that is, locally augmenting an effect with new behaviors. If you+-- want to do the same in Bluefin you may want to start with+-- @Bluefin.GadtEffect.'Bluefin.GadtEffect.interpose`@.+module Bluefin.Capability.AskCapability+ ( -- * Handle+ AskCapability,++ -- * Handlers+ runAskCapability,++ -- * Effectful operations+ askCapability,+ asksCapability,+ localCapability,+ )+where++import Bluefin.Internal
+ src/Bluefin/Capability/Await.hs view
@@ -0,0 +1,22 @@+module Bluefin.Capability.Await+ ( -- | 'Await' allows you to await values during the execution of+ -- a Bluefin operation. It provides similar functionality to+ -- @await@ from Conduit or Pipes.+ --+ -- For information about prompt finalization/resource safety when+ -- using Bluefin @Consume@s, see "Bluefin.Coroutine".++ -- * Capability+ Await,++ -- * Handlers+ eachAwait,+ awaitYield,++ -- * Effectful operations+ await,+ takeAwait,+ )+where++import Bluefin.Internal
+ src/Bluefin/Capability/JumpTo.hs view
@@ -0,0 +1,18 @@+module Bluefin.Capability.JumpTo+ ( -- | 'JumpTo' allows you to jump back to a previously-set location.+ -- A "jump" is equivalent to an untyped early return, or more+ -- precisely an early return of type @()@, which is itself an+ -- exception of type @()@.++ -- * Capability+ JumpTo,++ -- * Handlers+ withJumpTo,++ -- * Effectful operations+ jumpTo,+ )+where++import Bluefin.Internal
+ src/Bluefin/Capability/Modify.hs view
@@ -0,0 +1,17 @@+module Bluefin.Capability.Modify+ ( -- * Capability+ Modify,++ -- * Handlers+ evalModify,+ runModify,+ withModify,++ -- * Effectful operations+ get,+ put,+ modify,+ )+where++import Bluefin.Internal
+ src/Bluefin/Capability/Request.hs view
@@ -0,0 +1,89 @@+module Bluefin.Capability.Request+ ( -- | @Request@ allows to yield values and await the result. You+ -- might want to start with "Bluefin.Capability.Yield", which is+ -- the most common way to use @Request@s.++ -- ** Prompt finalization/resource safety++ -- | Bluefin+ -- t'Bluefin.Capability.Yield.Yield' \/ t'Bluefin.Capability.Await.Await' \/ t'Bluefin.Capability.Request.Request'+ -- computations have much better resource safety properties than+ -- Conduit and Pipes. You can use+ -- @Bluefin.Eff.'Bluefin.Eff.bracket'@ within a streaming+ -- computation and the acquired resource is guaranteed to be+ -- released and the end of the bracket, rather than at the end of+ -- the @ResourceT@ scope as it is the case in Conduit and Pipes.+ -- See the blog post [Bluefin streams finalize+ -- promptly](https://h2.jaguarpaw.co.uk/posts/bluefin-streams-finalize-promptly/)+ -- for more details.++ -- ** Running coroutines that communicate via @Request@s++ -- | Bluefin operations can be executed as coroutines using+ -- 'connectRequests' ([Wikipedia+ -- suggests](https://en.wikipedia.org/wiki/Coroutine#Definition_and_types)+ -- that such coroutines are "second-class stackful coroutines").+ -- Two coroutines run in this way communicate synchronously by+ -- using @Request@s to interact with a bi-directional+ -- channel. This means that such coroutines are often run+ -- exclusively for what they communicate via this channel, not+ -- for their return value.+ --+ -- @Request@s used in this way work a bit like UNIX pipes: there+ -- is a downstream consumer and an upstream generator. For every+ -- pair of such communicating coroutines there are two ends,+ -- represented with the capabilities @Request a b@ and @Request b+ -- a@. The first parameter to @Request@ is the type that can be+ -- /sent from/ that end, while the second parameter is the type+ -- that will subsequently be /received by/ that end. This explains+ -- the symmetry in the capabilities: what one end sends the other+ -- receives. The implication is that upstream and downstream+ -- exchange messages with each other at the same+ -- time. Additionally, there is a clear order of communication+ -- from the start (in Bluefin, communication is started by+ -- upstream).+ --+ -- 'request' is the only effectful operation required: a @Request+ -- a b@ capability that represents one end of a channel sends @a@s+ -- and receives a @b@s. For many use cases, upstream does not need+ -- to know anything from downstream (dually: downstream does not+ -- need to communicate anything to upstream) except that+ -- downstream is making a new request, so the capabilities that+ -- describe most channels are \"@Request a ()@\" and \"@Request ()+ -- a@\". Bluefin provides synonyms for these:+ -- @'Bluefin.Capability.Yield.Yield' a@ and+ -- @'Bluefin.Capability.Await.Await' a@, respectively. The+ -- specializations of @request@ for @Yield@ and @Await@ are called+ -- 'Bluefin.Capability.Yield.yield' and+ -- 'Bluefin.Capability.Await.await'. Coroutines that send data in+ -- only one direction like this can be created using 'awaitYield'.+ --+ -- Because the message exchange occurs synchronously, when yielding,+ -- the upstream will block until the downstream awaits. The converse+ -- is also true: when downstream awaits, it will block until upstream+ -- yields.+ --+ -- Any Bluefin effectful operation that takes a @Request@+ -- capability as an argument can be run as coroutine using+ -- 'connectRequests' by providing a second effectful operation+ -- as its counterpart on the other end of the channel.+ --+ -- For simple applications one may not need @connectRequests@ at+ -- all, because specific handlers are already provided by+ -- Bluefin. See the \"Handlers\" sections of the+ -- "Bluefin.Capability.Yield" and "Bluefin.Capability.Await"+ -- modules.++ -- * Capability+ Request,++ -- * Handlers+ forEach,+ connectRequests,++ -- * Effectful operations+ request,+ )+where++import Bluefin.Internal
+ src/Bluefin/Capability/ReturnEarly.hs view
@@ -0,0 +1,17 @@+module Bluefin.Capability.ReturnEarly+ ( -- | @Bluefin.ReturnEarly@ allows to define a block from which you can+ -- return early. Early return is implemented as an exception, and+ -- its API is just an alternate interface to exceptions.++ -- * Capability+ ReturnEarly,++ -- * Handlers+ withReturnEarly,++ -- * Effectful operations+ returnEarly,+ )+where++import Bluefin.Internal
+ src/Bluefin/Capability/Tell.hs view
@@ -0,0 +1,19 @@+module Bluefin.Capability.Tell+ ( -- | In most cases you'll probably prefer t'Bluefin.Capability.Yield.Yield'+ -- to @Tell@, but @Tell@ can still be useful in some cases,+ -- for example with @Data.Monoid.'Data.Monoid.Any'@ to determine+ -- whether an event ever occurred.++ -- * Capability+ Tell,++ -- * Handlers+ runTell,+ execTell,++ -- * Effectful operations+ tell,+ )+where++import Bluefin.Internal
+ src/Bluefin/Capability/Throw.hs view
@@ -0,0 +1,16 @@+module Bluefin.Capability.Throw+ ( -- * Capability+ Throw,++ -- * Handlers+ try,+ handle,+ catch,++ -- * Effectful operations+ throw,+ rethrowIO,+ )+where++import Bluefin.Internal
+ src/Bluefin/Capability/Yield.hs view
@@ -0,0 +1,35 @@+module Bluefin.Capability.Yield+ ( -- | 'Yield' allows you to yield values during the execution of a+ -- Bluefin operation. It provides similar functionality to+ -- Python's @yield@. The handler of the 'Yield' will either+ -- handle each element as soon as it is yielded (for example+ -- 'forEach') or gather all yielded elements into a list (for+ -- example 'yieldToList').+ --+ -- For information about prompt finalization/resource safety when+ -- using Bluefin @Yield@s, see "Bluefin.Capability.Request".++ -- * Capability+ Yield,++ -- * Handlers+ forEach,+ yieldToList,+ yieldToReverseList,+ withYieldToList,+ ignoreYield,+ enumerate,+ enumerateFrom,+ mapMaybe,+ catMaybes,+ awaitYield,++ -- * Effectful operations+ yield,+ inFoldable,+ cycleToYield,+ takeAwait,+ )+where++import Bluefin.Internal
src/Bluefin/CloneableHandle.hs view
@@ -1,6 +1,5 @@ -- | @Bluefin.CloneableHandle@ defines the 'CloneableHandle' class, -- whose purpose is to support 'withEffToIOCloneHandle'.- module Bluefin.CloneableHandle ( -- | 'withEffToIOCloneHandle' is an @IO@ unlifting function that -- clones its handle each time it runs @Eff@ in @IO@. This is@@ -81,7 +80,7 @@ -- * @CloneableHandle@ CloneableHandle,- GenericCloneableHandle(MkGenericCloneableHandle),+ GenericCloneableHandle (MkGenericCloneableHandle), GCloneableHandle, -- * @GHC.Generics@ re-exports
src/Bluefin/Compound.hs view
@@ -7,22 +7,22 @@ -- creating your own effects is equivalent to creating your own -- data types. We just use the techniques we know and love from -- Haskell! For example, if I want to make a "counter" effect- -- that allows me to increment a counter then I can wrap a 'Bluefin.State.State'- -- handle in a newtype:+ -- that allows me to increment a counter then I can wrap a 'Bluefin.Capability.Modify.Modify'+ -- capability in a newtype: -- -- @- -- newtype Counter1 e = MkCounter1 ('Bluefin.State.State' Int e)+ -- newtype Counter1 e = MkCounter1 ('Bluefin.Capability.Modify.Modify' Int e) -- -- incCounter1 :: (e \<: es) => Counter1 e -> 'Bluefin.Eff.Eff' es ()- -- incCounter1 (MkCounter1 st) = 'Bluefin.State.modify' st (+ 1)+ -- incCounter1 (MkCounter1 st) = 'Bluefin.Capability.Modify..modify' st (+ 1) -- -- runCounter1 :: -- (forall e. Counter1 e -> Eff (e :& es) r) -> -- Eff es Int -- runCounter1 k =- -- 'Bluefin.State.evalState' 0 $ \\st -> do+ -- 'Bluefin.Modify.evalModify' 0 $ \\st -> do -- _ <- k (MkCounter1 st)- -- 'Bluefin.State.get' st+ -- 'Bluefin.Capability.Modify.get' st -- @ -- -- Running the handler tells me the number of times I incremented@@ -47,29 +47,29 @@ -- normal approach we use to wrap multiple values into a single -- value: define a new data type with multiple fields. There's a -- caveat to this approach, but before we address the caveat let's- -- see the approach in action. Here we define a new handle,- -- @Counter2@, that contains a 'Bluefin.State.State' and 'Bluefin.Exception.Exception' handle+ -- see the approach in action. Here we define a new capabiilty,+ -- @Counter2@, that contains a 'Bluefin.Capability.Modify.Modify' and 'Bluefin.Capability.Throw.Throw' capability -- within it. That allows us to increment the counter and throw -- an exception when we hit a limit. -- -- @- -- data Counter2 e1 e2 = MkCounter2 ('Bluefin.State.State' Int e1) ('Bluefin.Exception.Exception' () e2)+ -- data Counter2 e1 e2 = MkCounter2 ('Bluefin.State.State' Int e1) ('Bluefin.Capability.Throw.Throw' () e2) -- -- incCounter2 :: (e1 \<: es, e2 \<: es) => Counter2 e1 e2 -> 'Bluefin.Eff.Eff' es () -- incCounter2 (MkCounter2 st ex) = do- -- count <- 'Bluefin.State.get' st+ -- count <- 'Bluefin.Capabiilty.Modify.get' st -- when (count >= 10) $- -- 'Bluefin.Exception.throw' ex ()- -- 'Bluefin.State.put' st (count + 1)+ -- 'Bluefin.Capability.Throw.throw' ex ()+ -- 'Bluefin.Modify.put' st (count + 1) -- -- runCounter2 :: -- (forall e1 e2. Counter2 e1 e2 -> Eff (e2 :& e1 :& es) r) -> -- Eff es Int -- runCounter2 k =- -- 'Bluefin.State.evalState' 0 $ \\st -> do- -- _ \<- 'Bluefin.Exception.try' $ \\ex -> do+ -- 'Bluefin.Modify.evalState' 0 $ \\st -> do+ -- _ \<- 'Bluefin.Capability.Throw.try' $ \\ex -> do -- k (MkCounter2 st ex)- -- 'Bluefin.State.get' st+ -- 'Bluefin.Modify.get' st -- @ -- -- We can see that attempting to increment the counter fovever@@ -88,7 +88,7 @@ -- @ -- -- The flaw of this approach is that you expose one effect- -- parameter for each handle in the data type. That's rather+ -- parameter for each capability in the data type. That's rather -- cumbersome! We can do better. -- ** Wrap multiple effects, a better approach@@ -97,27 +97,27 @@ -- expose a single one. To make this work we have to define our -- handler in a slightly different way. Firstly we apply -- 'useImplIn' to the effectful operation @k@ and secondly we- -- apply 'mapHandle' to each of the handles out of which we create- -- our compound handle. Everything else remains the same.+ -- apply 'mapHandle' to each of the capabiilties out of which we create+ -- our compound capability. Everything else remains the same. -- -- @- -- data Counter3 e = MkCounter3 ('Bluefin.State.State' Int e) ('Bluefin.Exception.Exception' () e)+ -- data Counter3 e = MkCounter3 ('Bluefin.Capability.Modify.Modify' Int e) ('Bluefin.Capability.Throw.Throw' () e) -- -- incCounter3 :: (e \<: es) => Counter3 e -> Eff es () -- incCounter3 (MkCounter3 st ex) = do- -- count <- 'Bluefin.State.get' st+ -- count <- 'Bluefin.Modify.get' st -- when (count >= 10) $- -- 'Bluefin.Exception.throw' ex ()- -- 'Bluefin.State.put' st (count + 1)+ -- 'Bluefin.Capability.Throw.throw' ex ()+ -- 'Bluefin.Modify.put' st (count + 1) -- -- runCounter3 :: -- (forall e. Counter3 e -> Eff (e :& es) r) -> -- Eff es Int -- runCounter3 k =- -- 'Bluefin.State.evalState' 0 $ \\st -> do- -- _ \<- 'Bluefin.Exception.try' $ \\ex -> do+ -- 'Bluefin.Modify.evalState' 0 $ \\st -> do+ -- _ \<- 'Bluefin.Capability.Throw.try' $ \\ex -> do -- 'useImplIn' k (MkCounter3 ('mapHandle' st) (mapHandle ex))- -- 'Bluefin.State.get' st+ -- 'Bluefin.Modify.get' st -- @ -- -- The example works as before:@@ -191,19 +191,19 @@ -- -- @ -- data Counter4 e- -- = MkCounter4 ('Bluefin.State.State' Int e) ('Bluefin.Exception.Exception' () e) ('Bluefin.Stream.Stream' String e)+ -- = MkCounter4 ('Bluefin.Capability.Modify.Modify' Int e) ('Bluefin.Capability.Throw.Throw' () e) ('Bluefin.Stream.Stream' String e) -- -- incCounter4 :: (e \<: es) => Counter4 e -> Eff es () -- incCounter4 (MkCounter4 st ex y) = do- -- count <- 'Bluefin.State.get' st+ -- count <- 'Bluefin.Modify.get' st -- -- when (even count) $ -- 'Bluefin.Stream.yield' y "Count was even" -- -- when (count >= 10) $- -- 'Bluefin.Exception.throw' ex ()+ -- 'Bluefin.Capability.Throw.throw' ex () --- -- 'Bluefin.State.put' st (count + 1)+ -- 'Bluefin.Modify.put' st (count + 1) -- -- getCounter4 :: (e \<: es) => Counter4 e -> String -> Eff es Int -- getCounter4 (MkCounter4 st _ y) msg = do@@ -244,7 +244,7 @@ -- new effects implemented in terms of specific other effects. We -- can also define dynamic effects, whose implementation is left -- abstract, to be defined in the handler. To do that we create a- -- handle that is a record of functions. To run an effectful+ -- capability that is a record of functions. To run an effectful -- operation we call one of the functions from the record. We -- define the record in the handler. Here @incCounter5Impl@ and -- @getCounter5Impl@ are exactly the same as @incCounter4@ and@@ -276,21 +276,21 @@ -- (forall e. Counter5 e -> Eff (e :& es) r) -> -- Eff es Int -- runCounter5 y k =- -- 'Bluefin.State.evalState' 0 $ \\st -> do- -- _ \<- 'Bluefin.Exception.try' $ \\ex -> do+ -- 'Bluefin.Modify.evalState' 0 $ \\st -> do+ -- _ \<- 'Bluefin.Capability.Throw.try' $ \\ex -> do -- 'useImplIn' -- k -- ( MkCounter5 -- { incCounter5Impl = do- -- count <- 'Bluefin.State.get' st+ -- count <- 'Bluefin.Modify.get' st -- -- when (even count) $ -- 'Bluefin.Stream.yield' y "Count was even" -- -- when (count >= 10) $- -- 'Bluefin.Exception.throw' ex ()+ -- 'Bluefin.Capability.Throw.throw' ex () --- -- 'Bluefin.State.put' st (count + 1),+ -- 'Bluefin.Modify.put' st (count + 1), -- getCounter5Impl = \\msg -> do -- yield y msg -- get st@@ -322,12 +322,12 @@ -- | We can also freely combine concrete and dynamic effects. In -- the following example, the @incCounter6@ effect is left -- dynamic, and defined in the handler, whilst @getCounter6@ is- -- implemented in terms of concrete 'Bluefin.State.State' and 'Bluefin.Stream.Stream' effects.+ -- implemented in terms of concrete 'Bluefin.Capability.Modify.Modify' and 'Bluefin.Stream.Stream' effects. -- -- @ -- data Counter6 e = MkCounter6 -- { incCounter6Impl :: 'Bluefin.Eff.Eff' e (),- -- counter6State :: 'Bluefin.State.State' Int e,+ -- counter6State :: 'Bluefin.Capability.Modify.Modify' Int e, -- counter6Stream :: 'Bluefin.Stream.Stream' String e -- } -- deriving (Generic)@@ -350,21 +350,21 @@ -- (forall e. Counter6 e -> Eff (e :& es) r) -> -- Eff es Int -- runCounter6 y k =- -- 'Bluefin.State.evalState' 0 $ \\st -> do- -- _ \<- 'Bluefin.Exception.try' $ \\ex -> do+ -- 'Bluefin.Modify.evalState' 0 $ \\st -> do+ -- _ \<- 'Bluefin.Capability.Throw.try' $ \\ex -> do -- 'useImplIn' -- k -- ( MkCounter6 -- { incCounter6Impl = do- -- count <- 'Bluefin.State.get' st+ -- count <- 'Bluefin.Modify.get' st -- -- when (even count) $ -- 'Bluefin.Stream.yield' y "Count was even" -- -- when (count >= 10) $- -- 'Bluefin.Exception.throw' ex ()+ -- 'Bluefin.Capability.Throw.throw' ex () --- -- 'Bluefin.State.put' st (count + 1),+ -- 'Bluefin.Modify.put' st (count + 1), -- counter6State = mapHandle st, -- counter6Stream = mapHandle y -- }@@ -392,14 +392,14 @@ -- ** Dynamic effects with handles as arguments - -- | We can implement dynamic effects that themselves take handles- -- as arguments, by giving all the handle arguments the effect tag+ -- | We can implement dynamic effects that themselves take capabilities+ -- as arguments, by giving all the capability arguments the effect tag -- @e'@. -- -- @ -- data Counter7 e = MkCounter7- -- { incCounter7Impl :: forall e'. 'Bluefin.Exception.Exception' () e' -> 'Bluefin.Eff.Eff' (e' :& e) (),- -- counter7State :: 'Bluefin.State.State' Int e,+ -- { incCounter7Impl :: forall e'. 'Bluefin.Capability.Throw.Throw' () e' -> 'Bluefin.Eff.Eff' (e' :& e) (),+ -- counter7State :: 'Bluefin.Capability.Modify.Modify' Int e, -- counter7Stream :: 'Bluefin.Stream.Stream' String e -- } -- deriving (Handle) via OneWayCoercibleHandle Counter7@@ -431,21 +431,21 @@ -- (forall e. Counter7 e -> Eff (e :& es) r) -> -- Eff es Int -- runCounter7 y k =- -- 'Bluefin.State.evalState' 0 $ \\st -> do+ -- 'Bluefin.Modify.evalState' 0 $ \\st -> do -- _ \<- -- 'useImplIn' -- k -- ( MkCounter7 -- { incCounter7Impl = \\ex -> do- -- count \<- 'Bluefin.State.get' st+ -- count \<- 'Bluefin.Modify.get' st -- -- when (even count) $ -- 'Bluefin.Stream.yield' y "Count was even" -- -- when (count >= 10) $- -- 'Bluefin.Exception.throw' ex ()+ -- 'Bluefin.Capability.Throw.throw' ex () --- -- 'Bluefin.State.put' st (count + 1),+ -- 'Bluefin.Modify.put' st (count + 1), -- counter7State = mapHandle st, -- counter7Stream = mapHandle y -- }@@ -580,18 +580,18 @@ -- (forall e2. FileSystem e2 -> Eff (e2 :& es) r) -> -- Eff es r -- runFileSystemPure ex fs0 k =- -- 'Bluefin.State.evalState' fs0 $ \\fs ->+ -- 'Bluefin.Modify.evalState' fs0 $ \\fs -> -- 'useImplIn' -- k -- MkFileSystem -- { readFileImpl = \\filepath -> do- -- fs' <- 'Bluefin.State.get' fs+ -- fs' <- 'Bluefin.Modify.get' fs -- case lookup filepath fs' of -- Nothing ->- -- 'Bluefin.Exception.throw' ex ("File not found: " <> filepath)+ -- 'Bluefin.Capability.Throw.throw' ex ("File not found: " <> filepath) -- Just s -> pure s, -- writeFileImpl = \\filepath contents ->- -- 'Bluefin.State.modify' fs ((filepath, contents) :)+ -- 'Bluefin.Modify.modify' fs ((filepath, contents) :) -- } -- @ --@@ -619,7 +619,7 @@ -- adapt :: (e1 \<: ess, e2 \<: ess) => IO a -> Eff ess a -- adapt m = -- effIO io (Control.Exception.try @IOException m) >>= \\case- -- Left e -> 'Bluefin.Exception.throw' ex (show e)+ -- Left e -> 'Bluefin.Capability.Throw.throw' ex (show e) -- Right r -> pure r -- @ --@@ -639,7 +639,7 @@ -- -- @ -- exampleRunFileSystemPure :: Either String String- -- exampleRunFileSystemPure = 'Bluefin.Eff.runPureEff' $ 'Bluefin.Exception.try' $ \\ex ->+ -- exampleRunFileSystemPure = 'Bluefin.Eff.runPureEff' $ 'Bluefin.Capability.Throw.try' $ \\ex -> -- runFileSystemPure ex [("\/dev\/null", "")] action -- @ --
src/Bluefin/Consume.hs view
@@ -1,3 +1,5 @@+-- | This is an old interface and will be deprecated in the+-- future. You are encouraged to use "Bluefin.Capability.Await" instead. module Bluefin.Consume ( -- | 'Consume' allows you to await values during the execution of -- a Bluefin operation. It provides similar functionality to
src/Bluefin/Coroutine.hs view
@@ -1,3 +1,6 @@+-- | This is an old interface and will be deprecated in the+-- future. You are encouraged to use "Bluefin.Capability.Request"+-- instead. module Bluefin.Coroutine ( -- | @Coroutine@ allows to yield values and receive results back. -- [Wikipedia
src/Bluefin/DslBuilder.hs view
@@ -177,7 +177,7 @@ -- @ -- -- @DslBuilder ArenaH@ is a @Monad@ that allows us access to the- -- effects inside the handle @ArenaH@ (and no others). So what is+ -- effects inside the capability @ArenaH@ (and no others). So what is -- @ArenaH@? It is defined like this: -- -- @@@ -241,7 +241,7 @@ -- @ -- -- Like with @ArenaBuilder@, to define the @Monad@ we define a- -- handle, this time @InstructionsH@:+ -- capability, this time @InstructionsH@: -- -- @ -- data InstructionsH e = MkInstructionsH ('Bluefin.Stream.Stream' Instruction e)@@ -332,7 +332,6 @@ -- @myDslArena :: Arena@ given above! -- * @DslBuilder@- DslBuilder, dslBuilder, runDslBuilder,
src/Bluefin/DslBuilderEff.hs view
@@ -1,8 +1,7 @@ -- | Like "Bluefin.DslBuilder", but when you want to be able to run -- additional effects as well.--module Bluefin.DslBuilderEff (- DslBuilderEff,+module Bluefin.DslBuilderEff+ ( DslBuilderEff, dslBuilderEff, runDslBuilderEff, )
src/Bluefin/EarlyReturn.hs view
@@ -1,3 +1,6 @@+-- | This is an old interface and will be deprecated in the+-- future. You are encouraged to use "Bluefin.Capability.ReturnEarly"+-- instead. module Bluefin.EarlyReturn ( -- | Early return allows to define a block from which you can -- return early. Early return is implemented as an exception, and
src/Bluefin/Exception.hs view
@@ -1,3 +1,6 @@+-- | This is an old interface and will be deprecated in the+-- future. You are encouraged to use "Bluefin.Capability.Throw"+-- instead. module Bluefin.Exception ( -- * Handle Exception,
src/Bluefin/GadtEffect.hs view
@@ -6,7 +6,7 @@ -- algebraic data type) whose contructors correspond to primitive -- operations of the effect, and then creating values of the GADT -- and interpreting them in terms of existing effects. This- -- module provides Bluefin's equivalent. In fact, it @effectful@+ -- module provides Bluefin's equivalent. In fact, in @effectful@ -- and @polysemy@ this is essentially the /only/ way you can -- create new effects. That's not true for Bluefin. Bluefin -- supports a rich collection of ways to create new effects, most@@ -228,7 +228,6 @@ -- @ -- * Handle- Send, -- * Effectful operations
src/Bluefin/HandleReader.hs view
@@ -1,7 +1,11 @@--- | 'HandleReader' is like t'Bluefin.Reader.Reader', generalized to+-- | This is an old interface and will be deprecated in the+-- future. You are encouraged to use+-- "Bluefin.Capability.AskCapability" instead.+--+-- 'HandleReader' is like t'Bluefin.Reader.Reader', generalized to -- work for arbitrary t'Bluefin.Compound.Handle's. 'localHandle'--- locally overrides the value of a handle in a well-scoped way. The--- original handle will be restored when you exit the @localHandle@+-- locally overrides the value of a capability in a well-scoped way. The+-- original capability will be restored when you exit the @localHandle@ -- block regardless of whether the exit was normal or via an -- exception. --@@ -11,8 +15,7 @@ -- [@intercept@](https://hackage.haskell.org/package/polysemy/docs/Polysemy.html#v:intercept), -- that is, locally augmenting an effect with new behaviors. If you -- want to do the same in Bluefin you may want to start with--- @Bluefin.FunctorCoroutine.'Bluefin.FunctorCoroutine.interpose`@.-+-- @Bluefin.GadtEffect.'Bluefin.GadtEffect.interpose`@. module Bluefin.HandleReader ( -- * Handle HandleReader,
src/Bluefin/IO.hs view
@@ -1,7 +1,7 @@ module Bluefin.IO ( -- | You can run 'IO' operations inside 'Eff'. - -- * Handle+ -- * Capability IOE, -- * Handlers
src/Bluefin/Jump.hs view
@@ -1,3 +1,5 @@+-- | This is an old interface and will be deprecated in the+-- future. You are encouraged to use "Bluefin.Capability.Jump" instead. module Bluefin.Jump ( -- | 'Jump' allows you to jump back to a previously-set location. -- A "jump" is equivalent to an untyped early return, or more
src/Bluefin/Pipes.hs view
@@ -9,12 +9,12 @@ -- if you want it. Instead of using it directly you are recommended -- to use ----- * 'Bluefin.Stream', 'Bluefin.Stream.yield'--- * 'Bluefin.Consume', 'Bluefin.Consume.await'--- * 'Bluefin.Stream.consumeStream'--- * For advanced cases only, 'Bluefin.Coroutine',--- 'Bluefin.Coroutine.yieldCoroutine' and--- 'Bluefin.Coroutine.connectCoroutines'+-- * t'Bluefin.Capability.Yield.Yield', 'Bluefin.Capability.Yield.yield'+-- * t'Bluefin.Capability.Await.Await', 'Bluefin.Capability.Await.await'+-- * 'Bluefin.Capability.Yield.awaitYield'+-- * For advanced cases only, t'Bluefin.Capability.Request.Request',+-- 'Bluefin.Capability.Request.request' and+-- 'Bluefin.Capability.Request.connectRequests' -- -- See also "Bluefin.Pipes.Prelude". module Bluefin.Pipes
src/Bluefin/Pipes/Prelude.hs view
@@ -9,12 +9,12 @@ -- if you want it. Instead of using it directly you are recommended -- to use ----- * 'Bluefin.Stream', 'Bluefin.Stream.yield'--- * 'Bluefin.Consume', 'Bluefin.Consume.await'--- * 'Bluefin.Stream.consumeStream'--- * For advanced cases only, 'Bluefin.Coroutine',--- 'Bluefin.Coroutine.yieldCoroutine' and--- 'Bluefin.Coroutine.connectCoroutines'+-- * t'Bluefin.Capability.Yield.Yield', 'Bluefin.Capability.Yield.yield'+-- * t'Bluefin.Capability.Await.Await', 'Bluefin.Capability.Await.await'+-- * 'Bluefin.Capability.Yield.awaitYield'+-- * For advanced cases only, t'Bluefin.Capability.Request.Request',+-- 'Bluefin.Capability.Request.request' and+-- 'Bluefin.Capability.Request.connectRequests' -- -- See also "Bluefin.Pipes". --
src/Bluefin/Prim.hs view
@@ -1,7 +1,7 @@ -- | For defining @PrimMonad@ instances, for example: -- -- @--- -- Define a handle which includes Prim+-- -- Define a capability which includes Prim -- data ExAndPrim e = MkExAndPrim (Exception String e) (P.Prim e) -- -- Give it a Handle instance, as per Bluefin.Compound -- deriving (Handle) via OneWayCoercibleHandle ExAndPrim@@ -10,7 +10,7 @@ -- instance (e \<: es) => OneWayCoercible (ExAndPrim e) (ExAndPrim es) where -- oneWayCoercibleImpl = gOneWayCoercible ----- -- Define a monad M containing the Prim handle+-- -- Define a monad M containing the Prim capability -- newtype M e es a = MkM (ReaderT (ExAndPrim e) (Eff es) a) -- deriving newtype (Functor, Applicative, Monad) --
src/Bluefin/Reader.hs view
@@ -1,7 +1,9 @@+-- | This is an old interface and will be deprecated in the+-- future. You are encouraged to use "Bluefin.Capability.Ask" instead. module Bluefin.Reader ( -- | 'Reader' is Bluefin's version of the -- "Control.Monad.Trans.Reader" monad. 'local' allows you to- -- locally override the value in the @Reader@ handle in a+ -- locally override the value in the @Reader@ capability in a -- well-scoped way. The original value will be restored when you -- exit the @local@ block regardless of whether the exit was -- normal or via an exception .
src/Bluefin/State.hs view
@@ -1,3 +1,6 @@+-- | This is an old interface and will be deprecated in the+-- future. You are encouraged to use "Bluefin.Capability.Modify"+-- instead. module Bluefin.State ( -- * Handle State,
src/Bluefin/Stream.hs view
@@ -1,9 +1,12 @@+-- | This is an old interface and will be deprecated in the+-- future. You are encouraged to use "Bluefin.Capability.Yield"+-- instead. module Bluefin.Stream ( -- | 'Stream' allows you to yield values during the execution of a -- Bluefin operation. It provides similar functionality to -- Python's @yield@. The handler of the 'Stream' will either -- handle each element as soon as it is yielded (for example- -- 'forEach') or gather all yielded elements int o a list (for+ -- 'forEach') or gather all yielded elements into a list (for -- example 'yieldToList'). -- -- For information about prompt finalization/resource safety when
src/Bluefin/Writer.hs view
@@ -1,3 +1,6 @@+-- | This is an old interface and will be deprecated in the+-- future. You are encouraged to use "Bluefin.Capability.Writer"+-- instead. module Bluefin.Writer ( -- | In most cases you'll probably prefer t'Bluefin.Stream.Stream' -- to @Writer@, but @Writer@ can still be useful in some cases,