on master help sign in
Add support for GADTs
005c8c4Andrzej Rybczak
bench/Countdown.hs+2−2
@@ -9,11 +9,11 @@ import qualified Control.Effect as L
9#endif
9#endif
10
10
11-- effectful
11-- effectful
12−import qualified Effectful as E
12+import qualified Effectful.Monad as E
13import qualified Effectful.Reader as E
13import qualified Effectful.Reader as E
14import qualified Effectful.State as E
14import qualified Effectful.State as E
15−import qualified Effectful.State.MVar as ME
16import qualified Effectful.State.Dynamic as DE
15import qualified Effectful.State.Dynamic as DE
16+import qualified Effectful.State.MVar as ME
17
17
18-- freer-simple
18-- freer-simple
19#ifdef VERSION_freer_simple
19#ifdef VERSION_freer_simple
bench/Main.hs+36−29
@@ -1,54 +1,61 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2module Main (main) where
2module Main (main) where
3
3
4+#ifdef VERSION_criterion
5+import Criterion
6+import Criterion.Main
7+#endif
8+
9+#ifdef VERSION_tasty_bench
4import Test.Tasty.Bench
10import Test.Tasty.Bench
11+#endif
5
12
6import Countdown
13import Countdown
7
14
8main :: IO ()
15main :: IO ()
9main = defaultMain
16main = defaultMain
10− [ bgroup "countdown"
17+ [ bgroup "countdown" $ map countdown [1000, 10000]
18+ ]
19+
20+countdown :: Integer -> Benchmark
21+countdown n = bgroup (show n)
22+ [ bgroup "shallow"
11− [ bgroup "reference" $ mkCountdown nf countdownRef
23+ [ bench "reference" $ nf countdownRef n
12− , bgroup "shallow"
13− [ bgroup "efectful (SP)" $ mkCountdown nfAppIO countdownEffectfulStatic
24+ , bench "effectful (PS)" $ nfAppIO countdownEffectfulStatic n
25+ , bench "effectful (PD)" $ nfAppIO countdownEffectfulDynPure n
14− , bgroup "efectful (SM)" $ mkCountdown nfAppIO countdownEffectfulMVar
26+ , bench "effectful (MS)" $ nfAppIO countdownEffectfulMVar n
15− , bgroup "efectful (DP)" $ mkCountdown nfAppIO countdownEffectfulDynPure
16− , bgroup "efectful (DM)" $ mkCountdown nfAppIO countdownEffectfulDynMVar
27+ , bench "effectful (MD)" $ nfAppIO countdownEffectfulDynMVar n
17#ifdef VERSION_freer_simple
28#ifdef VERSION_freer_simple
18− , bgroup "freer-simple" $ mkCountdown nf countdownFreerSimple
29+ , bench "freer-simple" $ nf countdownFreerSimple n
19#endif
30#endif
20#ifdef VERSION_eff
31#ifdef VERSION_eff
21− , bgroup "eff" $ mkCountdown nf countdownEff
32+ , bench "eff" $ nf countdownEff n
22#endif
33#endif
23− , bgroup "mtl" $ mkCountdown nf countdownMtl
34+ , bench "mtl" $ nf countdownMtl n
24#ifdef VERSION_fused_effects
35#ifdef VERSION_fused_effects
25− , bgroup "fused-effects" $ mkCountdown nf countdownFusedEffects
36+ , bench "fused-effects" $ nf countdownFusedEffects n
26#endif
37#endif
27#ifdef VERSION_polysemy
38#ifdef VERSION_polysemy
28− , bgroup "polysemy" $ mkCountdown nf countdownPolysemy
39+ , bench "polysemy" $ nf countdownPolysemy n
29−#endif
30− ]
31− , bgroup "deep"
32− [ bgroup "efectful (SP)" $ mkCountdown nfAppIO countdownEffectfulStaticDeep
33− , bgroup "efectful (SM)" $ mkCountdown nfAppIO countdownEffectfulMVarDeep
34− , bgroup "efectful (DP)" $ mkCountdown nfAppIO countdownEffectfulDynPureDeep
35− , bgroup "efectful (DM)" $ mkCountdown nfAppIO countdownEffectfulDynMVarDeep
36−#ifdef VERSION_eff
37− , bgroup "eff" $ mkCountdown nf countdownEffDeep
38#endif
40#endif
41+ ]
42+ , bgroup "deep"
43+ [ bench "effectful (PS)" $ nfAppIO countdownEffectfulStaticDeep n
44+ , bench "effectful (PD)" $ nfAppIO countdownEffectfulDynPureDeep n
45+ , bench "effectful (MS)" $ nfAppIO countdownEffectfulMVarDeep n
46+ , bench "effectful (MD)" $ nfAppIO countdownEffectfulDynMVarDeep n
39#ifdef VERSION_freer_simple
47#ifdef VERSION_freer_simple
40− , bgroup "freer-simple" $ mkCountdown nf countdownFreerSimpleDeep
48+ , bench "freer-simple" $ nf countdownFreerSimpleDeep n
49+#endif
50+#ifdef VERSION_eff
51+ , bench "eff" $ nf countdownEffDeep n
41#endif
52#endif
42#ifdef VERSION_polysemy
53#ifdef VERSION_polysemy
43− , bgroup "polysemy" $ mkCountdown nf countdownPolysemyDeep
54+ , bench "polysemy" $ nf countdownPolysemyDeep n
44#endif
55#endif
45− , bgroup "mtl" $ mkCountdown nf countdownMtlDeep
56+ , bench "mtl" $ nf countdownMtlDeep n
46#ifdef VERSION_fused_effects
57#ifdef VERSION_fused_effects
47− , bgroup "fused-effects" $ mkCountdown nf countdownFusedEffectsDeep
58+ , bench "fused-effects" $ nf countdownFusedEffectsDeep n
48#endif
59#endif
49− ]
50 ]
60 ]
51 ]
61 ]
52−
53−mkCountdown :: (nf -> Integer -> Benchmarkable) -> nf -> [Benchmark]
54−mkCountdown f g = map (\n -> bench (show n) $ f g n) [100, 1000, 10000]
effectful.cabal+6−11
@@ -15,8 +15,7 @@ description: A simple, performant extensible effects library with seamless
15extra-source-files: CHANGELOG.md
15extra-source-files: CHANGELOG.md
16 README.md
16 README.md
17
17
18−tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.4
18+tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.4 || ==9.0.1 || ==9.2.0.20210422
19− || ==9.0.1 || ==9.2.0.20210422
20
19
21bug-reports: https://github.com/arybczak/effectful/issues
20bug-reports: https://github.com/arybczak/effectful/issues
22source-repository head
21source-repository head
@@ -31,14 +30,10 @@ common language
31 default-extensions: BangPatterns
30 default-extensions: BangPatterns
32 ConstraintKinds
31 ConstraintKinds
33 DataKinds
32 DataKinds
34− DefaultSignatures
35− DeriveFunctor
36 FlexibleContexts
33 FlexibleContexts
37 FlexibleInstances
34 FlexibleInstances
38 GADTs
35 GADTs
39− GeneralizedNewtypeDeriving
40 LambdaCase
36 LambdaCase
41− NamedFieldPuns
42 MultiParamTypeClasses
37 MultiParamTypeClasses
43 RankNTypes
38 RankNTypes
44 RecordWildCards
39 RecordWildCards
@@ -47,14 +42,13 @@ common language
47 StrictData
42 StrictData
48 TupleSections
43 TupleSections
49 TypeApplications
44 TypeApplications
50− TypeInType
51 TypeFamilies
45 TypeFamilies
52 TypeOperators
46 TypeOperators
53
47
54library
48library
55 import: language
49 import: language
56
50
57− build-depends: base <5
51+ build-depends: base >= 4.12 && <5
58 , containers
52 , containers
59 , exceptions
53 , exceptions
60 , ghc-prim
54 , ghc-prim
@@ -67,15 +61,16 @@ library
67
61
68 hs-source-dirs: src
62 hs-source-dirs: src
69
63
70− exposed-modules: Effectful
64+ exposed-modules: Effectful.Class.Reader
71− Effectful.Class.Reader
72 Effectful.Class.State
65 Effectful.Class.State
73 Effectful.Class.Writer
66 Effectful.Class.Writer
74 Effectful.Error
67 Effectful.Error
68+ Effectful.Internal.Effect
75 Effectful.Internal.Env
69 Effectful.Internal.Env
76− Effectful.Internal.Has
77 Effectful.Internal.Monad
70 Effectful.Internal.Monad
78 Effectful.Internal.Utils
71 Effectful.Internal.Utils
72+ Effectful.Interpreter
73+ Effectful.Monad
79 Effectful.Reader
74 Effectful.Reader
80 Effectful.Resource
75 Effectful.Resource
81 Effectful.State
76 Effectful.State
examples/FileSystem.hs+34−32
@@ -1,5 +1,5 @@
1module FileSystem
1module FileSystem
2− ( FileSystem
2+ ( FileSystem(..)
3 , FsError(..)
3 , FsError(..)
4 , readFile
4 , readFile
5 , writeFile
5 , writeFile
@@ -7,23 +7,24 @@ module FileSystem
7 , runFileSystemPure
7 , runFileSystemPure
8 ) where
8 ) where
9
9
10+import Control.Exception (IOException)
10import Control.Monad.Catch
11import Control.Monad.Catch
12+import Control.Monad.IO.Class
11import Prelude hiding (readFile, writeFile)
13import Prelude hiding (readFile, writeFile)
12−import qualified Control.Exception as E
13import qualified Data.Map.Strict as M
14import qualified Data.Map.Strict as M
14import qualified System.IO as IO
15import qualified System.IO as IO
15
16
16−import Effectful
17import Effectful.Error
17import Effectful.Error
18+import Effectful.Interpreter
19+import Effectful.Monad
20+import Effectful.State
18
21
19−-- | Effect for reading and writing files.
22+-- | An effect for reading and writing files.
20−data FileSystem = forall fs. FileSystem
23+data FileSystem :: Effect where
21− { _fs :: fs
24+ ReadFile :: FilePath -> FileSystem m String
22− , _readFile :: forall es. Error FsError :> es => fs -> FilePath -> Eff es String
25+ WriteFile :: FilePath -> String -> FileSystem m ()
23− , _writeFile :: forall es. Error FsError :> es => fs -> FilePath -> String -> Eff es fs
24− }
25
26
26−-- | File system error.
27+--- | File system error.
27newtype FsError = FsError String
28newtype FsError = FsError String
28 deriving Show
29 deriving Show
29instance Exception FsError
30instance Exception FsError
@@ -32,32 +33,33 @@ instance Exception FsError
32-- Operations
33-- Operations
33
34
34-- | Read contents of a file.
35-- | Read contents of a file.
35−readFile :: (FileSystem :> es, Error FsError :> es) => FilePath -> Eff es String
36+readFile :: FileSystem :> es => FilePath -> Eff es String
36−readFile path = readerEffectM $ \FileSystem{..} -> _readFile _fs path
37+readFile = send . ReadFile
37
38
38-- | Write contents to a file.
39-- | Write contents to a file.
39−writeFile :: (FileSystem :> es, Error FsError :> es) => FilePath -> String -> Eff es ()
40+writeFile :: FileSystem :> es => FilePath -> String -> Eff es ()
40−writeFile path contents = stateEffectM $ \FileSystem{..} -> do
41+writeFile path = send . WriteFile path
41− fs <- _writeFile _fs path contents
42− pure ((), FileSystem { _fs = fs, .. })
43
42
44----------------------------------------
43----------------------------------------
45-- Handlers
44-- Handlers
46
45
46+runFileSystemIO
47+ :: (IOE :> es, Error FsError :> es)
47−runFileSystemIO :: Eff (FileSystem : es) a -> Eff es a
48+ => Eff (FileSystem : es) a
49+ -> Eff es a
48−runFileSystemIO = evalEffect $ FileSystem
50+runFileSystemIO = interpret $ \case
49− { _fs = ()
50− , _readFile = \() path -> wrap $ IO.readFile path
51+ ReadFile path -> adapt $ IO.readFile path
51− , _writeFile = \() path contents -> wrap $ IO.writeFile path contents
52+ WriteFile path contents -> adapt $ IO.writeFile path contents
52− }
53 where
53 where
54− wrap m = impureEff_ m `catch` \(e::E.IOException) -> throwError . FsError $ show e
54+ adapt m = liftIO m `catch` \(e::IOException) -> throwError . FsError $ show e
55−
55+
56+runFileSystemPure
57+ :: Error FsError :> es
56−runFileSystemPure :: M.Map FilePath String -> Eff (FileSystem : es) a -> Eff es a
58+ => M.Map FilePath String
59+ -> Eff (FileSystem : es) a
60+ -> Eff es a
57−runFileSystemPure fs0 = evalEffect $ FileSystem
61+runFileSystemPure fs0 = reinterpret (evalState fs0) $ \case
58− { _fs = fs0
62+ ReadFile path -> gets (M.lookup path) >>= \case
59− , _readFile = \fs path -> case path `M.lookup` fs of
60− Just contents -> pure contents
63+ Just contents -> pure contents
61− Nothing -> throwError . FsError $ "File not found: " ++ show path
64+ Nothing -> throwError . FsError $ "File not found: " ++ show path
62− , _writeFile = \fs path contents -> pure $ M.insert path contents fs
65+ WriteFile path contents -> modify $ M.insert path contents
63− }
src/Effectful.hsdeleted−34
@@ -1,34 +0,0 @@
1−module Effectful
2− ( -- * The 'Eff' monad
3− Eff
4− , runEff
5− , Effect
6− , (:>)
7−
8− -- * The IO effect
9− , IOE
10− , runIOE
11−
12− -- * Execution of IO in effect handlers
13− , impureEff
14− , impureEff_
15−
16− -- * Helpers for defining new effects
17−
18− -- ** Running
19− , runEffect
20− , evalEffect
21− , execEffect
22−
23− -- ** Retrieval and modification
24− , getEffect
25− , putEffect
26− , stateEffect
27− , localEffect
28− , listenEffect
29− , readerEffectM
30− , stateEffectM
31− ) where
32−
33−import Effectful.Internal.Has
34−import Effectful.Internal.Monad
src/Effectful/Class/Reader.hs+1−1
@@ -7,7 +7,7 @@ module Effectful.Class.Reader
7import Control.Monad.Trans.Class
7import Control.Monad.Trans.Class
8import Control.Monad.Trans.Control
8import Control.Monad.Trans.Control
9
9
10−import Effectful.Internal.Has
10+import Effectful.Internal.Effect
11import Effectful.Internal.Monad
11import Effectful.Internal.Monad
12import qualified Effectful.Reader as R
12import qualified Effectful.Reader as R
13
13
src/Effectful/Class/State.hs+5−2
@@ -3,9 +3,9 @@ module Effectful.Class.State where
3
3
4import Control.Monad.Trans.Class
4import Control.Monad.Trans.Class
5
5
6−import Effectful.Internal.Has
6+import Effectful.Internal.Effect
7import Effectful.Internal.Monad
7import Effectful.Internal.Monad
8−import qualified Effectful.State.Dynamic as S
8+import qualified Effectful.State as S
9
9
10-- | Compatiblity layer for a transition period from MTL-style effect handling
10-- | Compatiblity layer for a transition period from MTL-style effect handling
11-- to 'Effectful.Eff'.
11-- to 'Effectful.Eff'.
@@ -39,5 +39,8 @@ instance S.State s :> es => MonadState s (Eff es) where
39 put = S.put
39 put = S.put
40 state = S.state
40 state = S.state
41
41
42+gets :: MonadState s m => (s -> a) -> m a
43+gets f = f <$> get
44+
42modify :: MonadState s m => (s -> s) -> m ()
45modify :: MonadState s m => (s -> s) -> m ()
43modify f = state (\s -> ((), f s))
46modify f = state (\s -> ((), f s))
src/Effectful/Class/Writer.hs+1−1
@@ -4,7 +4,7 @@ module Effectful.Class.Writer where
4import Control.Monad.Trans.Class
4import Control.Monad.Trans.Class
5import Control.Monad.Trans.Control
5import Control.Monad.Trans.Control
6
6
7−import Effectful.Internal.Has
7+import Effectful.Internal.Effect
8import Effectful.Internal.Monad
8import Effectful.Internal.Monad
9import qualified Effectful.Writer as W
9import qualified Effectful.Writer as W
10
10
src/Effectful/Error.hs+14−13
@@ -11,40 +11,41 @@ import Control.Exception
11import Data.Typeable
11import Data.Typeable
12import GHC.Stack
12import GHC.Stack
13
13
14+import Effectful.Internal.Effect
14import Effectful.Internal.Env
15import Effectful.Internal.Env
15−import Effectful.Internal.Has
16import Effectful.Internal.Monad
16import Effectful.Internal.Monad
17import Effectful.Internal.Utils
17import Effectful.Internal.Utils
18
18
19−type role Error nominal
19+type role Error nominal phantom phantom
20−data Error e = Error
20+data Error e :: Effect where
21+ Error :: Error e m r
21
22
22runError
23runError
23 :: forall e es a. Exception e
24 :: forall e es a. Exception e
24 => Eff (Error e : es) a
25 => Eff (Error e : es) a
25 -> Eff es (Either ([String], e) a)
26 -> Eff es (Either ([String], e) a)
26−runError (Eff m) = impureEff $ \es0 -> mask $ \release -> do
27+runError (Eff m) = unsafeEff $ \es0 -> mask $ \release -> do
27 size0 <- sizeEnv es0
28 size0 <- sizeEnv es0
28− es <- unsafeConsEnv (Error @e) es0
29+ es <- unsafeConsEnv (IdE (Error @e)) noRelinker es0
29 try (release $ m es) >>= \case
30 try (release $ m es) >>= \case
30− Right a -> Right a <$ unsafeTailEnv size0 es
31+ Right a -> Right a <$ unsafeTailEnv size0 es
31− Left (WrapE cs e) -> Left (cs, e) <$ unsafeTailEnv size0 es
32+ Left (WrapErr cs e) -> Left (cs, e) <$ unsafeTailEnv size0 es
32
33
33throwError
34throwError
34 :: (HasCallStack, Exception e, Error e :> es)
35 :: (HasCallStack, Exception e, Error e :> es)
35 => e
36 => e
36 -> Eff es a
37 -> Eff es a
37−throwError e = impureEff_ $ do
38+throwError e = unsafeEff_ $ do
38− throwIO $ WrapE (ppCallStack <$> getCallStack callStack) e
39+ throwIO $ WrapErr (ppCallStack <$> getCallStack callStack) e
39
40
40catchError
41catchError
41 :: (Exception e, Error e :> es)
42 :: (Exception e, Error e :> es)
42 => Eff es a
43 => Eff es a
43 -> ([String] -> e -> Eff es a)
44 -> ([String] -> e -> Eff es a)
44 -> Eff es a
45 -> Eff es a
45−catchError (Eff m) handler = impureEff $ \es -> do
46+catchError (Eff m) handler = unsafeEff $ \es -> do
46 size <- sizeEnv es
47 size <- sizeEnv es
47− m es `catch` \(WrapE e cs) -> do
48+ m es `catch` \(WrapErr e cs) -> do
48 checkSizeEnv size es
49 checkSizeEnv size es
49 unEff (handler e cs) es
50 unEff (handler e cs) es
50
51
@@ -57,6 +58,6 @@ tryError m = (Right <$> m) `catchError` \es e -> pure (Left (es, e))
57----------------------------------------
58----------------------------------------
58-- WrapE
59-- WrapE
59
60
60−data WrapE e = WrapE [String] e
61+data WrapErr e = WrapErr [String] e
61 deriving Show
62 deriving Show
62−instance (Show e, Typeable e) => Exception (WrapE e)
63+instance (Show e, Typeable e) => Exception (WrapErr e)
src/Effectful/Internal/Has.hs → src/Effectful/Internal/Effect.hsrenamed+13−13
@@ -5,31 +5,31 @@
5--
5--
6-- This module is intended for internal use only, and may change without warning
6-- This module is intended for internal use only, and may change without warning
7-- in subsequent releases.
7-- in subsequent releases.
8−module Effectful.Internal.Has
8+module Effectful.Internal.Effect
9 ( Effect
9 ( Effect
10− , (:>)
10+ , (:>)(..)
11− , ixEnv
11+ , IdE(..)
12 ) where
12 ) where
13
13
14import Data.Kind
14import Data.Kind
15
15
16−type Effect = Type
16+type Effect = (Type -> Type) -> Type -> Type
17+
18+-- | Adapter for statically dispatched effects.
19+newtype IdE (e :: Effect) where
20+ IdE :: (forall m r. e m r) -> IdE e
17
21
18class (e :: Effect) :> (es :: [Effect]) where
22class (e :: Effect) :> (es :: [Effect]) where
19 -- | Get position of @e@ in @es@.
23 -- | Get position of @e@ in @es@.
20 --
24 --
21 -- /Note:/ GHC is kind enough to cache these values as they're top level CAFs,
25 -- /Note:/ GHC is kind enough to cache these values as they're top level CAFs,
22 -- so the lookup is amortized @O(1)@ without any language level tricks.
26 -- so the lookup is amortized @O(1)@ without any language level tricks.
23− ixOf :: Int
27+ reifyIndex :: Int
24− ixOf = -- Don't show "minimal complete definition" in haddock.
28+ reifyIndex = -- Don't show "minimal complete definition" in haddock.
25− error "unimplemented"
29+ error "unimplemented"
26
30
27instance {-# OVERLAPPING #-} e :> (e : es) where
31instance {-# OVERLAPPING #-} e :> (e : es) where
28− ixOf = 0
32+ reifyIndex = 0
29
33
30instance e :> es => e :> (x : es) where
34instance e :> es => e :> (x : es) where
31− ixOf = 1 + ixOf @e @es
35+ reifyIndex = 1 + reifyIndex @e @es
32−
33−-- | Get position of @e@ in the 'Effectful.Internal.Env.Env'.
34−ixEnv :: forall e es. e :> es => Int -> Int
35−ixEnv n = n - ixOf @e @es - 1
src/Effectful/Internal/Env.hs+247−60
@@ -1,14 +1,18 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
1{-# OPTIONS_HADDOCK not-home #-}
2{-# OPTIONS_HADDOCK not-home #-}
2−-- | The enviroment for 'Effectful.Internal.Monad.Eff'.
3+-- | The environment for 'Effectful.Internal.Monad.Eff'.
3--
4--
4-- This module is intended for internal use only, and may change without warning
5-- This module is intended for internal use only, and may change without warning
5-- in subsequent releases.
6-- in subsequent releases.
6module Effectful.Internal.Env
7module Effectful.Internal.Env
7 ( Env
8 ( Env
9+ , Relinker(..)
10+ , noRelinker
8
11
9 -- * Safe operations
12 -- * Safe operations
10 , emptyEnv
13 , emptyEnv
11 , cloneEnv
14 , cloneEnv
15+ , forkEnv
12 , sizeEnv
16 , sizeEnv
13 , getEnv
17 , getEnv
14 , checkSizeEnv
18 , checkSizeEnv
@@ -30,8 +34,9 @@ import Data.Primitive.SmallArray
30import GHC.Exts (Any)
34import GHC.Exts (Any)
31import GHC.Stack
35import GHC.Stack
32import Unsafe.Coerce
36import Unsafe.Coerce
37+import qualified Data.Map.Strict as M
33
38
34−import Effectful.Internal.Has
39+import Effectful.Internal.Effect
35
40
36type role Env nominal
41type role Env nominal
37
42
@@ -48,61 +53,192 @@ type role Env nominal
48--
53--
49-- - Modification of a specific element: /O(1)/.
54-- - Modification of a specific element: /O(1)/.
50--
55--
51−newtype Env (es :: [Effect]) = Env (IORef EnvRef)
56+data Env (es :: [Effect]) = Env Forks (IORef EnvRef)
52
57
58+-- | A fork is uniquely determined by its base index and depth.
59+data ForkId = ForkId
60+ { baseIx :: Int
61+ , depth :: Int
62+ } deriving (Eq, Ord, Show)
63+
64+-- | Implicit 'ForkId' of the 'EnvRef' directly available from 'Env'.
65+globalFid :: ForkId
66+globalFid = ForkId 0 0
67+
68+-- | Local forks of the environment.
69+data Forks = Forks ForkId (IORef EnvRef) Forks | NoFork
70+
71+data EnvRef = EnvRef
72+ { _capacity :: Int
73+ , _data :: SmallMutableArray RealWorld Any
53−data EnvRef = EnvRef Int (SmallMutableArray RealWorld Any)
74+ , _relinkers :: SmallMutableArray RealWorld Any
75+ }
76+
77+newtype Relinker i (e :: Effect) where
78+ Relinker
79+ :: ((forall es. Env es -> IO (Env es)) -> i e -> IO (i e))
80+ -> Relinker i e
81+
82+noRelinker :: Relinker i e
83+noRelinker = Relinker $ \_ -> pure
54
84
55-- | Create an empty environment.
85-- | Create an empty environment.
56emptyEnv :: IO (Env '[])
86emptyEnv :: IO (Env '[])
57−emptyEnv = fmap Env . newIORef . EnvRef 0
87+emptyEnv = Env NoFork <$> emptyEnvRef
58− =<< newSmallArray 0 (error "undefined field")
59
88
60-- | Clone the environment.
89-- | Clone the environment.
61−cloneEnv :: Env es -> IO (Env es)
90+cloneEnv :: HasCallStack => Env es -> IO (Env es)
62−cloneEnv (Env ref) = do
91+cloneEnv (Env NoFork gref0) = do
63− EnvRef n es <- readIORef ref
92+ cache <- newIORef M.empty
64− fmap Env . newIORef . EnvRef n
93+ gref <- cloneEnvRef cache globalFid gref0
94+ pure $ Env NoFork gref
95+cloneEnv _ = error "Cloning of forked enviroment is not supported yet"
96+{-# NOINLINE cloneEnv #-}
97+
98+----------------------------------------
99+-- Utils for cloning
100+
101+type EnvRefCache = IORef (M.Map ForkId (IORef EnvRef))
102+
103+-- | Clone the 'EnvRef' and put it in a cache.
104+cloneEnvRef
105+ :: EnvRefCache
106+ -> ForkId
107+ -> IORef EnvRef
108+ -> IO (IORef EnvRef)
109+cloneEnvRef cache fid ref0 = do
110+ EnvRef n es0 fs0 <- readIORef ref0
111+ es <- cloneSmallMutableArray es0 0 (sizeofSmallMutableArray es0)
65− =<< cloneSmallMutableArray es 0 (sizeofSmallMutableArray es)
112+ fs <- cloneSmallMutableArray fs0 0 (sizeofSmallMutableArray fs0)
113+ ref <- newIORef $ EnvRef n es fs
114+ modifyIORef' cache $ M.insert fid ref
115+ relinkData cache es fs n
116+ pure ref
117+
118+-- | Relink local environments hiding in the interpreters.
119+relinkData
120+ :: EnvRefCache
121+ -> SmallMutableArray RealWorld Any
122+ -> SmallMutableArray RealWorld Any
123+ -> Int
124+ -> IO ()
125+relinkData cache es fs = \case
126+ 0 -> pure ()
127+ n -> do
128+ let i = n - 1
129+ Relinker f <- fromAny <$> readSmallArray fs i
130+ readSmallArray es i
131+ >>= f relinkEnv . fromAny
132+ >>= writeSmallArray es i . toAny
133+ relinkData cache es fs i
134+ where
135+ relinkEnv :: Env es -> IO (Env es)
136+ relinkEnv (Env forks _) = do
137+ Just gref <- M.lookup globalFid <$> readIORef cache
138+ Env <$> relinkForks cache forks <*> pure gref
139+
140+relinkForks :: EnvRefCache -> Forks -> IO Forks
141+relinkForks cache = \case
142+ NoFork -> pure NoFork
143+ Forks fid lref0 forks -> (M.lookup fid <$> readIORef cache) >>= \case
144+ Just lref -> Forks fid <$> pure lref
145+ <*> relinkForks cache forks
146+ Nothing -> Forks fid <$> cloneEnvRef cache fid lref0
147+ <*> relinkForks cache forks
148+
149+----------------------------------------
150+
151+-- | Create a local fork of the environment for interpreters.
152+forkEnv :: HasCallStack => Env es -> IO (Env es)
153+forkEnv env@(Env NoFork gref) = do
154+ size <- sizeEnv env
155+ let fid = ForkId
156+ { baseIx = size
157+ , depth = 1
158+ }
159+ Env <$> (Forks fid <$> emptyEnvRef <*> pure NoFork)
160+ <*> pure gref
161+forkEnv (Env forks@(Forks fid0 lref0 olderForks) gref) = do
162+ EnvRef n _ _ <- readIORef lref0
163+ -- If the fork is empty, replace it as no data is lost.
164+ if n == 0
165+ then do
166+ lref <- emptyEnvRef
167+ let fid = fid0
168+ { depth = depth fid0 + 1
169+ }
170+ pure $ Env (Forks fid lref olderForks) gref
171+ else do
172+ lref <- emptyEnvRef
173+ let fid = fid0
174+ { baseIx = baseIx fid0 + n
175+ , depth = depth fid0 + 1
176+ }
177+ pure $ Env (Forks fid lref forks) gref
178+{-# NOINLINE forkEnv #-}
66
179
67-- | Get the current size of the environment.
180-- | Get the current size of the environment.
68sizeEnv :: Env es -> IO Int
181sizeEnv :: Env es -> IO Int
69−sizeEnv (Env ref) = do
182+sizeEnv (Env NoFork ref) = do
70− EnvRef n _ <- readIORef ref
183+ EnvRef n _ _ <- readIORef ref
71 pure n
184 pure n
185+sizeEnv (Env (Forks fid lref _) _) = do
186+ EnvRef n _ _ <- readIORef lref
187+ pure $ baseIx fid + n
188+{-# NOINLINE sizeEnv #-}
72
189
73-- | Extract a specific data type from the environment.
190-- | Extract a specific data type from the environment.
74−getEnv :: forall e es. (HasCallStack, e :> es) => Env es -> IO e
191+getEnv :: forall e es i. (HasCallStack, e :> es) => Env es -> IO (i e)
75−getEnv (Env ref) = do
192+getEnv env = do
76− EnvRef n es <- readIORef ref
193+ (i, es) <- getLocation @e env
77− fromAny <$> readSmallArray es (ixEnv @e @es n)
194+ fromAny <$> readSmallArray es i
78
195
79-- | Check that the size of the environment is the same as the expected value.
196-- | Check that the size of the environment is the same as the expected value.
80checkSizeEnv :: HasCallStack => Int -> Env es -> IO ()
197checkSizeEnv :: HasCallStack => Int -> Env es -> IO ()
81−checkSizeEnv k (Env ref) = do
198+checkSizeEnv k (Env NoFork ref) = do
82− EnvRef n _ <- readIORef ref
199+ EnvRef n _ _ <- readIORef ref
200+ when (k /= n) $ do
201+ error $ "k (" ++ show k ++ ") /= n (" ++ show n ++ ")"
202+checkSizeEnv k (Env (Forks fid lref _) _) = do
203+ EnvRef n _ _ <- readIORef lref
204+ when (k /= baseIx fid + n) $ do
83− when (k /= n) . error $ "k (" ++ show k ++ ") /= n (" ++ show n ++ ")"
205+ error $ "k (" ++ show k ++ ") /= baseIx + n (baseIx: "
206+ ++ show (baseIx fid) ++ ", n: " ++ show n ++ ")"
207+{-# NOINLINE checkSizeEnv #-}
84
208
85----------------------------------------
209----------------------------------------
86-- Extending and shrinking
210-- Extending and shrinking
87
211
88-- | Extend the environment with a new data type (in place).
212-- | Extend the environment with a new data type (in place).
89−unsafeConsEnv :: HasCallStack => e -> Env es -> IO (Env (e : es))
213+unsafeConsEnv :: HasCallStack => i e -> Relinker i e -> Env es -> IO (Env (e : es))
90−unsafeConsEnv e (Env ref) = do
214+unsafeConsEnv e f (Env fork gref) = case fork of
91− EnvRef n es0 <- readIORef ref
215+ NoFork -> do
92− let len0 = sizeofSmallMutableArray es0
216+ extendEnvRef gref
93− case n `compare` len0 of
217+ pure $ Env NoFork gref
94− GT -> error $ "n (" ++ show n ++ ") > len0 (" ++ show len0 ++ ")"
218+ Forks base lref forks -> do
95− LT -> do
219+ extendEnvRef lref
96− e `seq` writeSmallArray es0 n (toAny e)
220+ pure $ Env (Forks base lref forks) gref
97− writeIORef ref $! EnvRef (n + 1) es0
98− EQ -> do
99− let len = doubleCapacity len0
100− es <- newSmallArray len (error "undefined field")
101− copySmallMutableArray es 0 es0 0 len0
102− e `seq` writeSmallArray es n (toAny e)
103− writeIORef ref $! EnvRef (n + 1) es
104− pure $ Env ref
105 where
221 where
222+ extendEnvRef :: IORef EnvRef -> IO ()
223+ extendEnvRef ref = do
224+ EnvRef n es0 fs0 <- readIORef ref
225+ let len0 = sizeofSmallMutableArray es0
226+ case n `compare` len0 of
227+ GT -> error $ "n (" ++ show n ++ ") > len0 (" ++ show len0 ++ ")"
228+ LT -> do
229+ e `seq` writeSmallArray es0 n (toAny e)
230+ f `seq` writeSmallArray fs0 n (toAny f)
231+ writeIORef ref $! EnvRef (n + 1) es0 fs0
232+ EQ -> do
233+ let len = doubleCapacity len0
234+ es <- newSmallArray len (error "undefined field")
235+ copySmallMutableArray es 0 es0 0 len0
236+ e `seq` writeSmallArray es n (toAny e)
237+ fs <- newSmallArray len (error "undefined field")
238+ copySmallMutableArray fs 0 fs0 0 len0
239+ f `seq` writeSmallArray fs n (toAny f)
240+ writeIORef ref $! EnvRef (n + 1) es fs
241+
106 doubleCapacity :: Int -> Int
242 doubleCapacity :: Int -> Int
107 doubleCapacity n = max 1 n * 2
243 doubleCapacity n = max 1 n * 2
108{-# NOINLINE unsafeConsEnv #-}
244{-# NOINLINE unsafeConsEnv #-}
@@ -110,14 +246,23 @@ unsafeConsEnv e (Env ref) = do
110-- | Shrink the environment by one data type (in place). Makes sure the size of
246-- | Shrink the environment by one data type (in place). Makes sure the size of
111-- the environment is as expected.
247-- the environment is as expected.
112unsafeTailEnv :: HasCallStack => Int -> Env (e : es) -> IO (Env es)
248unsafeTailEnv :: HasCallStack => Int -> Env (e : es) -> IO (Env es)
113−unsafeTailEnv k (Env ref) = do
249+unsafeTailEnv len (Env fork gref) = case fork of
250+ NoFork -> do
251+ shrinkEnvRef len gref
252+ pure $ Env NoFork gref
253+ Forks fid lref forks -> do
254+ shrinkEnvRef (len - baseIx fid) lref
255+ pure $ Env (Forks fid lref forks) gref
256+ where
257+ shrinkEnvRef :: Int -> IORef EnvRef -> IO ()
258+ shrinkEnvRef k ref = do
114− EnvRef n es <- readIORef ref
259+ EnvRef n es fs <- readIORef ref
115− if k /= n - 1
260+ if k /= n - 1
116− then error $ "k (" ++ show k ++ ") /= n - 1 (" ++ show (n - 1) ++ ")"
261+ then error $ "k (" ++ show k ++ ") /= n - 1 (" ++ show (n - 1) ++ ")"
117− else do
262+ else do
118− writeSmallArray es k (error "undefined field")
263+ writeSmallArray es k (error "undefined field")
264+ writeSmallArray fs k (error "undefined field")
119− writeIORef ref $! EnvRef k es
265+ writeIORef ref $! EnvRef k es fs
120− pure $ Env ref
121{-# NOINLINE unsafeTailEnv #-}
266{-# NOINLINE unsafeTailEnv #-}
122
267
123----------------------------------------
268----------------------------------------
@@ -125,34 +270,32 @@ unsafeTailEnv k (Env ref) = do
125
270
126-- | Replace the data type in the environment with a new value (in place).
271-- | Replace the data type in the environment with a new value (in place).
127unsafePutEnv
272unsafePutEnv
128− :: forall e es. (HasCallStack, e :> es)
273+ :: forall e es i. (HasCallStack, e :> es)
129− => e
274+ => i e
130 -> Env es
275 -> Env es
131 -> IO ()
276 -> IO ()
132−unsafePutEnv e (Env ref) = do
277+unsafePutEnv e env = do
133− EnvRef n es <- readIORef ref
278+ (i, es) <- getLocation @e env
134− e `seq` writeSmallArray es (ixEnv @e @es n) (toAny e)
279+ e `seq` writeSmallArray es i (toAny e)
135
280
136-- | Modify the data type in the environment (in place).
281-- | Modify the data type in the environment (in place).
137unsafeModifyEnv
282unsafeModifyEnv
138− :: forall e es. (HasCallStack, e :> es)
283+ :: forall e es i. (HasCallStack, e :> es)
139− => (e -> e)
284+ => (i e -> i e)
140 -> Env es
285 -> Env es
141 -> IO ()
286 -> IO ()
142−unsafeModifyEnv f (Env ref) = do
287+unsafeModifyEnv f env = do
143− EnvRef n es <- readIORef ref
288+ (i, es) <- getLocation @e env
144− let i = ixEnv @e @es n
145 e <- f . fromAny <$> readSmallArray es i
289 e <- f . fromAny <$> readSmallArray es i
146 e `seq` writeSmallArray es i (toAny e)
290 e `seq` writeSmallArray es i (toAny e)
147
291
148−-- | Modify the data type in the enviroment (in place) and return a value.
292+-- | Modify the data type in the environment (in place) and return a value.
149unsafeStateEnv
293unsafeStateEnv
150− :: forall e es a. (HasCallStack, e :> es)
294+ :: forall e es i a. (HasCallStack, e :> es)
151− => (e -> (a, e))
295+ => (i e -> (a, i e))
152 -> Env es -> IO a
296 -> Env es -> IO a
153−unsafeStateEnv f (Env ref) = do
297+unsafeStateEnv f env = do
154− EnvRef n es <- readIORef ref
298+ (i, es) <- getLocation @e env
155− let i = ixEnv @e @es n
156 (a, e) <- f . fromAny <$> readSmallArray es i
299 (a, e) <- f . fromAny <$> readSmallArray es i
157 e `seq` writeSmallArray es i (toAny e)
300 e `seq` writeSmallArray es i (toAny e)
158 pure a
301 pure a
@@ -160,6 +303,50 @@ unsafeStateEnv f (Env ref) = do
160----------------------------------------
303----------------------------------------
161-- Internal helpers
304-- Internal helpers
162
305
306+emptyEnvRef :: IO (IORef EnvRef)
307+emptyEnvRef = do
308+ es <- newSmallArray 0 (error "undefined field")
309+ fs <- newSmallArray 0 (error "undefined field")
310+ newIORef $ EnvRef 0 es fs
311+
312+-- | Determine location of the data type in the environment.
313+getLocation
314+ :: forall e es. e :> es
315+ => Env es
316+ -> IO (Int, SmallMutableArray RealWorld Any)
317+getLocation (Env fork ref) = do
318+ EnvRef n es _ <- readIORef ref
319+ -- Optimized for the common access pattern:
320+ --
321+ -- - Most application code has no access to forks, in which case we look at
322+ -- the global EnvRef.
323+ --
324+ -- - Interpreters will most likely access the newest fork for reinterpretation
325+ -- of effects that are there.
326+ case fork of
327+ NoFork -> pure (ix n, es)
328+ Forks fid lref forks -> do
329+ EnvRef ln les _ <- readIORef lref
330+ let i = ix (baseIx fid + ln)
331+ if i >= baseIx fid
332+ then pure (i - baseIx fid, les)
333+ else go es i forks
334+ where
335+ go :: SmallMutableArray RealWorld Any
336+ -> Int
337+ -> Forks
338+ -> IO (Int, SmallMutableArray RealWorld Any)
339+ go es i = \case
340+ NoFork -> pure (i, es)
341+ Forks fid lref forks -> do
342+ EnvRef _ les _ <- readIORef lref
343+ if i >= baseIx fid
344+ then pure (i - baseIx fid, les)
345+ else go es i forks
346+
347+ ix :: Int -> Int
348+ ix n = n - reifyIndex @e @es - 1
349+
163toAny :: a -> Any
350toAny :: a -> Any
164toAny = unsafeCoerce
351toAny = unsafeCoerce
165
352
src/Effectful/Internal/Monad.hs+50−58
@@ -8,9 +8,9 @@ module Effectful.Internal.Monad
8 ( -- * Monad
8 ( -- * Monad
9 Eff(..)
9 Eff(..)
10 , runEff
10 , runEff
11− , impureEff
11+ , unsafeEff
12− , impureEff_
12+ , unsafeEff_
13− , impureUnliftEff
13+ , unsafeUnliftEff
14
14
15 -- * IO
15 -- * IO
16 , IOE
16 , IOE
@@ -24,7 +24,6 @@ module Effectful.Internal.Monad
24 , putEffect
24 , putEffect
25 , stateEffect
25 , stateEffect
26 , localEffect
26 , localEffect
27− , listenEffect
28 , readerEffectM
27 , readerEffectM
29 , stateEffectM
28 , stateEffectM
30 ) where
29 ) where
@@ -37,8 +36,8 @@ import Control.Monad.IO.Unlift
37import Control.Monad.Trans.Control
36import Control.Monad.Trans.Control
38import GHC.Magic (oneShot)
37import GHC.Magic (oneShot)
39
38
39+import Effectful.Internal.Effect
40import Effectful.Internal.Env
40import Effectful.Internal.Env
41−import Effectful.Internal.Has
42
41
43type role Eff nominal representational
42type role Eff nominal representational
44
43
@@ -47,15 +46,15 @@ newtype Eff (es :: [Effect]) a = Eff { unEff :: Env es -> IO a }
47runEff :: Eff '[] a -> IO a
46runEff :: Eff '[] a -> IO a
48runEff (Eff m) = m =<< emptyEnv
47runEff (Eff m) = m =<< emptyEnv
49
48
50−impureEff :: (Env es -> IO a) -> Eff es a
49+unsafeEff :: (Env es -> IO a) -> Eff es a
51−impureEff m = Eff (oneShot m)
50+unsafeEff m = Eff (oneShot m)
52
51
53−impureEff_ :: IO a -> Eff es a
52+unsafeEff_ :: IO a -> Eff es a
54−impureEff_ m = impureEff $ \_ -> m
53+unsafeEff_ m = unsafeEff $ \_ -> m
55
54
56−-- | Run 'Eff' computations in 'IO'.
55+-- | Lower 'Eff' computations into 'IO'.
57−impureUnliftEff :: ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
56+unsafeUnliftEff :: ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
58−impureUnliftEff f = impureEff $ \es -> do
57+unsafeUnliftEff f = unsafeEff $ \es -> do
59 tid0 <- myThreadId
58 tid0 <- myThreadId
60 f $ \(Eff m) -> do
59 f $ \(Eff m) -> do
61 tid <- myThreadId
60 tid <- myThreadId
@@ -70,14 +69,14 @@ impureUnliftEff f = impureEff $ \es -> do
70-- Base
69-- Base
71
70
72instance Functor (Eff es) where
71instance Functor (Eff es) where
73− fmap f (Eff m) = impureEff $ \es -> f <$> m es
72+ fmap f (Eff m) = unsafeEff $ \es -> f <$> m es
74
73
75instance Applicative (Eff es) where
74instance Applicative (Eff es) where
76− pure = impureEff_ . pure
75+ pure = unsafeEff_ . pure
77− Eff mf <*> Eff mx = impureEff $ \es -> mf es <*> mx es
76+ Eff mf <*> Eff mx = unsafeEff $ \es -> mf es <*> mx es
78
77
79instance Monad (Eff es) where
78instance Monad (Eff es) where
80− Eff m >>= k = impureEff $ \es -> do
79+ Eff m >>= k = unsafeEff $ \es -> do
81 a <- m es
80 a <- m es
82 unEff (k a) es
81 unEff (k a) es
83
82
@@ -85,23 +84,23 @@ instance Monad (Eff es) where
85-- Exception
84-- Exception
86
85
87instance MonadThrow (Eff es) where
86instance MonadThrow (Eff es) where
88− throwM = impureEff_ . throwM
87+ throwM = unsafeEff_ . throwM
89
88
90instance MonadCatch (Eff es) where
89instance MonadCatch (Eff es) where
91− catch (Eff m) handler = impureEff $ \es -> do
90+ catch (Eff m) handler = unsafeEff $ \es -> do
92 size <- sizeEnv es
91 size <- sizeEnv es
93 m es `catch` \e -> do
92 m es `catch` \e -> do
94 checkSizeEnv size es
93 checkSizeEnv size es
95 unEff (handler e) es
94 unEff (handler e) es
96
95
97instance MonadMask (Eff es) where
96instance MonadMask (Eff es) where
98− mask k = impureEff $ \es -> mask $ \restore ->
97+ mask k = unsafeEff $ \es -> mask $ \restore ->
99− unEff (k $ \(Eff m) -> impureEff $ restore . m) es
98+ unEff (k $ \(Eff m) -> unsafeEff $ restore . m) es
100
99
101− uninterruptibleMask k = impureEff $ \es -> uninterruptibleMask $ \restore ->
100+ uninterruptibleMask k = unsafeEff $ \es -> uninterruptibleMask $ \restore ->
102− unEff (k $ \(Eff m) -> impureEff $ restore . m) es
101+ unEff (k $ \(Eff m) -> unsafeEff $ restore . m) es
103
102
104− generalBracket acquire release use = impureEff $ \es -> mask $ \restore -> do
103+ generalBracket acquire release use = unsafeEff $ \es -> mask $ \restore -> do
105 size <- sizeEnv es
104 size <- sizeEnv es
106 resource <- unEff acquire es
105 resource <- unEff acquire es
107 b <- restore (unEff (use resource) es) `catch` \e -> do
106 b <- restore (unEff (use resource) es) `catch` \e -> do
@@ -115,77 +114,70 @@ instance MonadMask (Eff es) where
115----------------------------------------
114----------------------------------------
116-- IO
115-- IO
117
116
118−data IOE = IOE
117+data IOE :: Effect where
118+ IOE :: IOE m r
119
119
120runIOE :: Eff '[IOE] a -> Eff '[] a
120runIOE :: Eff '[IOE] a -> Eff '[] a
121−runIOE = evalEffect IOE
121+runIOE = evalEffect (IdE IOE)
122
122
123instance IOE :> es => MonadIO (Eff es) where
123instance IOE :> es => MonadIO (Eff es) where
124− liftIO = impureEff_
124+ liftIO = unsafeEff_
125
125
126instance IOE :> es => MonadBase IO (Eff es) where
126instance IOE :> es => MonadBase IO (Eff es) where
127− liftBase = impureEff_
127+ liftBase = unsafeEff_
128
128
129instance IOE :> es => MonadBaseControl IO (Eff es) where
129instance IOE :> es => MonadBaseControl IO (Eff es) where
130 type StM (Eff es) a = a
130 type StM (Eff es) a = a
131− liftBaseWith = impureUnliftEff
131+ liftBaseWith = unsafeUnliftEff
132 restoreM = pure
132 restoreM = pure
133
133
134instance IOE :> es => MonadUnliftIO (Eff es) where
134instance IOE :> es => MonadUnliftIO (Eff es) where
135− withRunInIO = impureUnliftEff
135+ withRunInIO = unsafeUnliftEff
136
136
137----------------------------------------
137----------------------------------------
138-- Helpers
138-- Helpers
139
139
140−runEffect :: e -> Eff (e : es) a -> Eff es (a, e)
140+runEffect :: i e -> Eff (e : es) a -> Eff es (a, i e)
141−runEffect e0 (Eff m) = impureEff $ \es0 -> do
141+runEffect e0 (Eff m) = unsafeEff $ \es0 -> do
142 size0 <- sizeEnv es0
142 size0 <- sizeEnv es0
143− bracket (unsafeConsEnv e0 es0)
143+ bracket (unsafeConsEnv e0 noRelinker es0)
144 (unsafeTailEnv size0)
144 (unsafeTailEnv size0)
145 (\es -> (,) <$> m es <*> getEnv es)
145 (\es -> (,) <$> m es <*> getEnv es)
146
146
147−evalEffect :: e -> Eff (e : es) a -> Eff es a
147+evalEffect :: i e -> Eff (e : es) a -> Eff es a
148−evalEffect e (Eff m) = impureEff $ \es0 -> do
148+evalEffect e (Eff m) = unsafeEff $ \es0 -> do
149 size0 <- sizeEnv es0
149 size0 <- sizeEnv es0
150− bracket (unsafeConsEnv e es0)
150+ bracket (unsafeConsEnv e noRelinker es0)
151 (unsafeTailEnv size0)
151 (unsafeTailEnv size0)
152 (\es -> m es)
152 (\es -> m es)
153
153
154−execEffect :: e -> Eff (e : es) a -> Eff es e
154+execEffect :: i e -> Eff (e : es) a -> Eff es (i e)
155−execEffect e0 (Eff m) = impureEff $ \es0 -> do
155+execEffect e0 (Eff m) = unsafeEff $ \es0 -> do
156 size0 <- sizeEnv es0
156 size0 <- sizeEnv es0
157− bracket (unsafeConsEnv e0 es0)
157+ bracket (unsafeConsEnv e0 noRelinker es0)
158 (unsafeTailEnv size0)
158 (unsafeTailEnv size0)
159 (\es -> m es *> getEnv es)
159 (\es -> m es *> getEnv es)
160
160
161−getEffect :: e :> es => Eff es e
161+getEffect :: e :> es => Eff es (i e)
162−getEffect = impureEff $ \es -> getEnv es
162+getEffect = unsafeEff $ \es -> getEnv es
163
163
164−putEffect :: e :> es => e -> Eff es ()
164+putEffect :: e :> es => i e -> Eff es ()
165−putEffect e = impureEff $ \es -> unsafePutEnv e es
165+putEffect e = unsafeEff $ \es -> unsafePutEnv e es
166
166
167−stateEffect :: e :> es => (e -> (a, e)) -> Eff es a
167+stateEffect :: e :> es => (i e -> (a, i e)) -> Eff es a
168−stateEffect f = impureEff $ \es -> unsafeStateEnv f es
168+stateEffect f = unsafeEff $ \es -> unsafeStateEnv f es
169
169
170−localEffect :: e :> es => (e -> e) -> Eff es a -> Eff es a
170+localEffect :: e :> es => (i e -> i e) -> Eff es a -> Eff es a
171−localEffect f (Eff m) = impureEff $ \es -> do
171+localEffect f (Eff m) = unsafeEff $ \es -> do
172 bracket (unsafeStateEnv (\e -> (e, f e)) es)
172 bracket (unsafeStateEnv (\e -> (e, f e)) es)
173 (\e -> unsafePutEnv e es)
173 (\e -> unsafePutEnv e es)
174 (\_ -> m es)
174 (\_ -> m es)
175
175
176−listenEffect :: (e :> es, Monoid e) => Eff es a -> Eff es (a, e)
176+readerEffectM :: e :> es => (i e -> Eff es a) -> Eff es a
177−listenEffect (Eff m) = impureEff $ \es -> mask $ \restore -> do
177+readerEffectM f = unsafeEff $ \es -> getEnv es >>= \e -> unEff (f e) es
178− e0 <- unsafeStateEnv (\e -> (e, mempty)) es
179− -- If an exception is thrown, restore e0 and keep parts of e1.
180− a <- restore (m es) `onException` unsafeModifyEnv (\e -> e0 `mappend` e) es
181− e1 <- unsafeStateEnv (\e -> (e, e0 `mappend` e)) es
182− pure (a, e1)
183
178
184−readerEffectM :: e :> es => (e -> Eff es a) -> Eff es a
185−readerEffectM f = impureEff $ \es -> getEnv es >>= \e -> unEff (f e) es
186−
187−stateEffectM :: e :> es => (e -> Eff es (a, e)) -> Eff es a
179+stateEffectM :: e :> es => (i e -> Eff es (a, i e)) -> Eff es a
188−stateEffectM f = impureEff $ \es -> do
180+stateEffectM f = unsafeEff $ \es -> do
189 (a, e) <- (\e -> unEff (f e) es) =<< getEnv es
181 (a, e) <- (\e -> unEff (f e) es) =<< getEnv es
190 unsafePutEnv e es
182 unsafePutEnv e es
191 pure a
183 pure a
src/Effectful/Interpreter.hsnew file+109
@@ -0,0 +1,109 @@
1+module Effectful.Interpreter
2+ ( -- * Sending commands
3+ send
4+
5+ -- * Interpretation
6+ , interpret
7+ , interpretM
8+ , interpretIO
9+
10+ -- * Reinterpretation
11+ , reinterpret
12+ , reinterpretM
13+ , reinterpretIO
14+ ) where
15+
16+import Control.Monad.Catch
17+
18+import Effectful.Internal.Effect
19+import Effectful.Internal.Env
20+import Effectful.Internal.Monad
21+
22+----------------------------------------
23+-- Interpreter
24+
25+data Interpreter (e :: Effect) = forall localEs. Interpreter
26+ { _env :: Env localEs
27+ , _interpret :: forall a es. (forall r. Eff es r -> IO r)
28+ -> e (Eff es) a -> Eff localEs a
29+ }
30+
31+runInterpreter :: Env es -> Interpreter e -> Eff (e : es) a -> IO a
32+runInterpreter es0 e (Eff m) = do
33+ size0 <- sizeEnv es0
34+ bracket (unsafeConsEnv e relinker es0)
35+ (unsafeTailEnv size0)
36+ (\es -> m es)
37+ where
38+ relinker :: Relinker Interpreter e
39+ relinker = Relinker $ \relink Interpreter{..} -> do
40+ env <- relink _env
41+ pure Interpreter { _env = env, .. }
42+
43+----------------------------------------
44+-- Sending commands
45+
46+-- | Send an operation of a given effect to the interpreter.
47+send :: e :> es => e (Eff es) a -> Eff es a
48+send op = readerEffectM $ \Interpreter{..} -> do
49+ unsafeUnliftEff $ \run -> unEff (_interpret run op) _env
50+
51+----------------------------------------
52+-- Interpretation
53+
54+-- | Interpret a first order effect.
55+interpret
56+ :: (forall r es. e (Eff es) r -> Eff baseEs r)
57+ -> Eff (e : baseEs) a
58+ -> Eff baseEs a
59+interpret f = interpretIO $ \_ -> f
60+
61+-- | Interpret a higher order effect with help of a natural transformation from
62+-- 'Eff' to 'Eff'.
63+interpretM
64+ :: (forall r es. (forall t. Eff es t -> Eff baseEs t) -> e (Eff es) r -> Eff baseEs r)
65+ -> Eff (e : baseEs) a
66+ -> Eff baseEs a
67+interpretM f = interpretIO $ \k -> f $ unsafeEff_ . k
68+
69+-- | Interpret a higher order effect with help of a natural transformation from
70+-- 'Eff' to 'IO'.
71+interpretIO
72+ :: (forall r es. (forall t. Eff es t -> IO t) -> e (Eff es) r -> Eff baseEs r)
73+ -> Eff (e : baseEs) a
74+ -> Eff baseEs a
75+interpretIO f m = unsafeEff $ \es -> do
76+ les <- forkEnv es
77+ runInterpreter es (Interpreter les f) m
78+
79+----------------------------------------
80+-- Reinterpretation
81+
82+-- | Reinterpret local effects as a first order effect.
83+reinterpret
84+ :: (Eff localEs a -> Eff baseEs b)
85+ -> (forall r es. e (Eff es) r -> Eff localEs r)
86+ -> Eff (e : baseEs) a
87+ -> Eff baseEs b
88+reinterpret runLocal f = reinterpretIO runLocal $ \_ -> f
89+
90+-- | Reinterpret local effects as a higher order effect with help of a natural
91+-- transformation from 'Eff' to 'Eff'.
92+reinterpretM
93+ :: (Eff localEs a -> Eff baseEs b)
94+ -> (forall r es. (forall t. Eff es t -> Eff localEs t) -> e (Eff es) r -> Eff localEs r)
95+ -> Eff (e : baseEs) a
96+ -> Eff baseEs b
97+reinterpretM runLocal f = reinterpretIO runLocal $ \k -> f $ unsafeEff_ . k
98+
99+-- | Reinterpret local effects as a higher order effect with help of a natural
100+-- transformation from 'Eff' to 'IO'.
101+reinterpretIO
102+ :: (Eff localEs a -> Eff baseEs b)
103+ -> (forall r es. (forall t. Eff es t -> IO t) -> e (Eff es) r -> Eff localEs r)
104+ -> Eff (e : baseEs) a
105+ -> Eff baseEs b
106+reinterpretIO runLocal f m = unsafeEff $ \es -> do
107+ les0 <- forkEnv es
108+ (`unEff` les0) . runLocal . unsafeEff $ \les -> do
109+ runInterpreter es (Interpreter les f) m
src/Effectful/Monad.hsnew file+14
@@ -0,0 +1,14 @@
1+module Effectful.Monad
2+ ( -- * The 'Eff' monad
3+ Eff
4+ , runEff
5+ , Effect
6+ , (:>)
7+
8+ -- * The IO effect
9+ , IOE
10+ , runIOE
11+ ) where
12+
13+import Effectful.Internal.Effect
14+import Effectful.Internal.Monad
src/Effectful/Reader.hs+11−6
@@ -8,23 +8,28 @@ module Effectful.Reader
8 , asks
8 , asks
9 ) where
9 ) where
10
10
11−import Effectful.Internal.Has
11+import Effectful.Internal.Effect
12import Effectful.Internal.Monad
12import Effectful.Internal.Monad
13
13
14-- | Provide access to a read only value of type @r@.
14-- | Provide access to a read only value of type @r@.
15−newtype Reader r = Reader { unReader :: r }
15+newtype Reader r :: Effect where
16+ Reader :: r -> Reader r m a
16
17
17runReader :: r -> Eff (Reader r : es) a -> Eff es a
18runReader :: r -> Eff (Reader r : es) a -> Eff es a
18−runReader r = evalEffect (Reader r)
19+runReader r = evalEffect (IdE (Reader r))
19
20
20ask :: Reader r :> es => Eff es r
21ask :: Reader r :> es => Eff es r
22+ask = do
21−ask = unReader <$> getEffect
23+ IdE (Reader r) <- getEffect
24+ pure r
22
25
23local :: Reader r :> es => (r -> r) -> Eff es a -> Eff es a
26local :: Reader r :> es => (r -> r) -> Eff es a -> Eff es a
24−local f = localEffect (Reader . f . unReader)
27+local f = localEffect $ \(IdE (Reader r)) -> IdE (Reader (f r))
25
28
26reader :: Reader r :> es => (r -> a) -> Eff es a
29reader :: Reader r :> es => (r -> a) -> Eff es a
27−reader f = f . unReader <$> getEffect
30+reader f = do
31+ IdE (Reader r) <- getEffect
32+ pure $ f r
28
33
29asks :: Reader r :> es => (r -> a) -> Eff es a
34asks :: Reader r :> es => (r -> a) -> Eff es a
30asks = reader
35asks = reader
src/Effectful/Resource.hs+7−6
@@ -17,20 +17,21 @@ import Control.Exception
17import qualified Control.Monad.Trans.Resource as R
17import qualified Control.Monad.Trans.Resource as R
18import qualified Control.Monad.Trans.Resource.Internal as RI
18import qualified Control.Monad.Trans.Resource.Internal as RI
19
19
20+import Effectful.Internal.Effect
20import Effectful.Internal.Env
21import Effectful.Internal.Env
21−import Effectful.Internal.Has
22import Effectful.Internal.Monad
22import Effectful.Internal.Monad
23
23
24-- | Data tag for a resource effect.
24-- | Data tag for a resource effect.
25+newtype Resource :: Effect where
25−newtype Resource = Resource R.InternalState
26+ Resource :: R.InternalState -> Resource m r
26
27
27-- | Run the resource effect.
28-- | Run the resource effect.
28runResource :: Eff (Resource : es) a -> Eff es a
29runResource :: Eff (Resource : es) a -> Eff es a
29−runResource (Eff m) = impureEff $ \es0 -> do
30+runResource (Eff m) = unsafeEff $ \es0 -> do
30 size0 <- sizeEnv es0
31 size0 <- sizeEnv es0
31 istate <- R.createInternalState
32 istate <- R.createInternalState
32 mask $ \restore -> do
33 mask $ \restore -> do
33− es <- unsafeConsEnv (Resource istate) es0
34+ es <- unsafeConsEnv (IdE (Resource istate)) noRelinker es0
34 a <- restore (m es) `catch` \e -> do
35 a <- restore (m es) `catch` \e -> do
35 _ <- unsafeTailEnv size0 es
36 _ <- unsafeTailEnv size0 es
36 RI.stateCleanupChecked (Just e) istate
37 RI.stateCleanupChecked (Just e) istate
@@ -40,5 +41,5 @@ runResource (Eff m) = impureEff $ \es0 -> do
40 pure a
41 pure a
41
42
42instance (IOE :> es, Resource :> es) => R.MonadResource (Eff es) where
43instance (IOE :> es, Resource :> es) => R.MonadResource (Eff es) where
43− liftResourceT (RI.ResourceT m) = impureEff $ \es -> do
44+ liftResourceT (RI.ResourceT m) = unsafeEff $ \es -> do
44− getEnv es >>= \(Resource istate) -> m istate
45+ getEnv es >>= \(IdE (Resource istate)) -> m istate
src/Effectful/State.hs+24−16
@@ -2,12 +2,9 @@
2--
2--
3-- Represented as a pure value underneath, therefore:
3-- Represented as a pure value underneath, therefore:
4--
4--
5−-- - very fast
5+-- - very fast,
6--
6--
7−-- - not suitable for sharing between multiple threads.
8−--
9−-- If you plan to do the latter, have a look at "Effectful.State.MVar" or
7+-- - thread local (if you need sharing, have a look at "Effectful.State.MVar").
10−-- "Effectful.State.Dynamic".
11--
8--
12module Effectful.State
9module Effectful.State
13 ( State
10 ( State
@@ -15,6 +12,7 @@ module Effectful.State
15 , evalState
12 , evalState
16 , execState
13 , execState
17 , get
14 , get
15+ , gets
18 , put
16 , put
19 , state
17 , state
20 , modify
18 , modify
@@ -22,37 +20,47 @@ module Effectful.State
22 , modifyM
20 , modifyM
23 ) where
21 ) where
24
22
25−import Data.Coerce
26−
27−import Effectful.Internal.Has
23+import Effectful.Internal.Effect
28import Effectful.Internal.Monad
24import Effectful.Internal.Monad
29
25
30-- | Provide access to a pure, mutable state of type @s@.
26-- | Provide access to a pure, mutable state of type @s@.
31−newtype State s = State { unState :: s }
27+newtype State s :: Effect where
28+ State :: s -> State s m r
32
29
33runState :: s -> Eff (State s : es) a -> Eff es (a, s)
30runState :: s -> Eff (State s : es) a -> Eff es (a, s)
31+runState s0 m = do
34−runState s = coerce . runEffect (State s)
32+ (a, IdE (State s)) <- runEffect (IdE (State s0)) m
33+ pure (a, s)
35
34
36evalState :: s -> Eff (State s : es) a -> Eff es a
35evalState :: s -> Eff (State s : es) a -> Eff es a
37−evalState s = evalEffect (State s)
36+evalState s = evalEffect (IdE (State s))
38
37
39execState :: s -> Eff (State s : es) a -> Eff es s
38execState :: s -> Eff (State s : es) a -> Eff es s
39+execState s0 m = do
40−execState s = coerce . execEffect (State s)
40+ IdE (State s) <- execEffect (IdE (State s0)) m
41+ pure s
41
42
42get :: State s :> es => Eff es s
43get :: State s :> es => Eff es s
43−get = unState <$> getEffect
44+get = do
45+ IdE (State s) <- getEffect
46+ pure s
47+
48+gets :: State s :> es => (s -> a) -> Eff es a
49+gets f = f <$> get
44
50
45put :: State s :> es => s -> Eff es ()
51put :: State s :> es => s -> Eff es ()
46−put = putEffect . State
52+put s = putEffect (IdE (State s))
47
53
48state :: State s :> es => (s -> (a, s)) -> Eff es a
54state :: State s :> es => (s -> (a, s)) -> Eff es a
49−state f = stateEffect $ \(State s0) -> let (a, s) = f s0 in (a, State s)
55+state f = stateEffect $ \(IdE (State s0)) -> let (a, s) = f s0 in (a, IdE (State s))
50
56
51modify :: State s :> es => (s -> s) -> Eff es ()
57modify :: State s :> es => (s -> s) -> Eff es ()
52modify f = state (\s -> ((), f s))
58modify f = state (\s -> ((), f s))
53
59
54stateM :: State s :> es => (s -> Eff es (a, s)) -> Eff es a
60stateM :: State s :> es => (s -> Eff es (a, s)) -> Eff es a
55−stateM f = stateEffectM $ \(State s0) -> coerce $ f s0
61+stateM f = stateEffectM $ \(IdE (State s0)) -> do
62+ (a, s) <- f s0
63+ pure (a, IdE (State s))
56
64
57modifyM :: State s :> es => (s -> Eff es s) -> Eff es ()
65modifyM :: State s :> es => (s -> Eff es s) -> Eff es ()
58modifyM f = stateM (\s -> ((), ) <$> f s)
66modifyM f = stateM (\s -> ((), ) <$> f s)
src/Effectful/State/Dynamic.hs+52−56
@@ -5,13 +5,13 @@
5--
5--
6-- - Share state between threads and need the synchonized version.
6-- - Share state between threads and need the synchonized version.
7--
7--
8−-- - Don't share state between threads and are free to use the faster, pure
8+-- - Don't share state between threads (or need them to be thread local) and are
9−-- version.
9+-- free to use the faster, pure version.
10--
10--
11-- However, let's include this for now.
11-- However, let's include this for now.
12--
12--
13module Effectful.State.Dynamic
13module Effectful.State.Dynamic
14− ( State
14+ ( State(..)
15
15
16 -- * Pure
16 -- * Pure
17 , runState
17 , runState
@@ -25,6 +25,7 @@ module Effectful.State.Dynamic
25
25
26 -- * Operations
26 -- * Operations
27 , get
27 , get
28+ , gets
28 , put
29 , put
29 , state
30 , state
30 , modify
31 , modify
@@ -32,94 +33,89 @@ module Effectful.State.Dynamic
32 , modifyM
33 , modifyM
33 ) where
34 ) where
34
35
35−import Control.Concurrent.MVar
36−
37−import Effectful.Internal.Has
36+import Effectful.Internal.Effect
38import Effectful.Internal.Monad
37import Effectful.Internal.Monad
38+import Effectful.Interpreter
39+
40+import qualified Effectful.State as SP
41+import qualified Effectful.State.MVar as SM
39
42
40-- | Provide access to a mutable state of type @s@.
43-- | Provide access to a mutable state of type @s@.
41--
44--
42−-- Whether the state is represented as a pure value or an 'MVar' depends on the
45+-- Whether the state is represented as a pure value or an 'MVar' underneath
43−-- interpretation.
46+-- depends on the interpretation.
44−data State s = forall ref. State
47+data State s :: Effect where
45− { _ref :: ref
48+ Get :: State s m s
46− , _get :: forall es. ref -> Eff es s
49+ Put :: s -> State s m ()
47− , _put :: forall es. ref -> s -> Eff es ref
50+ State :: (s -> (a, s)) -> State s m a
48− , _state :: forall es a. ref -> (s -> Eff es (a, s)) -> Eff es (a, ref)
51+ StateM :: (s -> m (a, s)) -> State s m a
49− }
50
52
51----------------------------------------
53----------------------------------------
52-- Pure
54-- Pure
53
55
54runState :: s -> Eff (State s : es) a -> Eff es (a, s)
56runState :: s -> Eff (State s : es) a -> Eff es (a, s)
55−runState s m = evalEffect (statePure s) $ (,) <$> m <*> get
57+runState s0 = reinterpretM (SP.runState s0) statePure
56
58
57evalState :: s -> Eff (State s : es) a -> Eff es a
59evalState :: s -> Eff (State s : es) a -> Eff es a
58−evalState s = evalEffect (statePure s)
60+evalState s0 = reinterpretM (SP.evalState s0) statePure
59
61
60execState :: s -> Eff (State s : es) a -> Eff es s
62execState :: s -> Eff (State s : es) a -> Eff es s
63+execState s0 = reinterpretM (SP.execState s0) statePure
64+
61−execState s m = evalEffect (statePure s) $ m *> get
65+statePure
62−
66+ :: SP.State s :> es
63−statePure :: s -> State s
67+ => (forall r. Eff es0 r -> Eff es r)
64−statePure s0 = State
68+ -> State s (Eff es0) a
65− { _ref = s0
69+ -> Eff es a
66− , _get = pure
70+statePure run = \case
67− , _put = \_ -> pure
71+ Get -> SP.get
68− , _state = \s f -> f s
72+ Put s -> SP.put s
69− }
73+ State f -> SP.state f
74+ StateM f -> SP.stateM (run . f)
70
75
71----------------------------------------
76----------------------------------------
72-- MVar
77-- MVar
73
78
74runStateMVar :: s -> Eff (State s : es) a -> Eff es (a, s)
79runStateMVar :: s -> Eff (State s : es) a -> Eff es (a, s)
75−runStateMVar s m = do
80+runStateMVar s0 = reinterpretM (SM.runState s0) stateMVar
76− v <- impureEff_ $ newMVar s
77− evalEffect (stateMVar v) $ (,) <$> m <*> get
78
81
79evalStateMVar :: s -> Eff (State s : es) a -> Eff es a
82evalStateMVar :: s -> Eff (State s : es) a -> Eff es a
80−evalStateMVar s m = do
83+evalStateMVar s0 = reinterpretM (SM.evalState s0) stateMVar
81− v <- impureEff_ $ newMVar s
82− evalEffect (stateMVar v) m
83
84
84execStateMVar :: s -> Eff (State s : es) a -> Eff es s
85execStateMVar :: s -> Eff (State s : es) a -> Eff es s
85−execStateMVar s m = do
86+execStateMVar s0 = reinterpretM (SM.execState s0) stateMVar
86− v <- impureEff_ $ newMVar s
87+
87− evalEffect (stateMVar v) $ m *> get
88+stateMVar
88−
89+ :: SM.State s :> es
89−stateMVar :: MVar s -> State s
90+ => (forall r. Eff es0 r -> Eff es r)
90−stateMVar v0 = State
91+ -> State s (Eff es0) a
91− { _ref = v0
92+ -> Eff es a
92− , _get = impureEff_ . readMVar
93+stateMVar run = \case
93− , _put = \v s -> impureEff_ . modifyMVar v $ \_ ->
94+ Get -> SM.get
94− s `seq` pure (s, v)
95+ Put s -> SM.put s
95− , _state = \v f -> impureEff $ \es -> modifyMVar v $ \s0 -> do
96+ State f -> SM.state f
96− (a, s) <- unEff (f s0) es
97+ StateM f -> SM.stateM (run . f)
97− s `seq` pure (s, (a, v))
98− }
99
98
100----------------------------------------
99----------------------------------------
101-- Operations
100-- Operations
102
101
103get :: State s :> es => Eff es s
102get :: State s :> es => Eff es s
104−get = readerEffectM $ \State{..} -> _get _ref
103+get = send Get
104+
105+gets :: State s :> es => (s -> a) -> Eff es a
106+gets f = f <$> get
105
107
106put :: State s :> es => s -> Eff es ()
108put :: State s :> es => s -> Eff es ()
107−put s = stateEffectM $ \State{..} -> do
109+put = send . Put
108− ref <- _put _ref s
109− pure ((), State { _ref = ref, .. })
110
110
111−state :: State s :> es => (s -> (a, s)) -> Eff es a
111+state :: (State s :> es) => (s -> (a, s)) -> Eff es a
112−state f = stateEffectM $ \State{..} -> do
112+state = send . State
113− (a, ref) <- _state _ref $ pure . f
114− pure (a, State { _ref = ref, .. })
115
113
116modify :: State s :> es => (s -> s) -> Eff es ()
114modify :: State s :> es => (s -> s) -> Eff es ()
117modify f = state (\s -> ((), f s))
115modify f = state (\s -> ((), f s))
118
116
119stateM :: State s :> es => (s -> Eff es (a, s)) -> Eff es a
117stateM :: State s :> es => (s -> Eff es (a, s)) -> Eff es a
120−stateM f = stateEffectM $ \State{..} -> do
118+stateM = send . StateM
121− (a, ref) <- _state _ref f
122− pure (a, State { _ref = ref, .. })
123
119
124modifyM :: State s :> es => (s -> Eff es s) -> Eff es ()
120modifyM :: State s :> es => (s -> Eff es s) -> Eff es ()
125modifyM f = stateM (\s -> ((), ) <$> f s)
121modifyM f = stateM (\s -> ((), ) <$> f s)
src/Effectful/State/MVar.hs+23−18
@@ -2,9 +2,9 @@
2--
2--
3-- Represented as an MVar underneath, therefore:
3-- Represented as an MVar underneath, therefore:
4--
4--
5−-- - slower than "Effectful.State"
5+-- - suitable for sharing between multiple threads,
6--
6--
7−-- - suitable for sharing between multiple threads.
7+-- - slower than "Effectful.State".
8--
8--
9module Effectful.State.MVar
9module Effectful.State.MVar
10 ( State
10 ( State
@@ -12,6 +12,7 @@ module Effectful.State.MVar
12 , evalState
12 , evalState
13 , execState
13 , execState
14 , get
14 , get
15+ , gets
15 , put
16 , put
16 , state
17 , state
17 , modify
18 , modify
@@ -21,49 +22,53 @@ module Effectful.State.MVar
21
22
22import Control.Concurrent.MVar
23import Control.Concurrent.MVar
23
24
24−import Effectful.Internal.Has
25+import Effectful.Internal.Effect
25import Effectful.Internal.Monad
26import Effectful.Internal.Monad
26
27
27-- | Provide access to a synchronized, mutable state of type @s@.
28-- | Provide access to a synchronized, mutable state of type @s@.
28−newtype State s = State (MVar s)
29+newtype State s :: Effect where
30+ State :: MVar s -> State s m r
29
31
30runState :: s -> Eff (State s : es) a -> Eff es (a, s)
32runState :: s -> Eff (State s : es) a -> Eff es (a, s)
31runState s m = do
33runState s m = do
32− v <- impureEff_ $ newMVar s
34+ v <- unsafeEff_ $ newMVar s
33− evalEffect (State v) $ (,) <$> m <*> get
35+ evalEffect (IdE (State v)) $ (,) <$> m <*> get
34
36
35evalState :: s -> Eff (State s : es) a -> Eff es a
37evalState :: s -> Eff (State s : es) a -> Eff es a
36evalState s m = do
38evalState s m = do
37− v <- impureEff_ $ newMVar s
39+ v <- unsafeEff_ $ newMVar s
38− evalEffect (State v) m
40+ evalEffect (IdE (State v)) m
39
41
40execState :: s -> Eff (State s : es) a -> Eff es s
42execState :: s -> Eff (State s : es) a -> Eff es s
41execState s m = do
43execState s m = do
42− v <- impureEff_ $ newMVar s
44+ v <- unsafeEff_ $ newMVar s
43− evalEffect (State v) $ m *> get
45+ evalEffect (IdE (State v)) $ m *> get
44
46
45get :: State s :> es => Eff es s
47get :: State s :> es => Eff es s
46get = do
48get = do
47− State v <- getEffect
49+ IdE (State v) <- getEffect
48− impureEff_ $ readMVar v
50+ unsafeEff_ $ readMVar v
51+
52+gets :: State s :> es => (s -> a) -> Eff es a
53+gets f = f <$> get
49
54
50put :: State s :> es => s -> Eff es ()
55put :: State s :> es => s -> Eff es ()
51put s = do
56put s = do
52− State v <- getEffect
57+ IdE (State v) <- getEffect
53− impureEff_ . modifyMVar_ v $ \_ -> s `seq` pure s
58+ unsafeEff_ . modifyMVar_ v $ \_ -> s `seq` pure s
54
59
55state :: State s :> es => (s -> (a, s)) -> Eff es a
60state :: State s :> es => (s -> (a, s)) -> Eff es a
56state f = do
61state f = do
57− State v <- getEffect
62+ IdE (State v) <- getEffect
58− impureEff_ . modifyMVar v $ \s0 -> let (a, s) = f s0 in s `seq` pure (s, a)
63+ unsafeEff_ . modifyMVar v $ \s0 -> let (a, s) = f s0 in s `seq` pure (s, a)
59
64
60modify :: State s :> es => (s -> s) -> Eff es ()
65modify :: State s :> es => (s -> s) -> Eff es ()
61modify f = state (\s -> ((), f s))
66modify f = state (\s -> ((), f s))
62
67
63stateM :: State s :> es => (s -> Eff es (a, s)) -> Eff es a
68stateM :: State s :> es => (s -> Eff es (a, s)) -> Eff es a
64stateM f = do
69stateM f = do
65− State v <- getEffect
70+ IdE (State v) <- getEffect
66− impureEff $ \es -> modifyMVar v $ \s0 -> do
71+ unsafeEff $ \es -> modifyMVar v $ \s0 -> do
67 (a, s) <- unEff (f s0) es
72 (a, s) <- unEff (f s0) es
68 s `seq` pure (s, a)
73 s `seq` pure (s, a)
69
74
src/Effectful/Writer.hs+21−11
@@ -9,33 +9,43 @@ module Effectful.Writer
9 , listens
9 , listens
10 ) where
10 ) where
11
11
12−import Data.Coerce
12+import Control.Monad.Catch
13−import qualified Data.Semigroup as S
14
13
15−import Effectful.Internal.Has
14+import Effectful.Internal.Effect
15+import Effectful.Internal.Env
16import Effectful.Internal.Monad
16import Effectful.Internal.Monad
17
17
18-- | Provide access to a write only value of type @w@.
18-- | Provide access to a write only value of type @w@.
19−newtype Writer w = Writer w
19+newtype Writer w :: Effect where
20− deriving (S.Semigroup, Monoid)
20+ Writer :: w -> Writer w m r
21
21
22runWriter :: Monoid w => Eff (Writer w : es) a -> Eff es (a, w)
22runWriter :: Monoid w => Eff (Writer w : es) a -> Eff es (a, w)
23+runWriter m = do
23−runWriter = fmap coerce . runEffect mempty
24+ (a, IdE (Writer w)) <- runEffect (IdE (Writer mempty)) m
25+ pure (a, w)
24
26
25execWriter :: Monoid w => Eff (Writer w : es) a -> Eff es w
27execWriter :: Monoid w => Eff (Writer w : es) a -> Eff es w
28+execWriter m = do
26−execWriter = fmap coerce . execEffect mempty
29+ IdE (Writer w) <- execEffect (IdE (Writer mempty)) m
30+ pure w
27
31
28writer :: (Writer w :> es, Monoid w) => (a, w) -> Eff es a
32writer :: (Writer w :> es, Monoid w) => (a, w) -> Eff es a
29−writer (a, w) = stateEffect $ \w0 -> (a, w0 `mappend` Writer w)
33+writer (a, w) = stateEffect $ \(IdE (Writer w0)) -> (a, IdE (Writer (w0 `mappend` w)))
30
34
31tell :: (Writer w :> es, Monoid w) => w -> Eff es ()
35tell :: (Writer w :> es, Monoid w) => w -> Eff es ()
32−tell w = stateEffect $ \w0 -> ((), w0 `mappend` Writer w)
36+tell w = stateEffect $ \(IdE (Writer w0)) -> ((), IdE (Writer (w0 `mappend` w)))
33
37
34listen
38listen
35− :: (Writer w :> es, Monoid w)
39+ :: forall w es a. (Writer w :> es, Monoid w)
36 => Eff es a
40 => Eff es a
37 -> Eff es (a, w)
41 -> Eff es (a, w)
38−listen = fmap (\(a, Writer w) -> (a, w)) . listenEffect
42+listen (Eff m) = unsafeEff $ \es -> mask $ \restore -> do
43+ w0 <- unsafeStateEnv (\(IdE (Writer w)) -> (w, IdE (Writer mempty))) es
44+ -- If an exception is thrown, restore e0 and keep parts of e1.
45+ a <- restore (m es) `onException`
46+ unsafeModifyEnv (\(IdE (Writer w)) -> IdE (Writer (w0 `mappend` w))) es
47+ w1 <- unsafeStateEnv (\(IdE (Writer w)) -> (w, IdE (Writer (w0 `mappend` w)))) es
48+ pure (a, w1)
39
49
40listens
50listens
41 :: (Writer w :> es, Monoid w)
51 :: (Writer w :> es, Monoid w)
tests/Main.hs+36−6
@@ -8,8 +8,9 @@ import qualified Control.Monad.Catch as E
8import qualified Control.Exception.Lifted as LE
8import qualified Control.Exception.Lifted as LE
9import qualified UnliftIO.Exception as UE
9import qualified UnliftIO.Exception as UE
10
10
11−import Effectful
11+import Effectful.Interpreter
12−import Effectful.State
12+import Effectful.State.Dynamic
13+import Effectful.Monad
13
14
14main :: IO ()
15main :: IO ()
15main = defaultMain $ testGroup "effectful"
16main = defaultMain $ testGroup "effectful"
@@ -20,6 +21,7 @@ main = defaultMain $ testGroup "effectful"
20 , testCase "deep stack" test_deepStack
21 , testCase "deep stack" test_deepStack
21 , testCase "exceptions" test_exceptions
22 , testCase "exceptions" test_exceptions
22 , testCase "concurrency" test_concurrentState
23 , testCase "concurrency" test_concurrentState
24+ , testCase "local effects" test_localEffects
23 ]
25 ]
24 ]
26 ]
25
27
@@ -88,7 +90,7 @@ test_exceptions = runEffIO $ do
88 modify @Int (+2)
90 modify @Int (+2)
89
91
90test_concurrentState :: Assertion
92test_concurrentState :: Assertion
91−test_concurrentState = runEffIO . evalState x $ do
93+test_concurrentState = runEffIO . runHasInt x $ do
92 replicateConcurrently_ 2 $ do
94 replicateConcurrently_ 2 $ do
93 r <- goDownward 0
95 r <- goDownward 0
94 assertEqual_ "x = n" x r
96 assertEqual_ "x = n" x r
@@ -96,13 +98,41 @@ test_concurrentState = runEffIO . evalState x $ do
96 x :: Int
98 x :: Int
97 x = 1000000
99 x = 1000000
98
100
99− goDownward :: State Int :> es => Int -> Eff es Int
101+ goDownward :: HasInt :> es => Int -> Eff es Int
100− goDownward acc = get @Int >>= \case
102+ goDownward acc = getInt >>= \case
101 0 -> pure acc
103 0 -> pure acc
102 n -> do
104 n -> do
103− put $ n - 1
105+ putInt $ n - 1
104 goDownward $ acc + 1
106 goDownward $ acc + 1
105
107
108+test_localEffects :: Assertion
109+test_localEffects = runEffIO $ do
110+ x <- runHasInt 0 $ do
111+ putInt 1
112+ execState () $ do
113+ putInt 2
114+ execState () $ do
115+ putInt 4
116+ getInt
117+ assertEqual_ "correct x" 4 x
118+
119+runHasInt :: Int -> Eff (HasInt : es) a -> Eff es a
120+runHasInt n =
121+ -- reinterpret with redundant local effects
122+ reinterpret (evalState () . evalState n . evalState True) $ \case
123+ GetInt -> get
124+ PutInt i -> put i
125+
126+data HasInt :: Effect where
127+ GetInt :: HasInt m Int
128+ PutInt :: Int -> HasInt m ()
129+
130+getInt :: HasInt :> es => Eff es Int
131+getInt = send GetInt
132+
133+putInt :: HasInt :> es => Int -> Eff es ()
134+putInt = send . PutInt
135+
106----------------------------------------
136----------------------------------------
107-- Helpers
137-- Helpers
108
138