-
Notifications
You must be signed in to change notification settings - Fork 53
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
Transformers instances #226
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ready for review.
@@ -9,7 +9,7 @@ module Control.Carrier | |||
, send | |||
) where | |||
|
|||
import {-# SOURCE #-} Control.Carrier.Class | |||
import Control.Carrier.Class |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was running into some weird compilation errors, and it occurred to me that I’d put the .hs-boot
files on the wrong edges. Putting them on the Control.Effect.*
types allows me to limit the boot files to just the datatypes & instances needed to define the base
& transformers
instances.
newtype RWSTF w s a = RWSTF { unRWSTF :: (a, s, w) } | ||
deriving (Functor) | ||
|
||
toRWSTF :: Monoid w => w -> (a, s, w) -> RWSTF w s a | ||
toRWSTF w (a, s, w') = RWSTF (a, s, mappend w w') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a lot easier than trying to futz around with swapping triples. Also, did you know that there’s no Functor
instance for (,,)
? What a pain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Jesus, yeah, I remember that. That shit sucks. Probably wanna INLINE
this bad boy just to be sure, though.
src/Control/Carrier/Class.hs
Outdated
eff (L (Throw e)) = Except.throwE e | ||
eff (L (Catch m h k)) = Except.catchE m h >>= k |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’d been writing these all out long-form for a while when it hit me that I can do it so much more easily with the operations transformers
exports.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is remarkable to me for many reasons, but most notably because it’s a visceral display of fused-effects
as an algebraic interface to mtl
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is remarkable to me for many reasons, but most notably because it’s a visceral display of
fused-effects
as an algebraic interface tomtl
.
…algebraic interface to transformers
.
src/Control/Carrier/Class.hs
Outdated
instance (Carrier sig m, Effect sig, Monoid w) => Carrier (Reader r :+: Writer w :+: State s :+: sig) (RWS.CPS.RWST r w s m) where | ||
eff (L (Ask k)) = RWS.CPS.ask >>= k | ||
eff (L (Local f m k)) = RWS.CPS.local f m >>= k | ||
eff (R (L (Tell w k))) = RWS.CPS.tell w *> k | ||
eff (R (L (Listen m k))) = RWS.CPS.listen m >>= uncurry (flip k) | ||
eff (R (L (Censor f m k))) = RWS.CPS.censor f m >>= k | ||
eff (R (R (L (Get k)))) = RWS.CPS.get >>= k | ||
eff (R (R (L (Put s k)))) = RWS.CPS.put s *> k | ||
eff (R (R (R other))) = RWS.CPS.rwsT $ \ r s -> unRWSTF <$> eff (handle (RWSTF ((), s, mempty)) (\ (RWSTF (x, s, w)) -> toRWSTF w <$> RWS.CPS.runRWST x r s) other) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My name is Ozymandias, King of Kings;
Look on my Works, ye Mighty, and despair!
I may never understand what RWS
is for, but at least I know how to write a Carrier
instance for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bro, how have we never talked about this? I’ve run businesses atop RWS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay maybe that’s a slight exaggeration. I have used RWS in production. It’s good. And we have an issue about this (#162) that this should address.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to know more!
eff (L (Throw e)) = ErrorC (ExceptT (pure (Left e))) | ||
eff (L (Catch m h k)) = ErrorC (ExceptT (runError m >>= either (either (pure . Left) (runError . k) <=< runError . h) (runError . k))) | ||
eff (R other) = ErrorC (ExceptT (eff (handle (Right ()) (either (pure . Left) runError) other))) | ||
eff = ErrorC . eff . handleCoercible |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, so happy with this.
newtype Choose m k | ||
= Choose (Bool -> m k) | ||
|
||
instance HFunctor Choose |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The .hs-boot
files for the effects have to export the effect datatypes, but also the HFunctor
instance, since that’s a superclass of Carrier
. We don’t need the Effect
or Functor
instances, since they’re not involved in the base
or transformers
instances; and certainly not the Generic1
instances.
src/Control/Effect/Error.hs-boot
Outdated
data Error exc m k | ||
= Throw exc | ||
| forall b . Catch (m b) (exc -> m b) (b -> m k) | ||
|
||
instance HFunctor (Error exc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will get split up following #247, of course.
import Control.Effect.Choose hiding ((<|>), many, some, optional) | ||
import Control.Effect.Empty hiding (empty, guard) | ||
import Control.Effect.Choose (Choose(..)) | ||
import Control.Effect.Empty (Empty(..)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Been meaning to do this for a while—it’s simpler to only list what we want to re-export.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a couple of valuable instances: Carrier (Lift IO) IO
and Carrier Pure Identity
.
instance Carrier (Lift IO) IO where | ||
eff = join . unLift |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is pretty obvious in hindsight; I expect it’ll obviate the need for the vast majority of runM
s.
instance Carrier Pure Identity where | ||
eff v = case v of {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
transformers
stacks tend to end with Identity
or IO
, so this seems pretty natural to support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any use of EmptyCase
is a win.
newtype RWSTF w s a = RWSTF { unRWSTF :: (a, s, w) } | ||
deriving (Functor) | ||
|
||
toRWSTF :: Monoid w => w -> (a, s, w) -> RWSTF w s a | ||
toRWSTF w (a, s, w') = RWSTF (a, s, mappend w w') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Jesus, yeah, I remember that. That shit sucks. Probably wanna INLINE
this bad boy just to be sure, though.
src/Control/Carrier/Class.hs
Outdated
instance (Carrier sig m, Effect sig, Monoid w) => Carrier (Reader r :+: Writer w :+: State s :+: sig) (RWS.CPS.RWST r w s m) where | ||
eff (L (Ask k)) = RWS.CPS.ask >>= k | ||
eff (L (Local f m k)) = RWS.CPS.local f m >>= k | ||
eff (R (L (Tell w k))) = RWS.CPS.tell w *> k | ||
eff (R (L (Listen m k))) = RWS.CPS.listen m >>= uncurry (flip k) | ||
eff (R (L (Censor f m k))) = RWS.CPS.censor f m >>= k | ||
eff (R (R (L (Get k)))) = RWS.CPS.get >>= k | ||
eff (R (R (L (Put s k)))) = RWS.CPS.put s *> k | ||
eff (R (R (R other))) = RWS.CPS.rwsT $ \ r s -> unRWSTF <$> eff (handle (RWSTF ((), s, mempty)) (\ (RWSTF (x, s, w)) -> toRWSTF w <$> RWS.CPS.runRWST x r s) other) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bro, how have we never talked about this? I’ve run businesses atop RWS.
src/Control/Carrier/Class.hs
Outdated
instance (Carrier sig m, Effect sig, Monoid w) => Carrier (Reader r :+: Writer w :+: State s :+: sig) (RWS.CPS.RWST r w s m) where | ||
eff (L (Ask k)) = RWS.CPS.ask >>= k | ||
eff (L (Local f m k)) = RWS.CPS.local f m >>= k | ||
eff (R (L (Tell w k))) = RWS.CPS.tell w *> k | ||
eff (R (L (Listen m k))) = RWS.CPS.listen m >>= uncurry (flip k) | ||
eff (R (L (Censor f m k))) = RWS.CPS.censor f m >>= k | ||
eff (R (R (L (Get k)))) = RWS.CPS.get >>= k | ||
eff (R (R (L (Put s k)))) = RWS.CPS.put s *> k | ||
eff (R (R (R other))) = RWS.CPS.rwsT $ \ r s -> unRWSTF <$> eff (handle (RWSTF ((), s, mempty)) (\ (RWSTF (x, s, w)) -> toRWSTF w <$> RWS.CPS.runRWST x r s) other) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay maybe that’s a slight exaggeration. I have used RWS in production. It’s good. And we have an issue about this (#162) that this should address.
Predefined carriers: | ||
|
||
* "Control.Carrier.Lift" | ||
* 'IO' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we give an example somewhere of using runM
versus discharging to IO
directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, but I don’t think we need to hold up 1.0 for it.
@@ -14,6 +14,10 @@ | |||
|
|||
- Adds a `NonDetC` carrier for `NonDet` in `Control.Effect.NonDet.Maybe`. ([#227](https://github.com/fused-effects/fused-effects/pull/227)) | |||
|
|||
- Defines `Carrier` instances for a number of types in `base`, including `Either`, `Maybe`, `[]`, and `IO`. ([#206](https://github.com/fused-effects/fused-effects/pull/206)) | |||
|
|||
- Defines `Carrier` instances for a number of types in `transformers`. ([#226](https://github.com/fused-effects/fused-effects/pull/226)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably add, like, a docs/carriers.md
document detailing what the deal is with what Carrier
people should use.
instance Carrier Pure Identity where | ||
eff v = case v of {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any use of EmptyCase
is a win.
src/Control/Carrier/Class.hs
Outdated
eff (L (Throw e)) = Except.throwE e | ||
eff (L (Catch m h k)) = Except.catchE m h >>= k |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is remarkable to me for many reasons, but most notably because it’s a visceral display of fused-effects
as an algebraic interface to mtl
.
Ughhhh the CPS Writer & RWS transformers require 8.6+ 😭 |
These are unavailable prior to ghc 8.6, and I’d rather these instances than ghc 8.2 & 8.4 or have to use CPP.
This PR builds on #206 to offer
Carrier
instances for various monad transformers vended bytransformers
, allowing much greater integration between the two ecosystems.Carrier
instances for various types intransformers
.Carrier
instance forIO
. (Fixes LiftC IO is inconvenient #258.)Carrier
instance forIdentity
.