packages feed

bluefin 0.4.2.0 → 0.4.3.0

raw patch · 4 files changed

+273/−5 lines, 4 filesdep ~bluefin-internalPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: bluefin-internal

API changes (from Hackage documentation)

+ Bluefin.GadtEffect: data GadtEffect (f :: Type -> Type -> Type -> Type) a (e :: Effects)
+ Bluefin.GadtEffect: data Send (f :: Effect) (e :: Effects)
+ Bluefin.GadtEffect: interpose :: forall (e1 :: Effects) (es :: Effects) (f :: Effect) r. e1 :> es => (Send f es -> EffectHandler f es) -> HandleReader (Send f) e1 -> Eff es r -> Eff es r
+ Bluefin.GadtEffect: interpret :: forall (f :: (Type -> Type) -> Type -> Type) (es :: Effects) r. EffectHandler f es -> (forall (e :: Effects). () => Send f e -> Eff (e :& es) r) -> Eff es r
+ Bluefin.GadtEffect: oneWayCoercibleGadtEffectTrustMe :: forall (e :: Effects) (es :: Effects) f r. e :> es => (forall (e' :: Effects) (es' :: Effects). e' :> es' => f (Eff e') r -> f (Eff es') r) -> OneWayCoercibleD (GadtEffect f r e) (GadtEffect f r es)
+ Bluefin.GadtEffect: passthrough :: forall f r (e1 :: Effects) (es :: Effects) (e2 :: Effects). (Handle (GadtEffect f r), e1 :> es, e2 :> es) => Send f e1 -> f (Eff e2) r -> Eff es r
+ Bluefin.GadtEffect: send :: forall (e1 :: Effects) (es :: Effects) f r. e1 :> es => Send f e1 -> f (Eff es) r -> Eff es r
+ Bluefin.GadtEffect: type Effect = Type -> Type -> Type -> Type
+ Bluefin.GadtEffect: type EffectHandler (f :: Type -> Type -> Type -> Type) (es :: Effects) = forall (e :: Effects) r. () => f Eff e r -> Eff e :& es r
+ Bluefin.HandleReader: asksHandle :: forall (e1 :: Effects) (es :: Effects) h r. (e1 :> es, Handle h) => HandleReader h e1 -> (forall (e :: Effects). () => h e -> Eff (e :& es) r) -> Eff es r
- Bluefin.Prim: data PrimStateEff (es :: k)
+ Bluefin.Prim: data PrimStateEff (es :: Effects)

Files

