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

Transformers instances #226

Merged
merged 38 commits into from
Oct 11, 2019
Merged

Transformers instances #226

merged 38 commits into from
Oct 11, 2019

Conversation

robrix
Copy link
Contributor

@robrix robrix commented Sep 28, 2019

This PR builds on #206 to offer Carrier instances for various monad transformers vended by transformers, allowing much greater integration between the two ecosystems.

@robrix robrix changed the base branch from base-instances to master October 9, 2019 04:35
Copy link
Contributor Author

@robrix robrix left a 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
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 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.

Comment on lines +74 to +78
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')
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 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.

Copy link
Collaborator

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.

Comment on lines 65 to 66
eff (L (Throw e)) = Except.throwE e
eff (L (Catch m h k)) = Except.catchE m h >>= k
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’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.

Copy link
Collaborator

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.

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 remarkable to me for many reasons, but most notably because it’s a visceral display of fused-effects as an algebraic interface to mtl.

…algebraic interface to transformers.

Comment on lines 80 to 88
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)
Copy link
Contributor Author

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.

Copy link
Collaborator

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.

Copy link
Collaborator

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.

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 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
Copy link
Contributor Author

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.

Comment on lines +7 to +10
newtype Choose m k
= Choose (Bool -> m k)

instance HFunctor Choose
Copy link
Contributor Author

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.

Comment on lines 8 to 12
data Error exc m k
= Throw exc
| forall b . Catch (m b) (exc -> m b) (b -> m k)

instance HFunctor (Error exc)
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 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(..))
Copy link
Contributor Author

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.

@robrix robrix marked this pull request as ready for review October 9, 2019 04:48
Copy link
Contributor Author

@robrix robrix left a 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.

Comment on lines +41 to +42
instance Carrier (Lift IO) IO where
eff = join . unLift
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 pretty obvious in hindsight; I expect it’ll obviate the need for the vast majority of runMs.

Comment on lines +44 to +45
instance Carrier Pure Identity where
eff v = case v of {}
Copy link
Contributor Author

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.

Copy link
Collaborator

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.

Comment on lines +74 to +78
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')
Copy link
Collaborator

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.

Comment on lines 80 to 88
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)
Copy link
Collaborator

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.

Comment on lines 80 to 88
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)
Copy link
Collaborator

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'
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is awesome.

Copy link
Collaborator

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?

Copy link
Contributor Author

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))
Copy link
Collaborator

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.

Comment on lines +44 to +45
instance Carrier Pure Identity where
eff v = case v of {}
Copy link
Collaborator

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.

Comment on lines 65 to 66
eff (L (Throw e)) = Except.throwE e
eff (L (Catch m h k)) = Except.catchE m h >>= k
Copy link
Collaborator

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.

@robrix
Copy link
Contributor Author

robrix commented Oct 11, 2019

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.
@robrix robrix merged commit 4abe611 into master Oct 11, 2019
@robrix robrix deleted the transformers-instances branch October 11, 2019 22:09
This was referenced Oct 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

LiftC IO is inconvenient
2 participants