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

Church-encoded State carrier #363

Merged
merged 36 commits into from
Mar 13, 2020
Merged

Church-encoded State carrier #363

merged 36 commits into from
Mar 13, 2020

Conversation

robrix
Copy link
Contributor

@robrix robrix commented Mar 6, 2020

This PR defines a Church-encoded State carrier, equivalent to Codensity ((->) s).

I mostly wanted to see how the MonadFix instance would turn out, since I’m sort of combining this and the CutC instance for fused-effects-parser.

{-# INLINE execState #-}

-- | @since 1.1.0.0
newtype StateC s m a = StateC { runStateC :: forall r . (a -> s -> m r) -> s -> m r }
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 debated exporting a CPS handler for this à la runNonDet &c., but:

  1. following the extant examples would suggest naming it runState, but that name already has a well-understood meaning for State, so I’d have to come up with a new naming scheme;

  2. it’s not immediately clear whether it would be more useful as (a -> s -> m r) -> s -> StateC s m a -> m r or (a -> s -> m r) -> StateC s m a -> s -> m r; and

  3. I didn’t really want to have to document it.

I export the constructor (tho not the selector), so users are always free to define their own.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I wouldn’t be averse to exporting the constructor, fwiw.

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 do export the constructor, just not the selector.

test/State.hs Outdated
Comment on lines 13 to 20
import qualified Control.Carrier.State.Lazy as LazyStateC
import qualified Control.Carrier.State.Strict as StrictStateC
import qualified Control.Carrier.State.Church as C.Church
import qualified Control.Carrier.State.Lazy as C.Lazy
import qualified Control.Carrier.State.Strict as C.Strict
import Control.Effect.State
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
import qualified Control.Monad.Trans.State.Strict as StrictStateT
import qualified Control.Monad.Trans.RWS.Lazy as RWST.Lazy
import qualified Control.Monad.Trans.RWS.Strict as RWST.Strict
import qualified Control.Monad.Trans.State.Lazy as T.Lazy
import qualified Control.Monad.Trans.State.Strict as T.Strict
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 renamed all of these because they were overcomplicated and we’re probably going to end up extending them at least a little anyway.

Comment on lines +99 to +100
instance MonadFix m => MonadFix (StateC s m) where
mfix f = StateC $ \ k s -> mfix (runState s . f . snd) >>= uncurry (flip 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’m quite pleased with how this turned out.

Comment on lines 114 to 117
alg hom = \case
L (Get k) -> StateC $ \ k' s -> runState s (hom (k s)) >>= uncurry (flip k')
L (Put s k) -> StateC $ \ k' _ -> runState s (hom k) >>= uncurry (flip k')
R other -> StateC $ \ k s -> alg id (thread (s, ()) (uncurry runState . fmap hom) other) >>= uncurry (flip 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.

This turned out pretty nicely, but I’m not completely convinced that this is the best way to be threading the state through. I plan to revisit that in/after #361.

@patrickt
Copy link
Collaborator

patrickt commented Mar 6, 2020

I think this is a fine addition, though I think we should document something about its performance. Do we expect it to be better or worse than the s -> (s, a) form?

@robrix
Copy link
Contributor Author

robrix commented Mar 7, 2020

@patrickt: I don’t have a strong intuition, and in fact I don’t even think there’s going to be a cut and dried answer to that. My guess is that it’d depend pretty crucially on the carriers surrounding it. My goal is to try to provide some more church-encoded carriers and measure how they fare against the classical varieties in larger carrier compositions in e.g. Starlight or semantic.

@patrickt
Copy link
Collaborator

patrickt commented Mar 9, 2020

@robrix That’s fine by me. We should probably mention somewhere that the choice of the “best” carrier for state is contextual.

@robrix
Copy link
Contributor Author

robrix commented Mar 11, 2020

Yeah, I might go a bit further and recommend Strict.StateC as a default for the time being until we know more.

@robrix robrix merged commit ead3648 into master Mar 13, 2020
@robrix robrix deleted the separation-of-church-and-state branch March 13, 2020 22:01
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.

None yet

2 participants