CHANGELOG.md view
@@ -1,3 +1,14 @@+# 0.4.3.0++* Add `Bluefin.GadtEffect`++* Add `Bluefin.HandleReader.asksHandle`++* Restrict type parameter of `PrimStateEff` to `Effects`++  This is technically a breaking change, but we did not enforce a+  major version bump for it.+ # 0.4.2.0  * Add `Bluefin.DslBuilderEff`
bluefin.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               bluefin-version:            0.4.2.0+version:            0.4.3.0 license:            MIT license-file:       LICENSE author:             Tom Ellis@@ -31,6 +31,7 @@       Bluefin.Eff,       Bluefin.Exception,       Bluefin.Exception.GeneralBracket,+      Bluefin.GadtEffect,       Bluefin.HandleReader,       Bluefin.IO,       Bluefin.Jump,@@ -44,6 +45,6 @@       Bluefin.System.IO,       Bluefin.Writer,     build-depends:-      bluefin-internal >= 0.4.1.0 && < 0.5+      bluefin-internal >= 0.4.3.0 && < 0.5     hs-source-dirs:   src     default-language: Haskell2010
+ src/Bluefin/GadtEffect.hs view
@@ -0,0 +1,252 @@+module Bluefin.GadtEffect+  ( -- * Introduction++    -- | The Haskell effect systems @effectful@ and @polysemy@ allow+    -- users to define new effects by defining a GADT (generalized+    -- 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@+    -- 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+    -- of which are documented at "Bluefin.Compound".  This particular+    -- module might be helpful for users coming from @effectful@ and+    -- @polysemy@, however.++    -- * Example filesystem effect++    -- | First we define a GADT with a constructor for each primitive+    -- operation of the effect we want to define.  Here the primitive+    -- operations are to read a file, write a file and to wrap an+    -- effectful computation in a "trace" block.+    --+    -- @+    -- data FileSystem :: 'Effect' where+    --   ReadFile :: FilePath -> FileSystem m String+    --   WriteFile :: FilePath -> String -> FileSystem m ()+    --   Trace :: String -> m r -> FileSystem m r+    -- @+    --+    -- Then we need to define two instances for @FileSystem@:+    --+    -- @+    -- instance+    --   (e :> es) =>+    --   t'Bluefin.Compound.OneWayCoercible' ('GadtEffect' FileSystem r e) (GadtEffect FileSystem r es)+    --   where+    --   'Bluefin.Compound.oneWayCoercibleImpl' = 'oneWayCoercibleGadtEffectTrustMe' $ \\case+    --     ReadFile path -> ReadFile path+    --     WriteFile path contents -> WriteFile path contents+    --     Trace msg body -> Trace msg (useImpl body)+    --+    -- deriving via+    --   t'Bluefin.Compound.OneWayCoercibleHandle' ('GadtEffect' FileSystem r)+    --   instance+    --     t'Bluefin.Compound.Handle' (GadtEffect FileSystem r)+    -- @+    --+    -- Then we can define functions that implement the primitive+    -- effectful operations for @FileSystem@:+    --+    -- @+    -- readFile ::+    --   (e1 :> es) =>+    --   'Send' FileSystem e1 ->+    --   FilePath ->+    --   Eff es String+    -- readFile fc path =+    --   'send' fc (ReadFile path)+    --+    -- writeFile ::+    --   (e1 :> es) =>+    --   Send FileSystem e1 ->+    --   FilePath ->+    --   String ->+    --   Eff es ()+    -- writeFile fc path content =+    --   send fc (WriteFile path content)+    --+    -- trace ::+    --   (e1 :> es) =>+    --   Send FileSystem e1 ->+    --   String ->+    --   Eff es r ->+    --   Eff es r+    -- trace fc msg body =+    --   send fc (Trace msg body)+    -- @+    --+    -- The instances and primitive effectful operations are+    -- boilerplate.  @effectful@ and @polysemy@ have Template Haskell+    -- for generating their boilerplate+    -- ([@makeEffect@](https://hackage.haskell.org/package/effectful-th/docs/Effectful-TH.html#v:makeEffect)+    -- and+    -- [@makeSem@](https://hackage.haskell.org/package/polysemy-1.9.2.0/docs/Polysemy.html#v:makeSem)+    -- respectively) but there is no such thing for Bluefin yet,+    -- sorry!  Please [open an+    -- issue](https://github.com/tomjaguarpaw/bluefin/issues/new) if+    -- that causes difficulties for you.+    --+    -- Finally we can write a handler for the @'Send' FileSystem@+    -- effect that gives it an interpretation via 'interpret':+    --+    -- @+    -- import System.IO qualified as IO+    --+    -- runFileSystem ::+    --   forall es e1 e2 r.+    --   (e1 :> es, e2 :> es) =>+    --   t'Bluefin.IO.IOE' e1 ->+    --   t'Bluefin.Exception.Exception' t'Control.Exception.IOException' e2 ->+    --   (forall e. 'Send' FileSystem e -> Eff (e :& es) r) ->+    --   Eff es r+    -- runFileSystem io ex = 'interpret' $ \\case+    --   ReadFile path ->+    --     adapt (IO.'System.IO.readFile' path)+    --   WriteFile path contents ->+    --     adapt (IO.'System.IO.writeFile' path contents)+    --   Trace msg body -> do+    --     'Bluefin.IO.effIO' io (putStrLn ("Start: " <> msg))+    --     r <- 'Bluefin.Compound.useImpl' body+    --     effIO io (putStrLn ("End: " <> msg))+    --     pure r+    --   where+    --     -- If you don't want to write this signature you can use+    --     -- {-# LANGUAGE NoMonoLocalBinds #-}+    --     adapt :: (e1 :> es', e2 :> es') => IO r' -> Eff es' r'+    --     adapt m = 'Bluefin.IO.rethrowIO' io ex (effIO io m)+    -- @++    -- * @interpose@ example++    -- | If you're familiar with @effectful@'s @interpose@ function+    -- you may want to use Bluefin's equivalent.  To see how, let's+    -- replicate [@effectful@'s interpose+    -- example](https://hackage-content.haskell.org/package/effectful-core-2.6.1.0/docs/Effectful-Dispatch-Dynamic.html#v:interpose). First+    -- we define a simple effect with three primitive operations:+    --+    -- @+    -- data E :: 'Effect' where+    --   Op1 :: E m ()+    --   Op2 :: E m ()+    --   Op3 :: E m ()+    -- @+    --+    -- Then we define the boilerplate instances+    --+    -- @+    -- instance+    --   (e :> es) =>+    --   t'Bluefin.Compound.OneWayCoercible' ('GadtEffect' E r e) (GadtEffect E r es)+    --   where+    --   'Bluefin.Compound.oneWayCoercibleImpl' = 'oneWayCoercibleGadtEffectTrustMe' $ \\case+    --     Op1 -> Op1+    --     Op2 -> Op2+    --     Op3 -> Op3+    --+    -- deriving via+    --   t'Bluefin.Compound.OneWayCoercibleHandle' (GadtEffect E r)+    --   instance+    --     t'Bluefin.Compound.Handle' (GadtEffect E r)+    -- @+    --+    -- and a handler for the @'Send' E@ effect:+    --+    -- @+    -- runE ::+    --   (e1 :> es) =>+    --   IOE e1 ->+    --   (forall e. Send E e -> Eff (e :& es) r) ->+    --   Eff es r+    -- runE io = interpret $ \\case+    --   Op1 -> effIO io (putStrLn "op1")+    --   Op2 -> effIO io (putStrLn "op2")+    --   Op3 -> effIO io (putStrLn "op3")+    -- @+    --+    -- Before using 'interpose', let's look at a use of its simpler+    -- cousin, 'interpret':+    --+    -- @+    -- augmentOp2Interpret ::+    --   (e1 :> es, e2 :> es) =>+    --   IOE e2 ->+    --   Send E e1 ->+    --   (forall e. Send E e -> Eff (e :& es) r) ->+    --   Eff es r+    -- augmentOp2Interpret io fc = 'interpret' $ \\case+    --   Op2 -> effIO io (putStrLn "augmented op2") >> send fc Op2+    --   op -> 'passthrough' fc op+    -- @+    --+    -- Using 'interpose' is similar:+    --+    -- @+    -- augmentOp2Interpose ::+    --   (e1 :> es, e2 :> es) =>+    --   IOE e2 ->+    --   t'Bluefin.HandleReader.HandleReader' (Send E) e1 ->+    --   Eff es r ->+    --   Eff es r+    -- augmentOp2Interpose io = 'interpose' $ \\fc -> \\case+    --   Op2 -> effIO io (putStrLn "augmented op2") >> send fc Op2+    --   op -> 'passthrough' fc op+    -- @+    --+    -- And now let's see what they each do:+    --+    -- @+    -- example :: IO ()+    -- example = runEff $ \\io -> do+    --   let action fc = do+    --         send fc Op1+    --         send fc Op2+    --         send fc Op3+    --+    --   effIO io (putStrLn "-- interpret:")+    --   runE io $ \\fc -> do+    --     augmentOp2Interpret io fc $ \\fc' -> action fc'+    --+    --   effIO io (putStrLn "-- interpose:")+    --   runE io $ \\fc -> 'Bluefin.HandleReader.runHandleReader' fc $ \\hr -> do+    --     augmentOp2Interpose io hr $ 'Bluefin.HandleReader.asksHandle' hr action+    -- @+    --+    -- @+    -- ghci> example+    -- -- interpret:+    -- op1+    -- augmented op2+    -- op2+    -- op3+    -- -- interpose:+    -- op1+    -- augmented op2+    -- op2+    -- op3+    -- @++    -- * Handle++    Send,++    -- * Effectful operations+    send,+    passthrough,++    -- * Interpretation+    EffectHandler,+    interpret,+    interpose,++    -- * @Effect@+    Effect,++    -- * @GadtEffect@+    GadtEffect,+    oneWayCoercibleGadtEffectTrustMe,+  )+where++import Bluefin.Internal.GadtEffect
src/Bluefin/HandleReader.hs view
@@ -6,9 +6,12 @@ -- exception. -- -- @HandleReader@ supports functionality similiar to @effectful@'s--- 'Effectful.Dispatch.Dynamic.interpose' and @polysemy@'s--- 'Polysemy.intercept', that is, locally augmenting an effect with--- new behaviors.+-- [@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.FunctorCoroutine.'Bluefin.FunctorCoroutine.interpose`@.  module Bluefin.HandleReader   ( -- * Handle@@ -19,6 +22,7 @@      -- * Effectful operations     askHandle,+    asksHandle,     localHandle,   ) where