-
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
Remove HFunctor in preparation for improvements to Effect #295
Conversation
build-depends: | ||
base >= 4.9 && < 4.14 | ||
, deepseq ^>= 1.4.3 |
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.
Meant to 🔥 this when we eliminated Resumable
, but better now than after 1.0.
@@ -3,3 +3,4 @@ | |||
dist-newstyle | |||
stack.yaml | |||
cabal.project.local | |||
.ghci |
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’ve started using cabal exec ghci
more and so I’m using this to set stuff up for that, but I don’t think we want to commit it since others will likely be using cabal repl
and it might break or confuse things?
@@ -179,7 +179,7 @@ instance | |||
unReinterpretLogC k | |||
|
|||
R other -> | |||
ReinterpretLogC (alg (R (handleCoercible other))) | |||
ReinterpretLogC (handleCoercible 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.
This is where I’ve come down re: #235: this is all boilerplate, so let’s at least make it minimal, obvious, & consistent boilerplate.
-- | Thread a 'Coercible' carrier through an 'Effect'. | ||
-- | ||
-- This is applicable whenever @m@ is 'Coercible' to @n@, e.g. simple @newtype@s. | ||
-- | ||
-- @since 1.0.0.0 | ||
handleCoercible :: (Monad m, Effect eff, Member eff sig, Algebra sig n, Coercible m n) => eff m a -> n a | ||
handleCoercible = handleIdentity coerce | ||
{-# INLINE 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.
The new bit here is that handleCoercible
calls (via handleIdentity
) send
, and thus no longer requires you to count R
s on the left.
instance (Algebra sig m, Effect sig) => Algebra (Error e :+: sig) (ErrorC e m) where | ||
alg = ErrorC . alg . handleCoercible | ||
instance Algebra sig m => Algebra (Error e :+: sig) (ErrorC e m) where | ||
-- NB: 'send' (& thus 'handleCoercible') can’t send sums, so we decompose the sum manually. | ||
alg (L (L op)) = ErrorC (handleCoercible op) | ||
alg (L (R op)) = ErrorC (handleCoercible op) | ||
alg (R op) = ErrorC (handleCoercible op) |
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 the one weird case: because handleCoercible
uses send
, and because send
uses Member
, and because Member
can’t resolve sums of effects on the left, we have to decompose the sum here instead of doing just alg = ErrorC . handleCoercible
.
I wouldn’t expect end users to have to do this very often, however, since it’s not very often you want to delegate all of the effects you handle to something else (how I wish we could simply GND-derive the instance!), or that (as in this case), you’re handling multiple effects combined in a sum, let alone both.
=> (forall x . eff m x -> m x) | ||
-> (forall s . Reifies s (Handler eff m) => InterpretC s eff m a) | ||
-> m a | ||
runInterpret f m = reify (Handler (InterpretC . f . handleCoercible)) (go m) where | ||
runInterpret f m = reify (Handler (InterpretC . fmap runIdentity . f . handle (Identity ()) (fmap Identity . coerce))) (go m) 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.
This is the other casualty of handleCoercible
: we can’t use it here any more. I’m ok with that, again because this is something most users are unlikely to have to do themselves. In particular, users of runInterpret
itself certainly don’t—runInterpretState
bears no such scar.
instance HFunctor (Catch e) where | ||
hmap f (Catch m h k) = Catch (f m) (f . h) (f . 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.
And now the good bit: 🔥
@@ -52,7 +52,7 @@ import qualified Data.Semigroup as S | |||
-- | |||
-- @since 1.0.0.0 | |||
(<|>) :: Has Choose sig m => m a -> m a -> m a | |||
(<|>) a b = send (Choose (bool b a)) | |||
a <|> b = send (Choose (bool b a)) |
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 really couldn’t tell you why I wrote it like that but when I noticed it I had to fix 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.
I love this. One fewer typeclass required to get up and running is an unquestionably good thing.
Revert #295, reintroducing HFunctor.
This PR:
HFunctor
.alg
intohandle…
functions. (See also Allow Effect instances to constrain the context functor #296 & Rename handle to thread #300.)