-
Notifications
You must be signed in to change notification settings - Fork 52
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
Separate carrier and effect modules #204
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.
@@ -0,0 +1,11 @@ | |||
{-# LANGUAGE FunctionalDependencies #-} | |||
module Control.Carrier.Class | |||
( Carrier(..) |
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.
Instead of exporting run
, Member
, &c. from this module, we export them from Control.Carrier
. Likewise, send
has been moved into Control.Effect.Sum
.
@@ -0,0 +1,90 @@ | |||
{-# LANGUAGE DeriveTraversable, FlexibleInstances, MultiParamTypeClasses, RankNTypes, TypeOperators, UndecidableInstances #-} | |||
module Control.Carrier.Choose.Church |
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.
Carrier modules are now named descriptively of the implementation; .Church
here indicates that this is a church-encoded carrier for Choose
.
@@ -0,0 +1,77 @@ | |||
{-# LANGUAGE DeriveFunctor, FlexibleInstances, MultiParamTypeClasses, TypeOperators, UndecidableInstances #-} | |||
module Control.Carrier.Empty.Maybe |
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.
Likewise with direct-stye carriers (i.e. those returning m (f a)
for some functor f
), we name the module after the functor being returned.
@@ -0,0 +1,53 @@ | |||
{-# LANGUAGE FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses, TypeOperators, UndecidableInstances #-} | |||
module Control.Carrier.Fresh.Strict |
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.
With stateful carriers, we instead note .Lazy
or .Strict
. (We only provide .Strict
for the Fresh
and Writer
effects, but State
offers both varieties.)
import Control.Effect.Carrier | ||
import Control.Effect.State | ||
import Control.Carrier | ||
import Control.Carrier.State.Strict |
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 illustrates the benefit of the new hierarchy nicely: the properties of the selected carrier are described by the module being imported.
|
||
instance (Carrier sig m, Member Choose sig) => S.Semigroup (Choosing m a) where |
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.
Kinda snuck this in here, but it’s extremely convenient to have available. We’re using an explicit qualified Data.Semigroup as S
import here to accommodate ghc 8.2.
-- | Conditional failure, returning only if the condition is 'True'. | ||
guard :: (Carrier sig m, Member Empty sig) => Bool -> m () | ||
guard True = pure () | ||
guard False = 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.
Snuck this in as well, with essentially the same rationale as above.
instance MonadPlus (NonDetC m) | ||
-- | Map a 'Foldable' collection of values into a nondeterministic computation using the supplied action. | ||
foldMapA :: (Foldable t, Alternative m) => (a -> m b) -> t a -> m b | ||
foldMapA f = getAlt #. foldMap (Alt #. f) |
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.
Snuck this in too. I end up defining this in just about every project that I use Alternative
in; I’m considering proposing its addition to base
(tho prolly under a different name, maybe asumMap
because concat
: concatMap
:: asum
: asumMap
).
-- | ||
-- cf https://github.com/fused-effects/diffused-effects/pull/1#discussion_r323560758 | ||
(#.) :: Coercible b c => (b -> c) -> (a -> b) -> (a -> c) | ||
(#.) _ = coerce |
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.
🎩 @patrickt for pointing this out
-- | Construct a request for an effect to be interpreted by some handler later on. | ||
send :: (Member effect sig, Carrier sig m) => effect m a -> m a | ||
send = eff . inj | ||
{-# INLINE send #-} |
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.
Glad to have this, and the HFunctor
/Effect
instances, back where they belong.
🚀 |
This PR separates carriers and effects into disjoint hierarchies. Goals include:
Control.Effect.*
, implementations inControl.Carrier.*
.base
&transformers
. (cf Define (terminal) Carrier instances for common monads? #183) I intend to explore this in separate PRs.StateC
” instead of “whichever one the effect module re-exports.”Carrier
machinery.On the other hand, this is also quite a disruptive PR, since it means pretty significant changes for consumers of the library. I’d therefore like to get input from users on this!
It also builds on several other PRs currently slated for 0.6:
AddWe’ll do this if we ship this as 0.6, but we might just ship it + some other stuff as 1.0; so will tackle that separately.Control.Effect.Carrier
back in with a deprecation notice.Control.Effect.NonDet
back in, re-exportingChoose
&Empty
.