Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More transformers instances #366

Merged
merged 19 commits into from
Mar 13, 2020
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

- Redefines all effects as GADTs. Since we no longer require `Functor`, `HFunctor`, or `Effect` instances, we no longer need to use continuations to allow distinct result types per constructor. `Algebra` instances for these effects can be ported forwards by removing the continuations. User-defined effects are not impacted, but we recommend migrating to GADT definitions of them for convenience and ease of comprehension going forwards. ([#365](https://github.com/fused-effects/fused-effects/pull/365))

- Defines `Algebra` instances for `Control.Monad.Trans.Maybe.MaybeT`, `Control.Monad.Trans.RWS.CPS`, and `Control.Monad.Trans.Writer.CPS`. ([#366](https://github.com/fused-effects/fused-effects/pull/366))


# v1.0.2.0

Expand Down
46 changes: 41 additions & 5 deletions src/Control/Algebra.hs
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,24 @@ import Control.Effect.Throw.Internal
import Control.Effect.Writer.Internal
import qualified Control.Monad.Trans.Except as Except
import qualified Control.Monad.Trans.Identity as Identity
import qualified Control.Monad.Trans.Maybe as Maybe
import qualified Control.Monad.Trans.Reader as Reader
#if MIN_VERSION_transformers(0,5,6)
import qualified Control.Monad.Trans.RWS.CPS as RWS.CPS
#endif
import qualified Control.Monad.Trans.RWS.Lazy as RWS.Lazy
import qualified Control.Monad.Trans.RWS.Strict as RWS.Strict
import qualified Control.Monad.Trans.State.Lazy as State.Lazy
import qualified Control.Monad.Trans.State.Strict as State.Strict
#if MIN_VERSION_transformers(0,5,6)
import qualified Control.Monad.Trans.Writer.CPS as Writer.CPS
#endif
import qualified Control.Monad.Trans.Writer.Lazy as Writer.Lazy
import qualified Control.Monad.Trans.Writer.Strict as Writer.Strict
import Data.Functor.Compose
import Data.Functor.Identity
import Data.List.NonEmpty (NonEmpty(..))
import Data.Monoid
import Data.Tuple (swap)

-- | The class of carriers (results) for algebras (effect handlers) over signatures (effects), whose actions are given by the 'alg' method.
--
Expand Down Expand Up @@ -201,6 +207,11 @@ deriving instance Algebra sig m => Algebra sig (Ap m)
-- @since 1.0.1.0
deriving instance Algebra sig m => Algebra sig (Alt m)

instance Algebra sig m => Algebra (Empty :+: sig) (Maybe.MaybeT m) where
alg hdl sig ctx = case sig of
L Empty -> Maybe.MaybeT (pure Nothing)
R other -> Maybe.MaybeT $ thread (maybe (pure Nothing) Maybe.runMaybeT) hdl other (Just ctx)

Comment on lines +210 to +214
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I honestly don’t know why I didn’t implement this before.

instance Algebra sig m => Algebra (Reader r :+: sig) (Reader.ReaderT r m) where
alg hdl sig ctx = case sig of
L Ask -> Reader.asks (<$ ctx)
Expand All @@ -214,10 +225,26 @@ toRWSTF :: Monoid w => w -> (a, s, w) -> RWSTF w s a
toRWSTF w (a, s, w') = RWSTF (a, s, mappend w w')
{-# INLINE toRWSTF #-}

newtype Swap s a = Swap { getSwap :: (a, s) }
deriving (Functor)
Comment on lines +228 to +229
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is handy for giving us a Functor instance for the backwards pairs transformers is so fond of.


swapAndLift :: Functor ctx => (ctx a, w) -> ctx (w, a)
swapAndLift p = (,) (snd p) <$> fst p
{-# INLINE swapAndLift #-}

#if MIN_VERSION_transformers(0,5,6)
instance (Algebra sig m, Monoid w) => Algebra (Reader r :+: Writer w :+: State s :+: sig) (RWS.CPS.RWST r w s m) where
alg hdl sig ctx = case sig of
L Ask -> RWS.CPS.asks (<$ ctx)
L (Local f m) -> RWS.CPS.local f (hdl (m <$ ctx))
R (L (Tell w)) -> ctx <$ RWS.CPS.tell w
R (L (Listen m)) -> swapAndLift <$> RWS.CPS.listen (hdl (m <$ ctx))
R (L (Censor f m)) -> RWS.CPS.censor f (hdl (m <$ ctx))
R (R (L Get)) -> RWS.CPS.gets (<$ ctx)
R (R (L (Put s))) -> ctx <$ RWS.CPS.put s
R (R (R other)) -> RWS.CPS.rwsT $ \ r s -> unRWSTF <$> thread (\ (RWSTF (x, s, w)) -> toRWSTF w <$> RWS.CPS.runRWST x r s) hdl other (RWSTF (ctx, s, mempty))
#endif

instance (Algebra sig m, Monoid w) => Algebra (Reader r :+: Writer w :+: State s :+: sig) (RWS.Lazy.RWST r w s m) where
alg hdl sig ctx = case sig of
L Ask -> RWS.Lazy.asks (<$ ctx)
Expand All @@ -244,24 +271,33 @@ instance Algebra sig m => Algebra (State s :+: sig) (State.Lazy.StateT s m) wher
alg hdl sig ctx = case sig of
L Get -> State.Lazy.gets (<$ ctx)
L (Put s) -> ctx <$ State.Lazy.put s
R other -> State.Lazy.StateT $ \ s -> swap <$> thread (\ (s, x) -> swap <$> State.Lazy.runStateT x s) hdl other (s, ctx)
R other -> State.Lazy.StateT $ \ s -> getSwap <$> thread (fmap Swap . uncurry State.Lazy.runStateT . getSwap) hdl other (Swap (ctx, s))

instance Algebra sig m => Algebra (State s :+: sig) (State.Strict.StateT s m) where
alg hdl sig ctx = case sig of
L Get -> State.Strict.gets (<$ ctx)
L (Put s) -> ctx <$ State.Strict.put s
R other -> State.Strict.StateT $ \ s -> swap <$> thread (\ (s, x) -> swap <$> State.Strict.runStateT x s) hdl other (s, ctx)
R other -> State.Strict.StateT $ \ s -> getSwap <$> thread (fmap Swap . uncurry State.Strict.runStateT . getSwap) hdl other (Swap (ctx, s))

#if MIN_VERSION_transformers(0,5,6)
instance (Algebra sig m, Monoid w) => Algebra (Writer w :+: sig) (Writer.CPS.WriterT w m) where
alg hdl sig ctx = case sig of
L (Tell w) -> ctx <$ Writer.CPS.tell w
L (Listen m) -> swapAndLift <$> Writer.CPS.listen (hdl (m <$ ctx))
L (Censor f m) -> Writer.CPS.censor f (hdl (m <$ ctx))
R other -> Writer.CPS.writerT $ getSwap <$> thread (\ (Swap (x, s)) -> Swap . fmap (mappend s) <$> Writer.CPS.runWriterT x) hdl other (Swap (ctx, mempty))
#endif
Comment on lines +282 to +289
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had implemented this before, but removed it because it was unavailable on some of our ghcs. But since we added CPP to support the Ap instance I figured I might as well add it back in.


instance (Algebra sig m, Monoid w) => Algebra (Writer w :+: sig) (Writer.Lazy.WriterT w m) where
alg hdl sig ctx = case sig of
L (Tell w) -> ctx <$ Writer.Lazy.tell w
L (Listen m) -> swapAndLift <$> Writer.Lazy.listen (hdl (m <$ ctx))
L (Censor f m) -> Writer.Lazy.censor f (hdl (m <$ ctx))
R other -> Writer.Lazy.WriterT $ swap <$> thread (\ (s, x) -> swap . fmap (mappend s) <$> Writer.Lazy.runWriterT x) hdl other (mempty, ctx)
R other -> Writer.Lazy.WriterT $ getSwap <$> thread (\ (Swap (x, s)) -> Swap . fmap (mappend s) <$> Writer.Lazy.runWriterT x) hdl other (Swap (ctx, mempty))

instance (Algebra sig m, Monoid w) => Algebra (Writer w :+: sig) (Writer.Strict.WriterT w m) where
alg hdl sig ctx = case sig of
L (Tell w) -> ctx <$ Writer.Strict.tell w
L (Listen m) -> swapAndLift <$> Writer.Strict.listen (hdl (m <$ ctx))
L (Censor f m) -> Writer.Strict.censor f (hdl (m <$ ctx))
R other -> Writer.Strict.WriterT $ swap <$> thread (\ (s, x) -> swap . fmap (mappend s) <$> Writer.Strict.runWriterT x) hdl other (mempty, ctx)
R other -> Writer.Strict.WriterT $ getSwap <$> thread (\ (Swap (x, s)) -> Swap . fmap (mappend s) <$> Writer.Strict.runWriterT x) hdl other (Swap (ctx, mempty))
8 changes: 1 addition & 7 deletions src/Control/Carrier/Empty/Maybe.hs
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,9 @@ runEmpty (EmptyC m) = runMaybeT m

-- | @since 1.0.0.0
newtype EmptyC m a = EmptyC (MaybeT m a)
deriving (Applicative, Functor, Monad, MonadFix, MonadIO, MonadTrans)
deriving (Algebra (Empty :+: sig), Applicative, Functor, Monad, MonadFix, MonadIO, MonadTrans)

-- | 'EmptyC' passes 'Fail.MonadFail' operations along to the underlying monad @m@, rather than interpreting it as a synonym for 'empty' à la 'MaybeT'.
instance Fail.MonadFail m => Fail.MonadFail (EmptyC m) where
fail = lift . Fail.fail
{-# INLINE fail #-}

instance Algebra sig m => Algebra (Empty :+: sig) (EmptyC m) where
alg hdl sig ctx = case sig of
L Empty -> EmptyC (MaybeT (pure Nothing))
R other -> EmptyC (MaybeT (thread (maybe (pure Nothing) runEmpty) hdl other (Just ctx)))
{-# INLINE alg #-}
3 changes: 2 additions & 1 deletion src/Control/Effect/Empty.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ This can be seen as similar to 'Control.Effect.Fail.Fail', but without an error

Predefined carriers:

* "Control.Carrier.Empty.Maybe".
* @"Control.Carrier.Empty.Maybe".'Control.Carrier.Empty.Maybe.MaybeC'@
* @"Control.Monad.Trans.Maybe".'Control.Monad.Trans.Maybe.MaybeT'@
* If 'Empty' is the last effect in a stack, it can be interpreted directly to a 'Maybe'.

@since 1.0.0.0
Expand Down
1 change: 1 addition & 0 deletions src/Control/Effect/State.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Predefined carriers:

* "Control.Carrier.State.Strict", which is strict in its updates.
* "Control.Carrier.State.Lazy", which is lazy in its updates. This enables more programs to terminate, such as cyclic computations expressed with @MonadFix@ or @-XRecursiveDo@, at the cost of efficiency.
* "Control.Monad.Trans.RWS.CPS"
* "Control.Monad.Trans.RWS.Lazy"
* "Control.Monad.Trans.RWS.Strict"
* "Control.Monad.Trans.State.Lazy"
Expand Down
2 changes: 2 additions & 0 deletions src/Control/Effect/Writer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
Predefined carriers:

* "Control.Carrier.Writer.Strict". (A lazy carrier is not provided due to the inherent space leaks associated with lazy writer monads.)
* "Control.Monad.Trans.RWS.CPS"
* "Control.Monad.Trans.RWS.Lazy"
* "Control.Monad.Trans.RWS.Strict"
* "Control.Monad.Trans.Writer.CPS"
* "Control.Monad.Trans.Writer.Lazy"
* "Control.Monad.Trans.Writer.Strict"
* If 'Writer' @w@ is the last effect in a stack, it can be interpreted to a tuple @(w, a)@ given some result type @a@ and the presence of a 'Monoid' instance for @w@.
Expand Down
2 changes: 2 additions & 0 deletions test/Empty.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module Empty
) where

import qualified Control.Carrier.Empty.Maybe as EmptyC
import qualified Control.Monad.Trans.Maybe as MaybeT
import Control.Effect.Empty
import Data.Maybe (maybeToList)
import Gen
Expand All @@ -26,6 +27,7 @@ tests = testGroup "Empty"
, testMonadFix
, testEmpty
] >>= ($ runL (fmap maybeToList . EmptyC.runEmpty))
, testGroup "MaybeT" $ testEmpty (runL (fmap maybeToList . MaybeT.runMaybeT))
, testGroup "Maybe" $ testEmpty (runL (pure . maybeToList))
] where
testMonad run = Monad.test (m gen0 (\ _ _ -> [])) a b c initial run
Expand Down
7 changes: 7 additions & 0 deletions test/State.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
Expand All @@ -13,6 +14,9 @@ module State
import qualified Control.Carrier.State.Lazy as LazyStateC
import qualified Control.Carrier.State.Strict as StrictStateC
import Control.Effect.State
#if MIN_VERSION_transformers(0,5,6)
import qualified Control.Monad.Trans.RWS.CPS as CPSRWST
#endif
import qualified Control.Monad.Trans.RWS.Lazy as LazyRWST
import qualified Control.Monad.Trans.RWS.Strict as StrictRWST
import qualified Control.Monad.Trans.State.Lazy as LazyStateT
Expand All @@ -38,6 +42,9 @@ tests = testGroup "State"
] >>= ($ runC StrictStateC.runState)
, testGroup "StateT (Lazy)" $ testState (runC (fmap (fmap swap) . flip LazyStateT.runStateT))
, testGroup "StateT (Strict)" $ testState (runC (fmap (fmap swap) . flip StrictStateT.runStateT))
#if MIN_VERSION_transformers(0,5,6)
, testGroup "RWST (CPS)" $ testState (runC (runRWST CPSRWST.runRWST))
#endif
, testGroup "RWST (Lazy)" $ testState (runC (runRWST LazyRWST.runRWST))
, testGroup "RWST (Strict)" $ testState (runC (runRWST StrictRWST.runRWST))
] where
Expand Down
13 changes: 13 additions & 0 deletions test/Writer.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
Expand All @@ -14,8 +15,14 @@ module Writer
import Control.Arrow ((&&&))
import qualified Control.Carrier.Writer.Strict as WriterC
import Control.Effect.Writer
#if MIN_VERSION_transformers(0,5,6)
import qualified Control.Monad.Trans.RWS.CPS as CPSRWST
#endif
import qualified Control.Monad.Trans.RWS.Lazy as LazyRWST
import qualified Control.Monad.Trans.RWS.Strict as StrictRWST
#if MIN_VERSION_transformers(0,5,6)
import qualified Control.Monad.Trans.Writer.CPS as CPSWriterT
#endif
import qualified Control.Monad.Trans.Writer.Lazy as LazyWriterT
import qualified Control.Monad.Trans.Writer.Strict as StrictWriterT
import Data.Bifunctor (first)
Expand All @@ -34,8 +41,14 @@ tests = testGroup "Writer"
, testWriter
] >>= ($ runL WriterC.runWriter)
, testGroup "(,)" $ testWriter (runL pure)
#if MIN_VERSION_transformers(0,5,6)
, testGroup "WriterT (CPS)" $ testWriter (runL (fmap swap . CPSWriterT.runWriterT))
#endif
, testGroup "WriterT (Lazy)" $ testWriter (runL (fmap swap . LazyWriterT.runWriterT))
, testGroup "WriterT (Strict)" $ testWriter (runL (fmap swap . StrictWriterT.runWriterT))
#if MIN_VERSION_transformers(0,5,6)
, testGroup "RWST (CPS)" $ testWriter (runL (runRWST CPSRWST.runRWST))
#endif
, testGroup "RWST (Lazy)" $ testWriter (runL (runRWST LazyRWST.runRWST))
, testGroup "RWST (Strict)" $ testWriter (runL (runRWST StrictRWST.runRWST))
] where
Expand Down