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

Base instances #206

Merged
merged 18 commits into from
Oct 6, 2019
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.hs-boot linguist-language=Haskell
2 changes: 1 addition & 1 deletion src/Control/Carrier.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module Control.Carrier
, send
) where

import Control.Carrier.Class
import {-# SOURCE #-} Control.Carrier.Class
import Control.Carrier.Pure
import Control.Effect.Class
import Control.Effect.Sum
Expand Down
19 changes: 19 additions & 0 deletions src/Control/Carrier.hs-boot
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{-# LANGUAGE ConstraintKinds #-}
module Control.Carrier
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We need .hs-boot files for Control.Carrier and Control.Carrier.Class to break the cycles in the module graph.

( -- * Re-exports
module Control.Carrier.Class
, module Control.Carrier.Pure
, module Control.Effect.Class
, (:+:)(..)
, Has
, send
) where

import {-# SOURCE #-} Control.Carrier.Class
import Control.Carrier.Pure
import Control.Effect.Class
import Control.Effect.Sum

type Has eff sig m = (Inject eff sig, Carrier sig m)

send :: Has eff sig m => eff m a -> m a
31 changes: 30 additions & 1 deletion src/Control/Carrier/Class.hs
Original file line number Diff line number Diff line change
@@ -1,11 +1,40 @@
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances, FunctionalDependencies #-}
module Control.Carrier.Class
( Carrier(..)
) where

import Control.Effect.Class
import Control.Effect.Choose (Choose(..))
import Control.Effect.Empty (Empty(..))
import Control.Effect.Error (Error(..))
import Control.Effect.NonDet (NonDet)
import Control.Effect.Reader (Reader(..))
import Control.Effect.Sum ((:+:)(..))
import Control.Effect.Writer (Writer(..))
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 payoff: we can import all of these effect modules right where we define the Carrier class.

import Control.Monad ((<=<))

-- | The class of carriers (results) for algebras (effect handlers) over signatures (effects), whose actions are given by the 'eff' method.
class (HFunctor sig, Monad m) => Carrier sig m | m -> sig where
-- | Construct a value in the carrier for an effect signature (typically a sum of a handled effect and any remaining effects).
eff :: sig m a -> m a


instance Carrier Empty Maybe where
eff Empty = Nothing
Copy link
Contributor Author

Choose a reason for hiding this comment

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

And that in turn allows us to implement Carrier instances for these effects without them being orphaned.


instance Carrier (Error e) (Either e) where
eff (Throw e) = Left e
eff (Catch m h k) = either (k <=< h) k m
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 called this branch base-instances both because they’re instances for types exported from basePrelude, even!—and because, like PureC and LiftC, they’re usable as the base of an effectful context. The signature does not mention :+:, since they’re not monad transformers.

Copy link
Collaborator

@patrickt patrickt Oct 4, 2019

Choose a reason for hiding this comment

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

Checking my understanding: I could have an effect stack State s :+: Error e and discharge it into an Either s a with just a runState?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, exactly.


instance Carrier (Reader r) ((->) r) where
eff (Ask k) r = k r r
eff (Local f m k) r = k (m (f r)) 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.

This allows you to use these instances anywhere expecting the type in question:

id' :: a -> a
id' = ask

Well, almost anywhere. I can’t figure out how to make this instance available to e.g. Control.Effect.Reader, and I suspect it’s due to some problem with the .hs-boot files. But this function compiles in this module, at least.


instance Carrier NonDet [] where
eff (L Empty) = []
eff (R (Choose k)) = k True ++ k False

instance Monoid w => Carrier (Writer w) ((,) w) where
eff (Tell w (w', k)) = (mappend w w', k)
eff (Listen m k) = uncurry k m
eff (Censor f (w, a) k) = let (w', a') = k a in (mappend (f w) w', a')
9 changes: 9 additions & 0 deletions src/Control/Carrier/Class.hs-boot
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{-# LANGUAGE FunctionalDependencies #-}
module Control.Carrier.Class
( Carrier(..)
) where

import Control.Effect.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.

In an effort to fix the aforementioned problem where Carrier (Reader r) ((->) r) isn’t available in e.g. Control.Effect.Reader, I attempted to duplicate the imports and instances here in the .hs-boot file, but it was not successful.


class (HFunctor sig, Monad m) => Carrier sig m | m -> sig where
eff :: sig m a -> m a
2 changes: 1 addition & 1 deletion src/Control/Carrier/Pure.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module Control.Carrier.Pure
) where

import Control.Applicative
import Control.Carrier.Class
import {-# SOURCE #-} Control.Carrier.Class
import Control.Effect.Pure
import Control.Monad.Fix
import Data.Coerce
Expand Down
2 changes: 1 addition & 1 deletion src/Control/Effect/Choose.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module Control.Effect.Choose
, Choosing(..)
) where

import Control.Carrier
import {-# SOURCE #-} Control.Carrier
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These directives indicate that it’s Control.Carrier that gets a .hs-boot file, and not, say, this module.

import Control.Effect.Empty
import Data.Bool (bool)
import Data.List.NonEmpty (NonEmpty (..))
Expand Down
2 changes: 1 addition & 1 deletion src/Control/Effect/Empty.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Control.Effect.Empty
, guard
) where

import Control.Carrier
import {-# SOURCE #-} Control.Carrier
import GHC.Generics (Generic1)

-- | An effect modelling nondeterminism without choice.
Expand Down
2 changes: 1 addition & 1 deletion src/Control/Effect/Error.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Control.Effect.Error
, catchError
) where

import Control.Carrier
import {-# SOURCE #-} Control.Carrier

data Error exc m k
= Throw exc
Expand Down
2 changes: 1 addition & 1 deletion src/Control/Effect/Reader.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module Control.Effect.Reader
, local
) where

import Control.Carrier
import {-# SOURCE #-} Control.Carrier

data Reader r m k
= Ask (r -> m k)
Expand Down
2 changes: 1 addition & 1 deletion src/Control/Effect/Writer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module Control.Effect.Writer
, censor
) where

import Control.Carrier
import {-# SOURCE #-} Control.Carrier

data Writer w m k
= Tell w (m k)
Expand Down