capability 0.3.0.0 → 0.4.0.0
raw patch · 24 files changed
+745/−92 lines, 24 filesdep +reflectiondep ~basedep ~constraintsdep ~containersPVP ok
version bump matches the API change (PVP)
Dependencies added: reflection
Dependency ranges changed: base, constraints, containers, dlist, generic-lens, hspec, lens, mtl, silently, streaming, temporary, text, unliftio, unliftio-core
API changes (from Hackage documentation)
- Capability.Error: instance forall k1 k2 (oldtag :: k2) e (m :: * -> *) (newtag :: k1). Capability.Error.HasCatch oldtag e m => Capability.Error.HasCatch newtag e (Capability.Accessors.Rename oldtag m)
- Capability.Error: instance forall k1 k2 (oldtag :: k2) e (m :: * -> *) (newtag :: k1). Capability.Error.HasThrow oldtag e m => Capability.Error.HasThrow newtag e (Capability.Accessors.Rename oldtag m)
+ Capability.Error: data family Reified (tag :: k) (c :: Capability) (m :: Type -> Type)
+ Capability.Error: instance forall k (m :: * -> *) s (tag :: k) e. (GHC.Base.Monad m, Data.Reflection.Reifies s (Capability.Reflection.Reified tag (Capability.Error.HasCatch tag e) m)) => Capability.Error.HasCatch tag e (Capability.Reflection.Reflected s (Capability.Error.HasCatch tag e) m)
+ Capability.Error: instance forall k (m :: * -> *) s (tag :: k) e. (GHC.Base.Monad m, Data.Reflection.Reifies s (Capability.Reflection.Reified tag (Capability.Error.HasCatch tag e) m)) => Capability.Error.HasThrow tag e (Capability.Reflection.Reflected s (Capability.Error.HasCatch tag e) m)
+ Capability.Error: instance forall k (m :: * -> *) s (tag :: k) e. (GHC.Base.Monad m, Data.Reflection.Reifies s (Capability.Reflection.Reified tag (Capability.Error.HasThrow tag e) m)) => Capability.Error.HasThrow tag e (Capability.Reflection.Reflected s (Capability.Error.HasThrow tag e) m)
+ Capability.Error: instance forall k1 k2 (oldtag :: k1) e (m :: * -> *) (newtag :: k2). Capability.Error.HasCatch oldtag e m => Capability.Error.HasCatch newtag e (Capability.Accessors.Rename oldtag m)
+ Capability.Error: instance forall k1 k2 (oldtag :: k1) e (m :: * -> *) (newtag :: k2). Capability.Error.HasThrow oldtag e m => Capability.Error.HasThrow newtag e (Capability.Accessors.Rename oldtag m)
+ Capability.Reader: data family Reified (tag :: k) (c :: Capability) (m :: Type -> Type)
+ Capability.Reflection: Proxy :: Proxy (t :: k)
+ Capability.Reflection: Reflect :: m a -> Reflected (s :: Type) (c :: Capability) (m :: Type -> Type) (a :: Type)
+ Capability.Reflection: class Reifies (s :: k) a | s -> a
+ Capability.Reflection: data Proxy (t :: k)
+ Capability.Reflection: data family Reified (tag :: k) (c :: Capability) (m :: Type -> Type)
+ Capability.Reflection: instance GHC.Base.Applicative m => GHC.Base.Applicative (Capability.Reflection.Reflected s c m)
+ Capability.Reflection: instance GHC.Base.Functor m => GHC.Base.Functor (Capability.Reflection.Reflected s c m)
+ Capability.Reflection: instance GHC.Base.Monad m => GHC.Base.Monad (Capability.Reflection.Reflected s c m)
+ Capability.Reflection: interpret :: forall tag (cs :: [Capability]) c m a. (Monad m, All cs m, forall s. Reifies s (Reified tag c m) => c (Reflected s c m)) => Reified tag c m -> (forall m'. All (c : cs) m' => m' a) -> m a
+ Capability.Reflection: interpret_ :: forall tag c m a. (Monad m, forall s. Reifies s (Reified tag c m) => c (Reflected s c m)) => Reified tag c m -> (forall m'. c m' => m' a) -> m a
+ Capability.Reflection: newtype Reflected (s :: Type) (c :: Capability) (m :: Type -> Type) (a :: Type)
+ Capability.Reflection: reflect :: Reifies s a => proxy s -> a
+ Capability.Reflection: reified :: forall s tag c m. Reifies s (Reified tag c m) => Reified tag c m
+ Capability.Reflection: reify :: a -> (forall s. Reifies s a => Proxy s -> r) -> r
+ Capability.State: data family Reified (tag :: k) (c :: Capability) (m :: Type -> Type)
+ Capability.Writer: data family Reified (tag :: k) (c :: Capability) (m :: Type -> Type)
+ Capability.Writer: instance forall k (m :: * -> *) w s (tag :: k). (GHC.Base.Monad m, GHC.Base.Monoid w, Data.Reflection.Reifies s (Capability.Reflection.Reified tag (Capability.Writer.HasWriter tag w) m)) => Capability.Writer.HasWriter tag w (Capability.Reflection.Reflected s (Capability.Writer.HasWriter tag w) m)
+ Capability.Writer: instance forall k w (m :: * -> *) s (tag :: k). (GHC.Base.Monoid w, GHC.Base.Monad m, Data.Reflection.Reifies s (Capability.Reflection.Reified tag (Capability.Writer.HasWriter tag w) m)) => Capability.Sink.Internal.Class.HasSink tag w (Capability.Reflection.Reflected s (Capability.Writer.HasWriter tag w) m)
- Capability.Accessors: (:.:) :: m a -> (:.:)
+ Capability.Accessors: (:.:) :: m a -> (:.:) (t2 :: (Type -> Type) -> Type -> Type) (t1 :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) (a :: Type)
- Capability.Accessors: Coerce :: m a -> Coerce m
+ Capability.Accessors: Coerce :: m a -> Coerce (to :: Type) m (a :: Type)
- Capability.Accessors: Ctor :: m a -> Ctor m
+ Capability.Accessors: Ctor :: m a -> Ctor (ctor :: Symbol) (oldtag :: k) m (a :: Type)
- Capability.Accessors: Field :: m a -> Field m
+ Capability.Accessors: Field :: m a -> Field (field :: Symbol) (oldtag :: k) m (a :: Type)
- Capability.Accessors: Lift :: m a -> Lift m
+ Capability.Accessors: Lift :: m a -> Lift m (a :: Type)
- Capability.Accessors: Pos :: m a -> Pos m
+ Capability.Accessors: Pos :: m a -> Pos (pos :: Nat) (oldtag :: k) m (a :: Type)
- Capability.Accessors: Rename :: m a -> Rename m
+ Capability.Accessors: Rename :: m a -> Rename (oldtag :: k) m (a :: Type)
- Capability.Accessors: newtype (:.:) (t2 :: (* -> *) -> * -> *) (t1 :: (* -> *) -> * -> *) (m :: * -> *) (a :: *)
+ Capability.Accessors: newtype (:.:) (t2 :: (Type -> Type) -> Type -> Type) (t1 :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) (a :: Type)
- Capability.Accessors: newtype Coerce (to :: *) m (a :: *)
+ Capability.Accessors: newtype Coerce (to :: Type) m (a :: Type)
- Capability.Accessors: newtype Ctor (ctor :: Symbol) (oldtag :: k) m (a :: *)
+ Capability.Accessors: newtype Ctor (ctor :: Symbol) (oldtag :: k) m (a :: Type)
- Capability.Accessors: newtype Field (field :: Symbol) (oldtag :: k) m (a :: *)
+ Capability.Accessors: newtype Field (field :: Symbol) (oldtag :: k) m (a :: Type)
- Capability.Accessors: newtype Lift m (a :: *)
+ Capability.Accessors: newtype Lift m (a :: Type)
- Capability.Accessors: newtype Pos (pos :: Nat) (oldtag :: k) m (a :: *)
+ Capability.Accessors: newtype Pos (pos :: Nat) (oldtag :: k) m (a :: Type)
- Capability.Accessors: newtype Rename (oldtag :: k) m (a :: *)
+ Capability.Accessors: newtype Rename (oldtag :: k) m (a :: Type)
- Capability.Constraints: type Capability = (* -> *) -> Constraint
+ Capability.Constraints: type Capability = (Type -> Type) -> Constraint
- Capability.Error: MonadCatch :: m a -> MonadCatch m
+ Capability.Error: MonadCatch :: m a -> MonadCatch (e :: Type) m (a :: Type)
- Capability.Error: MonadError :: m a -> MonadError m
+ Capability.Error: MonadError :: m a -> MonadError m (a :: Type)
- Capability.Error: MonadThrow :: m a -> MonadThrow m
+ Capability.Error: MonadThrow :: m a -> MonadThrow (e :: Type) m (a :: Type)
- Capability.Error: MonadUnliftIO :: m a -> MonadUnliftIO m
+ Capability.Error: MonadUnliftIO :: m a -> MonadUnliftIO (e :: Type) m (a :: Type)
- Capability.Error: SafeExceptions :: m a -> SafeExceptions m
+ Capability.Error: SafeExceptions :: m a -> SafeExceptions (e :: Type) m (a :: Type)
- Capability.Error: class HasThrow tag e m => HasCatch (tag :: k) (e :: *) (m :: * -> *) | tag m -> e
+ Capability.Error: class HasThrow tag e m => HasCatch (tag :: k) (e :: Type) (m :: Type -> Type) | tag m -> e
- Capability.Error: class Monad m => HasThrow (tag :: k) (e :: *) (m :: * -> *) | tag m -> e
+ Capability.Error: class Monad m => HasThrow (tag :: k) (e :: Type) (m :: Type -> Type) | tag m -> e
- Capability.Error: newtype MonadCatch (e :: *) m (a :: *)
+ Capability.Error: newtype MonadCatch (e :: Type) m (a :: Type)
- Capability.Error: newtype MonadError m (a :: *)
+ Capability.Error: newtype MonadError m (a :: Type)
- Capability.Error: newtype MonadThrow (e :: *) m (a :: *)
+ Capability.Error: newtype MonadThrow (e :: Type) m (a :: Type)
- Capability.Error: newtype MonadUnliftIO (e :: *) m (a :: *)
+ Capability.Error: newtype MonadUnliftIO (e :: Type) m (a :: Type)
- Capability.Error: newtype SafeExceptions (e :: *) m (a :: *)
+ Capability.Error: newtype SafeExceptions (e :: Type) m (a :: Type)
- Capability.Error: type family TypeOf k (s :: k) :: *
+ Capability.Error: type family TypeOf k (s :: k) :: Type
- Capability.Reader: MonadReader :: m a -> MonadReader
+ Capability.Reader: MonadReader :: m a -> MonadReader (m :: Type -> Type) (a :: Type)
- Capability.Reader: ReadState :: m a -> ReadState
+ Capability.Reader: ReadState :: m a -> ReadState (m :: Type -> Type) (a :: Type)
- Capability.Reader: ReadStatePure :: m a -> ReadStatePure
+ Capability.Reader: ReadStatePure :: m a -> ReadStatePure (m :: Type -> Type) (a :: Type)
- Capability.Reader: class (Monad m, HasSource tag r m) => HasReader (tag :: k) (r :: *) (m :: * -> *) | tag m -> r
+ Capability.Reader: class (Monad m, HasSource tag r m) => HasReader (tag :: k) (r :: Type) (m :: Type -> Type) | tag m -> r
- Capability.Reader: newtype MonadReader (m :: * -> *) (a :: *)
+ Capability.Reader: newtype MonadReader (m :: Type -> Type) (a :: Type)
- Capability.Reader: newtype ReadState (m :: * -> *) (a :: *)
+ Capability.Reader: newtype ReadState (m :: Type -> Type) (a :: Type)
- Capability.Reader: newtype ReadStatePure (m :: * -> *) (a :: *)
+ Capability.Reader: newtype ReadStatePure (m :: Type -> Type) (a :: Type)
- Capability.Reader: type family TypeOf k (s :: k) :: *
+ Capability.Reader: type family TypeOf k (s :: k) :: Type
- Capability.Sink: SinkDList :: m a -> SinkDList m
+ Capability.Sink: SinkDList :: m a -> SinkDList m (a :: Type)
- Capability.Sink: SinkLog :: m a -> SinkLog m
+ Capability.Sink: SinkLog :: m a -> SinkLog m (a :: Type)
- Capability.Sink: SinkStack :: m a -> SinkStack m
+ Capability.Sink: SinkStack :: m a -> SinkStack m (a :: Type)
- Capability.Sink: class Monad m => HasSink (tag :: k) (a :: *) (m :: * -> *) | tag m -> a
+ Capability.Sink: class Monad m => HasSink (tag :: k) (a :: Type) (m :: Type -> Type) | tag m -> a
- Capability.Sink: newtype SinkDList m (a :: *)
+ Capability.Sink: newtype SinkDList m (a :: Type)
- Capability.Sink: newtype SinkLog m (a :: *)
+ Capability.Sink: newtype SinkLog m (a :: Type)
- Capability.Sink: newtype SinkStack m (a :: *)
+ Capability.Sink: newtype SinkStack m (a :: Type)
- Capability.Sink: type family TypeOf k (s :: k) :: *
+ Capability.Sink: type family TypeOf k (s :: k) :: Type
- Capability.Source: MonadReader :: m a -> MonadReader
+ Capability.Source: MonadReader :: m a -> MonadReader (m :: Type -> Type) (a :: Type)
- Capability.Source: MonadState :: m a -> MonadState
+ Capability.Source: MonadState :: m a -> MonadState (m :: Type -> Type) (a :: Type)
- Capability.Source: ReadState :: m a -> ReadState
+ Capability.Source: ReadState :: m a -> ReadState (m :: Type -> Type) (a :: Type)
- Capability.Source: ReadStatePure :: m a -> ReadStatePure
+ Capability.Source: ReadStatePure :: m a -> ReadStatePure (m :: Type -> Type) (a :: Type)
- Capability.Source: ReaderRef :: m a -> ReaderRef m
+ Capability.Source: ReaderRef :: m a -> ReaderRef m (a :: Type)
- Capability.Source: class Monad m => HasSource (tag :: k) (a :: *) (m :: * -> *) | tag m -> a
+ Capability.Source: class Monad m => HasSource (tag :: k) (a :: Type) (m :: Type -> Type) | tag m -> a
- Capability.Source: newtype MonadReader (m :: * -> *) (a :: *)
+ Capability.Source: newtype MonadReader (m :: Type -> Type) (a :: Type)
- Capability.Source: newtype MonadState (m :: * -> *) (a :: *)
+ Capability.Source: newtype MonadState (m :: Type -> Type) (a :: Type)
- Capability.Source: newtype ReadState (m :: * -> *) (a :: *)
+ Capability.Source: newtype ReadState (m :: Type -> Type) (a :: Type)
- Capability.Source: newtype ReadStatePure (m :: * -> *) (a :: *)
+ Capability.Source: newtype ReadStatePure (m :: Type -> Type) (a :: Type)
- Capability.Source: newtype ReaderRef m (a :: *)
+ Capability.Source: newtype ReaderRef m (a :: Type)
- Capability.Source: type family TypeOf k (s :: k) :: *
+ Capability.Source: type family TypeOf k (s :: k) :: Type
- Capability.State: MonadState :: m a -> MonadState
+ Capability.State: MonadState :: m a -> MonadState (m :: Type -> Type) (a :: Type)
- Capability.State: ReaderRef :: m a -> ReaderRef m
+ Capability.State: ReaderRef :: m a -> ReaderRef m (a :: Type)
- Capability.State: class (Monad m, HasSource tag s m, HasSink tag s m) => HasState (tag :: k) (s :: *) (m :: * -> *) | tag m -> s
+ Capability.State: class (Monad m, HasSource tag s m, HasSink tag s m) => HasState (tag :: k) (s :: Type) (m :: Type -> Type) | tag m -> s
- Capability.State: newtype MonadState (m :: * -> *) (a :: *)
+ Capability.State: newtype MonadState (m :: Type -> Type) (a :: Type)
- Capability.State: newtype ReaderRef m (a :: *)
+ Capability.State: newtype ReaderRef m (a :: Type)
- Capability.State: type family TypeOf k (s :: k) :: *
+ Capability.State: type family TypeOf k (s :: k) :: Type
- Capability.TypeOf: type family TypeOf k (s :: k) :: *
+ Capability.TypeOf: type family TypeOf k (s :: k) :: Type
- Capability.Writer: SinkLog :: m a -> SinkLog m
+ Capability.Writer: SinkLog :: m a -> SinkLog m (a :: Type)
- Capability.Writer: class (Monoid w, Monad m, HasSink tag w m) => HasWriter (tag :: k) (w :: *) (m :: * -> *) | tag m -> w
+ Capability.Writer: class (Monoid w, Monad m, HasSink tag w m) => HasWriter (tag :: k) (w :: Type) (m :: Type -> Type) | tag m -> w
- Capability.Writer: newtype SinkLog m (a :: *)
+ Capability.Writer: newtype SinkLog m (a :: Type)
- Capability.Writer: type family TypeOf k (s :: k) :: *
+ Capability.Writer: type family TypeOf k (s :: k) :: Type
Files
- ChangeLog.md +11/−0
- README.md +1/−4
- capability.cabal +20/−16
- examples/CountLog.hs +4/−3
- examples/Error.hs +1/−0
- examples/Reader.hs +4/−3
- examples/Reflection.hs +255/−0
- examples/Test.hs +2/−0
- src/Capability.hs +6/−2
- src/Capability/Accessors.hs +11/−10
- src/Capability/Constraints.hs +3/−3
- src/Capability/Error.hs +60/−12
- src/Capability/Reader/Internal/Class.hs +42/−5
- src/Capability/Reader/Internal/Strategies.hs +2/−1
- src/Capability/Reflection.hs +172/−0
- src/Capability/Sink/Internal/Class.hs +19/−2
- src/Capability/Sink/Internal/Strategies.hs +5/−7
- src/Capability/Source/Internal/Class.hs +19/−2
- src/Capability/Source/Internal/Strategies.hs +5/−5
- src/Capability/State/Internal/Class.hs +45/−3
- src/Capability/State/Internal/Strategies.hs +2/−2
- src/Capability/State/Internal/Strategies/Common.hs +3/−2
- src/Capability/TypeOf.hs +6/−4
- src/Capability/Writer.hs +47/−6
ChangeLog.md view
@@ -1,5 +1,16 @@ # Revision history for capability +## 0.4.0.0 -- 2021-03-15++* Fix infinite loop in `writer`.+ See [#85](https://github.com/tweag/capability/pull/85)++* Introduce `Capability.Reflection` to define ad-hoc interpreters.+ See [#86](https://github.com/tweag/capability/pull/86)++* Fix compatibility with GHC 8.10.+ See [#87](https://github.com/tweag/capability/pull/87)+ ## 0.3.0.0 -- 2020-03-19 * Rename HasStream to HasSink, for symmetry.
README.md view
@@ -132,10 +132,7 @@ ### Nix Shell -Some of this package's dependencies require patches to build with GHC 8.6.-These patches are defined in-[`nix/haskell/default.nix`](nix/haskell/default.nix).-A development environment with all patched dependencies in scope is defined in+A development environment with all dependencies in scope is defined in [`shell.nix`](shell.nix). ### Build
capability.cabal view
@@ -1,5 +1,5 @@ name: capability-version: 0.3.0.0+version: 0.4.0.0 homepage: https://github.com/tweag/capability license: BSD3 license-file: LICENSE.md@@ -12,7 +12,7 @@ CONTRIBUTING.md README.md cabal-version: 1.18-tested-with: GHC==8.6.1+tested-with: GHC==8.10.4 synopsis: Extensional capabilities and deriving combinators description: Standard capability type classes for extensional effects and combinators@@ -44,6 +44,7 @@ Capability.Reader Capability.Reader.Internal.Class Capability.Reader.Internal.Strategies+ Capability.Reflection Capability.Sink Capability.Sink.Internal.Class Capability.Sink.Internal.Strategies@@ -59,22 +60,23 @@ Capability.Writer Capability.Writer.Discouraged build-depends:- base >= 4.12 && < 5.0- , constraints >= 0.1 && < 0.12+ base >= 4.14 && < 5.0+ , constraints >= 0.1 && < 0.13 , dlist >= 0.8 && < 0.9 , exceptions >= 0.6 && < 0.11- , generic-lens >= 1.0 && < 1.3+ , generic-lens >= 2.0 && < 2.1 , lens >= 4.16 && < 5.0 , monad-control >= 1.0 && < 1.1 , monad-unlift >= 0.2 && < 0.3 , mtl >= 2.0 && < 3.0 , mutable-containers >= 0.3 && < 0.4 , primitive >= 0.6 && < 0.8+ , reflection >= 2.1 && < 2.2 , safe-exceptions >= 0.1 && < 0.2 , streaming >= 0.2 && < 0.3 , transformers >= 0.5.5 && < 0.6 , unliftio >= 0.2 && < 0.3- , unliftio-core >= 0.1 && < 0.2+ , unliftio-core >= 0.1 && < 0.3 if flag(dev) ghc-options: -Wall -Werror -Wcompat -Wincomplete-record-updates@@ -88,6 +90,7 @@ test-suite examples type: exitcode-stdio-1.0 other-modules:+ Reflection WordCount CountLog Error@@ -98,17 +101,18 @@ Writer main-is: Test.hs build-depends:- base >= 4.12 && < 5.0+ base >= 4.14 && < 5.0 , capability- , containers >= 0.6 && < 0.7- , hspec >= 2.0 && < 3.0- , lens >= 4.16 && < 5.0- , mtl >= 2.0 && < 3.0- , silently >= 1.2 && < 1.3- , streaming >= 0.2 && < 0.3- , temporary >= 1.0 && < 1.4- , text >= 0.2 && < 1.3- , unliftio >= 0.2 && < 0.3+ , containers+ , dlist+ , hspec+ , lens+ , mtl+ , silently+ , streaming+ , temporary+ , text+ , unliftio if flag(hspec-jenkins) build-depends: hspec-jenkins if flag(dev)
examples/CountLog.hs view
@@ -22,6 +22,7 @@ import qualified Data.Char import Data.Coerce (coerce) import Data.IORef+import Data.Kind (Type) import GHC.Generics (Generic) import Test.Common import Test.Hspec@@ -63,7 +64,7 @@ -- | Deriving @HasReader@ from @MonadReader@.-newtype LogM m (a :: *) = LogM (ReaderT LogCtx m a)+newtype LogM m (a :: Type) = LogM (ReaderT LogCtx m a) deriving (Functor, Applicative, Monad) deriving Logger via (TheLoggerReader (Field "logger" ()@@ -110,7 +111,7 @@ -- ReaderT IORef instance -------------------------------------------- -- | Deriving @HasState@ from @HasReader@ of an @IORef@.-newtype Counter'M m (a :: *) = Counter'M (ReaderT (IORef Int) m a)+newtype Counter'M m (a :: Type) = Counter'M (ReaderT (IORef Int) m a) deriving (Functor, Applicative, Monad) deriving Counter via TheCounterState (ReaderIORef (MonadReader (ReaderT (IORef Int) m)))@@ -139,7 +140,7 @@ -- | Deriving two capabilities from the record fields of @MonadReader@.-newtype CountLogM m (a :: *) = CountLogM (ReaderT CountLogCtx m a)+newtype CountLogM m (a :: Type) = CountLogM (ReaderT CountLogCtx m a) deriving (Functor, Applicative, Monad) deriving Counter via TheCounterState (ReaderIORef (Rename "countCtx" (Field "countCtx" ()
examples/Error.hs view
@@ -8,6 +8,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeInType #-}
examples/Reader.hs view
@@ -14,6 +14,7 @@ import Capability.Source import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader (ReaderT (..))+import Data.Kind (Type) import GHC.Generics (Generic) import Test.Common import Test.Hspec@@ -21,8 +22,8 @@ data Foo data Bar -type instance TypeOf * Foo = Int-type instance TypeOf * Bar = Int+type instance TypeOf Type Foo = Int+type instance TypeOf Type Bar = Int ---------------------------------------------------------------------- -- Example Programs@@ -65,7 +66,7 @@ -- Instances -- | @HasReader@ instance derived via @MonadReader@.-newtype FooReaderT m (a :: *) = FooReaderT (ReaderT Int m a)+newtype FooReaderT m (a :: Type) = FooReaderT (ReaderT Int m a) deriving (Functor, Applicative, Monad, MonadIO) deriving (HasSource Foo Int) via MonadReader (ReaderT Int m) deriving (HasReader Foo Int) via MonadReader (ReaderT Int m)
+ examples/Reflection.hs view
@@ -0,0 +1,255 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}++module Reflection where++import Capability.Error+import Capability.Reader+import Capability.Reflection+import Capability.Sink+import Capability.Source+import Capability.State+import Capability.Writer+import Control.Monad (forM_, replicateM)+import qualified Control.Monad.Except as MTL+import qualified Control.Monad.State as MTL+import Data.DList (DList)+import qualified Data.DList as DList+import Data.Monoid (Product (..))+import Test.Hspec++----------------------------------------------------------------------+-- Instances++newtype Logger s a = Logger (MTL.State (DList String, s) a)+ deriving (Functor, Applicative, Monad)+ deriving+ (HasSink "log" String)+ via SinkDList (SinkLog (Rename 1 (Pos 1 () (MonadState (MTL.State (DList String, s))))))+ deriving+ (HasState "test" s, HasSource "test" s, HasSink "test" s)+ via Rename 2 (Pos 2 () (MonadState (MTL.State (DList String, s))))++runLogger :: s -> Logger s a -> (a, s, [String])+runLogger s0 (Logger m) =+ case MTL.runState m (mempty, s0) of+ (a, (log', s)) -> (a, s, DList.toList log')++----------------------------------------------------------------------+-- Test Cases++spec :: Spec+spec = do+ describe "interpret_" $ do+ it "evaluates HasSource" $ do+ runLogger+ [1, 2, 3 :: Int]+ ( interpret_ @"source"+ ReifiedSource+ { _await = do+ yield @"log" "await"+ state @"test" $ \case+ (x : xs) -> (x, xs)+ [] -> error "empty stream"+ }+ (replicateM 3 $ await @"source")+ )+ `shouldBe` ([1, 2, 3], [], ["await", "await", "await"])+ it "evaluates HasSink" $ do+ runLogger+ []+ ( interpret_ @"sink"+ ReifiedSink+ { _yield = \x -> do+ yield @"log" "yield"+ modify @"test" (x :)+ }+ (forM_ [1, 2, 3 :: Int] $ yield @"sink")+ )+ `shouldBe` ((), [3, 2, 1], ["yield", "yield", "yield"])+ it "evaluates HasReader" $ do+ runLogger+ (42 :: Int)+ ( interpret_ @"reader"+ ReifiedReader+ { _readerSource =+ ReifiedSource+ { _await = do+ yield @"log" "ask"+ get @"test"+ },+ _local = \f m -> do+ yield @"log" "local"+ r <- state @"test" $ \r -> (r, f r)+ a <- m+ put @"test" r+ pure a,+ _reader = \f -> do+ yield @"log" "reader"+ f <$> get @"test"+ }+ ( sequence+ [ ask @"reader",+ local @"reader" succ $ do+ asks @"reader" succ,+ reader @"reader" pred+ ]+ )+ )+ `shouldBe` ([42, 44, 41], 42, ["ask", "local", "ask", "reader"])+ it "evaluates HasState" $ do+ runLogger+ (42 :: Int)+ ( interpret_ @"state"+ ReifiedState+ { _stateSource =+ ReifiedSource+ { _await = do+ yield @"log" "get"+ get @"test"+ },+ _stateSink =+ ReifiedSink+ { _yield = \x -> do+ yield @"log" "put"+ put @"test" x+ },+ _state = \f -> do+ yield @"log" "state"+ state @"test" f+ }+ ( do+ old <- get @"state"+ put @"state" 0+ state @"state" (\new -> (old, succ new))+ )+ )+ `shouldBe` (42, 1, ["get", "put", "state"])+ it "evaluates HasWriter" $ do+ runLogger+ (mempty :: Product Int)+ ( interpret_ @"writer"+ ReifiedWriter+ { _writerSink =+ ReifiedSink+ { _yield = \w -> do+ yield @"log" "tell"+ modify @"test" (<> w)+ },+ _writer = \(a, w) -> do+ yield @"log" "writer"+ modify @"test" (<> w)+ pure a,+ _listen = \m -> do+ yield @"log" "listen"+ w0 <- state @"test" $ \s -> (s, mempty)+ a <- m+ w <- state @"test" $ \s -> (s, w0 <> s)+ pure (a, w),+ _pass = \m -> do+ yield @"log" "pass"+ w0 <- state @"test" $ \s -> (s, mempty)+ (a, f) <- m+ modify' @"test" $ \s -> w0 <> f s+ pure a+ }+ ( do+ tell @"writer" 2+ writer @"writer" ((), 3)+ ((), w) <- listen @"writer" $ do+ tell @"writer" 5+ pass @"writer" $ do+ tell @"writer" 6+ pure ((), (+ 1))+ pure w+ )+ )+ `shouldBe` (5, 2 * 3 * 5 * 7, ["tell", "writer", "listen", "tell", "pass", "tell"])+ it "evaluates HasThrow" $ do+ runLogger+ ()+ ( MTL.runExceptT $+ interpret_ @"throw"+ ReifiedThrow+ { _throw = \e -> do+ MTL.lift $ yield @"log" "throw"+ MTL.throwError e+ }+ ( do+ _ <- throw @"throw" "some error"+ pure $! tail ""+ )+ )+ `shouldBe` (Left "some error", (), ["throw"])+ it "evaluates HasCatch" $ do+ runLogger+ ()+ ( MTL.runExceptT $+ interpret_ @"catch"+ ReifiedCatch+ { _catchThrow =+ ReifiedThrow+ { _throw = \e -> do+ MTL.lift $ yield @"log" "throw"+ MTL.throwError e+ },+ _catch = \m h -> do+ MTL.lift $ yield @"log" "catch"+ MTL.catchError m h,+ _catchJust = \f m h -> do+ MTL.lift $ yield @"log" "catchJust"+ MTL.catchError m $ \e ->+ case f e of+ Just b -> h b+ Nothing -> MTL.throwError e+ }+ ( do+ e1 <-+ catch @"catch"+ ( do+ _ <- throw @"catch" "some error"+ pure $! tail ""+ )+ pure+ catchJust @"catch"+ (const Nothing)+ ( do+ _ <- throw @"catch" ("again " ++ e1)+ pure $! tail ""+ )+ pure+ )+ )+ `shouldBe` (Left "again some error", (), ["catch", "throw", "catchJust", "throw"])+ describe "interpret" $ do+ it "can be nested" $ do+ runLogger+ ()+ ( interpret @"source" @'[HasSink "log" String]+ ReifiedSource+ { _await = do+ yield @"log" "await"+ pure ()+ }+ ( interpret @"sink" @'[HasSink "log" String, HasSource "source" ()]+ ReifiedSink+ { _yield = \() -> do+ yield @"log" "yield"+ pure ()+ }+ ( do+ yield @"log" "begin"+ await @"source"+ yield @"sink" ()+ yield @"log" "end"+ )+ )+ )+ `shouldBe` ((), (), ["begin", "await", "yield", "end"])
examples/Test.hs view
@@ -14,6 +14,7 @@ import qualified CountLog import qualified Error import qualified Reader+import qualified Reflection import qualified State import qualified Sink import qualified Writer@@ -25,6 +26,7 @@ describe "CountLog" CountLog.spec describe "Error" Error.spec describe "Reader" Reader.spec+ describe "Reflection" Reflection.spec describe "State" State.spec describe "Sink" Sink.spec describe "Writer" Writer.spec
src/Capability.hs view
@@ -108,14 +108,18 @@ -- combinators which you can use if you absolutely must: they are correct, but -- inefficient, so we recommend that you do not. ----- Finally there is+-- Finally there are -- -- * "Capability.Derive"--- -- Which exports a (still experimental) 'Capability.Derive.derive' function, -- which lets you run a computation which requires capabilities which are not -- directly provided by the ambient monad, but can be derived from the -- capabilities provided by the ambient monad.+-- * "Capability.Reflection"+-- Which exports 'Capability.Reflection.interpret_' and+-- 'Capability.Reflection.interpret', which let you define an ad-hoc+-- interpretation of a capability based on the capabilities provided by the+-- ambient monad. -- -- == Further considerations --
src/Capability/Accessors.hs view
@@ -20,6 +20,7 @@ import Control.Monad.IO.Class (MonadIO) import Control.Monad.Primitive (PrimMonad)+import Data.Kind (Type) import GHC.TypeLits (Nat, Symbol) -- | Coerce the type in the context @m@ to @to@.@@ -37,7 +38,7 @@ -- @'Capability.Reader.MonadReader' (Reader Int)@ to a -- @'Capability.Reader.HasReader' \"a\" MyInt@ -- instance using @Coercible Int MyInt@.-newtype Coerce (to :: *) m (a :: *) = Coerce (m a)+newtype Coerce (to :: Type) m (a :: Type) = Coerce (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) -- | Rename the tag.@@ -58,7 +59,7 @@ -- and @Rename@ is redundant in this example. -- -- See 'Pos' below for a common use-case.-newtype Rename (oldtag :: k) m (a :: *) = Rename (m a)+newtype Rename (oldtag :: k) m (a :: Type) = Rename (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) -- | Access the record field @field@ in the context @m@.@@ -78,7 +79,7 @@ -- instance by focusing on the field @foo@ in the @Foo@ record. -- -- See 'Rename' for a way to change the tag.-newtype Field (field :: Symbol) (oldtag :: k) m (a :: *) = Field (m a)+newtype Field (field :: Symbol) (oldtag :: k) m (a :: Type) = Field (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) -- | Access the value at position @pos@ in the context @m@.@@ -104,7 +105,7 @@ -- deriving (HasReader "foo" Int) via -- Rename 1 (Pos 1 () (MonadReader (Reader (Int, Bool)))) -- @-newtype Pos (pos :: Nat) (oldtag :: k) m (a :: *) = Pos (m a)+newtype Pos (pos :: Nat) (oldtag :: k) m (a :: Type) = Pos (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) -- | Choose the given constructor in the sum-type in context @m@.@@ -122,7 +123,7 @@ -- @'Capability.Error.MonadError' (ExceptT MyError Identity)@ to a -- @'Capability.Error.HasThrow' \"ErrB\" String@ -- instance by wrapping thrown @String@s in the @ErrB@ constructor.-newtype Ctor (ctor :: Symbol) (oldtag :: k) m (a :: *) = Ctor (m a)+newtype Ctor (ctor :: Symbol) (oldtag :: k) m (a :: Type) = Ctor (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) -- | Skip one level in a monad transformer stack.@@ -143,7 +144,7 @@ -- the @'Capability.State.HasState' "\foo\" Bool@ instance of the underlying -- @'Capability.State.MonadState' (State Bool)@ over the -- @StateT Int@ monad transformer.-newtype Lift m (a :: *) = Lift (m a)+newtype Lift m (a :: Type) = Lift (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) @@ -152,10 +153,10 @@ -- This is not necessary in deriving via clauses, but in places where a -- transformer is expected as a type argument. E.g. 'HasError.wrapError'. newtype (:.:)- (t2 :: (* -> *) -> * -> *)- (t1 :: (* -> *) -> * -> *)- (m :: * -> *)- (a :: *)+ (t2 :: (Type -> Type) -> Type -> Type)+ (t1 :: (Type -> Type) -> Type -> Type)+ (m :: Type -> Type)+ (a :: Type) = (:.:) (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) infixr 9 :.:
src/Capability/Constraints.hs view
@@ -16,12 +16,12 @@ ) where import Data.Constraint (Dict(..))-import Data.Kind (Constraint)+import Data.Kind (Constraint, Type) --- | A 'Capability' takes a type constructor @* -> *@ (e.g., a monad) and+-- | A 'Capability' takes a type constructor @Type -> Type@ (e.g., a monad) and -- returns a 'Constraint'. Examples of capabilities includ: @HasReader "foo" -- Int@, @MonadIO@, …-type Capability = (* -> *) -> Constraint+type Capability = (Type -> Type) -> Constraint -- | Type family used used to express a conjunction of constraints over a single -- type.
src/Capability/Error.hs view
@@ -39,6 +39,7 @@ {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeInType #-}+{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UndecidableInstances #-}@@ -65,6 +66,8 @@ , MonadUnliftIO(..) -- ** Modifiers , module Capability.Accessors+ -- * Reflection+ , Reified(..) -- * Re-exported , Exception(..) , Typeable@@ -73,6 +76,7 @@ import Capability.Accessors import Capability.Constraints import Capability.Derive (derive)+import Capability.Reflection import Capability.TypeOf import Control.Exception (Exception(..)) import qualified Control.Exception.Safe as Safe@@ -87,6 +91,7 @@ import Control.Monad.Trans.Control (MonadTransControl(..)) import Data.Coerce (Coercible, coerce) import qualified Data.Generics.Sum.Constructors as Generic+import Data.Kind (Type) import Data.Typeable (Typeable) import GHC.Exts (Proxy#, proxy#) import qualified UnliftIO.Exception as UnliftIO@@ -96,7 +101,7 @@ -- @HasThrow@/@HasCatch@ capabilities at different tags should be independent. -- See 'HasCatch'. class Monad m- => HasThrow (tag :: k) (e :: *) (m :: * -> *) | tag m -> e+ => HasThrow (tag :: k) (e :: Type) (m :: Type -> Type) | tag m -> e where -- | For technical reasons, this method needs an extra proxy argument. -- You only need it if you are defining new instances of 'HasReader'.@@ -106,7 +111,7 @@ -- | Throw an exception in the specified exception capability. throw :: forall tag e m a. HasThrow tag e m => e -> m a-throw = throw_ (proxy# @_ @tag)+throw = throw_ (proxy# @tag) {-# INLINE throw #-} -- | Capability to catch exceptions of type @e@ under @tag@.@@ -124,7 +129,7 @@ -- -- See 'wrapError' for a way to combine multiple exception types into one. class HasThrow tag e m- => HasCatch (tag :: k) (e :: *) (m :: * -> *) | tag m -> e+ => HasCatch (tag :: k) (e :: Type) (m :: Type -> Type) | tag m -> e where -- | For technical reasons, this method needs an extra proxy argument. -- You only need it if you are defining new instances of 'HasReader'.@@ -140,14 +145,14 @@ -- | Provide a handler for exceptions thrown in the given action -- in the given exception capability. catch :: forall tag e m a. HasCatch tag e m => m a -> (e -> m a) -> m a-catch = catch_ (proxy# @_ @tag)+catch = catch_ (proxy# @tag) {-# INLINE catch #-} -- | Like 'catch', but only handle the exception if the provided function -- returns 'Just'. catchJust :: forall tag e m a b. HasCatch tag e m => (e -> Maybe b) -> m a -> (b -> m a) -> m a-catchJust = catchJust_ (proxy# @_ @tag)+catchJust = catchJust_ (proxy# @tag) {-# INLINE catchJust #-} -- | Wrap exceptions @inner@ originating from the given action according to@@ -177,7 +182,7 @@ -- What would the meaning of the tag be? -- | Derive 'HasError from @m@'s 'Control.Monad.Except.MonadError' instance.-newtype MonadError m (a :: *) = MonadError (m a)+newtype MonadError m (a :: Type) = MonadError (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) instance Except.MonadError e m => HasThrow tag e (MonadError m) where@@ -200,7 +205,7 @@ {-# INLINE catchJust_ #-} -- | Derive 'HasThrow' from @m@'s 'Control.Monad.Catch.MonadThrow' instance.-newtype MonadThrow (e :: *) m (a :: *) = MonadThrow (m a)+newtype MonadThrow (e :: Type) m (a :: Type) = MonadThrow (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) instance (Catch.Exception e, Catch.MonadThrow m)@@ -211,7 +216,7 @@ {-# INLINE throw_ #-} -- | Derive 'HasCatch from @m@'s 'Control.Monad.Catch.MonadCatch instance.-newtype MonadCatch (e :: *) m (a :: *) = MonadCatch (m a)+newtype MonadCatch (e :: Type) m (a :: Type) = MonadCatch (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) deriving (HasThrow tag e) via MonadThrow e m @@ -238,7 +243,7 @@ -- | Derive 'HasError' using the functionality from the @safe-exceptions@ -- package.-newtype SafeExceptions (e :: *) m (a :: *) = SafeExceptions (m a)+newtype SafeExceptions (e :: Type) m (a :: Type) = SafeExceptions (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) instance (Safe.Exception e, Safe.MonadThrow m)@@ -269,7 +274,7 @@ {-# INLINE catchJust_ #-} -- | Derive 'HasError' using the functionality from the @unliftio@ package.-newtype MonadUnliftIO (e :: *) m (a :: *) = MonadUnliftIO (m a)+newtype MonadUnliftIO (e :: Type) m (a :: Type) = MonadUnliftIO (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) instance (UnliftIO.Exception e, MonadIO m)@@ -416,7 +421,7 @@ -- | Compose two accessors.-deriving via ((t2 :: (* -> *) -> * -> *) ((t1 :: (* -> *) -> * -> *) m))+deriving via ((t2 :: (Type -> Type) -> Type -> Type) ((t1 :: (Type -> Type) -> Type -> Type) m)) instance ( forall x. Coercible (m x) (t2 (t1 m) x) , Monad m, HasThrow tag e (t2 (t1 m)) )@@ -424,7 +429,7 @@ -- | Compose two accessors.-deriving via ((t2 :: (* -> *) -> * -> *) ((t1 :: (* -> *) -> * -> *) m))+deriving via ((t2 :: (Type -> Type) -> Type -> Type) ((t1 :: (Type -> Type) -> Type -> Type) m)) instance ( forall x. Coercible (m x) (t2 (t1 m) x) , Monad m, HasCatch tag e (t2 (t1 m)) )@@ -437,3 +442,46 @@ -- | Type synonym using the 'TypeOf' type family to specify 'HasCatch' -- constraints without having to specify the type associated to a tag. type HasCatch' (tag :: k) = HasCatch tag (TypeOf k tag)++--------------------------------------------------------------------------------++data instance Reified tag (HasThrow tag e) m = ReifiedThrow {_throw :: forall a. e -> m a}++data instance Reified tag (HasCatch tag e) m = ReifiedCatch+ { _catchThrow :: Reified tag (HasThrow tag e) m,+ _catch :: forall a. m a -> (e -> m a) -> m a,+ _catchJust :: forall a b. (e -> Maybe b) -> m a -> (b -> m a) -> m a+ }++instance+ ( Monad m,+ Reifies s (Reified tag (HasThrow tag e) m)+ ) =>+ HasThrow tag e (Reflected s (HasThrow tag e) m)+ where+ throw_ :: forall a. Proxy# tag -> e -> Reflected s (HasThrow tag e) m a+ throw_ _ = coerce @(e -> m a) $ _throw $ reified @s+ {-# INLINE throw_ #-}++instance+ ( Monad m,+ Reifies s (Reified tag (HasCatch tag e) m)+ ) =>+ HasThrow tag e (Reflected s (HasCatch tag e) m)+ where+ throw_ :: forall a. Proxy# tag -> e -> Reflected s (HasCatch tag e) m a+ throw_ _ = coerce @(e -> m a) $ _throw $ _catchThrow $ reified @s+ {-# INLINE throw_ #-}++instance+ ( Monad m,+ Reifies s (Reified tag (HasCatch tag e) m)+ ) =>+ HasCatch tag e (Reflected s (HasCatch tag e) m)+ where+ catch_ :: forall a. Proxy# tag -> Reflected s (HasCatch tag e) m a -> (e -> Reflected s (HasCatch tag e) m a) -> Reflected s (HasCatch tag e) m a+ catch_ _ = coerce @(m a -> (e -> m a) -> m a) $ _catch $ reified @s+ {-# INLINE catch_ #-}+ catchJust_ :: forall a b. Proxy# tag -> (e -> Maybe b) -> Reflected s (HasCatch tag e) m a -> (b -> Reflected s (HasCatch tag e) m a) -> Reflected s (HasCatch tag e) m a+ catchJust_ _ = coerce @((e -> Maybe b) -> m a -> (b -> m a) -> m a) $ _catchJust $ reified @s+ {-# INLINE catchJust_ #-}
src/Capability/Reader/Internal/Class.hs view
@@ -1,5 +1,8 @@ {-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE InstanceSigs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-}@@ -7,6 +10,7 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeInType #-} @@ -19,12 +23,15 @@ , local , reader , magnify+ , Reified(..) ) where import Capability.Constraints-import Capability.Source.Internal.Class import Capability.Derive (derive)-import Data.Coerce (Coercible)+import Capability.Reflection+import Capability.Source.Internal.Class+import Data.Coerce (Coercible, coerce)+import Data.Kind (Type) import GHC.Exts (Proxy#, proxy#) -- | Reader capability@@ -41,7 +48,7 @@ -- prop> local @t f (m >>= \x -> k x) = local @t f m >>= \x -> local @t f (k x) -- prop> reader @t f = f <$> ask @t class (Monad m, HasSource tag r m)- => HasReader (tag :: k) (r :: *) (m :: * -> *) | tag m -> r+ => HasReader (tag :: k) (r :: Type) (m :: Type -> Type) | tag m -> r where -- | For technical reasons, this method needs an extra proxy argument. -- You only need it if you are defining new instances of 'HasReader'.@@ -74,7 +81,7 @@ -- where @e@ is the environment of the reader capability @tag@. -- Symbolically: @return e = ask \@tag@. local :: forall tag r m a. HasReader tag r m => (r -> r) -> m a -> m a-local = local_ (proxy# @_ @tag)+local = local_ (proxy# @tag) {-# INLINE local #-} -- | @reader \@tag act@@@ -83,7 +90,7 @@ -- -- It happens to coincide with @asks@: @reader = asks@. reader :: forall tag r m a. HasReader tag r m => (r -> a) -> m a-reader = reader_ (proxy# @_ @tag)+reader = reader_ (proxy# @tag) {-# INLINE reader #-} -- | Execute the given reader action on a sub-component of the current context@@ -103,3 +110,33 @@ magnify = derive @t @'[HasReader innertag inner] @cs {-# INLINE magnify #-}++--------------------------------------------------------------------------------++data instance Reified tag (HasReader tag r) m = ReifiedReader+ { _readerSource :: Reified tag (HasSource tag r) m,+ _local :: forall a. (r -> r) -> m a -> m a,+ _reader :: forall a. (r -> a) -> m a+ }++instance+ ( Monad m,+ Reifies s (Reified tag (HasReader tag r) m)+ ) =>+ HasSource tag r (Reflected s (HasReader tag r) m)+ where+ await_ _ = coerce $ _await $ _readerSource $ reified @s+ {-# INLINE await_ #-}++instance+ ( Monad m,+ Reifies s (Reified tag (HasReader tag r) m)+ ) =>+ HasReader tag r (Reflected s (HasReader tag r) m)+ where+ local_ :: forall a. Proxy# tag -> (r -> r) -> Reflected s (HasReader tag r) m a -> Reflected s (HasReader tag r) m a+ local_ _ = coerce @((r -> r) -> m a -> m a) $ _local (reified @s)+ {-# INLINE local_ #-}+ reader_ :: forall a. Proxy# tag -> (r -> a) -> Reflected s (HasReader tag r) m a+ reader_ _ = coerce @((r -> a) -> m a) $ _reader (reified @s)+ {-# INLINE reader_ #-}
src/Capability/Reader/Internal/Strategies.hs view
@@ -40,6 +40,7 @@ import Data.Coerce (Coercible, coerce) import qualified Data.Generics.Product.Fields as Generic import qualified Data.Generics.Product.Positions as Generic+import Data.Kind (Type) import GHC.Exts (Proxy#) instance Reader.MonadReader r m => HasReader tag r (MonadReader m) where@@ -167,7 +168,7 @@ {-# INLINE reader_ #-} -- | Compose two accessors.-deriving via ((t2 :: (* -> *) -> * -> *) ((t1 :: (* -> *) -> * -> *) m))+deriving via ((t2 :: (Type -> Type) -> Type -> Type) ((t1 :: (Type -> Type) -> Type -> Type) m)) instance ( forall x. Coercible (m x) (t2 (t1 m) x) , Monad m, HasReader tag r (t2 (t1 m)) )
+ src/Capability/Reflection.hs view
@@ -0,0 +1,172 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++-- | Use this module to provide an ad-hoc interpreter for a capability using+-- type class reflection.+--+-- Use the functions 'interpret_' or 'interpret' for ad-hoc interpretation of+-- capabilities.+--+-- Refer to 'Reflected' if you would like to enable reflection for a new+-- capability.+--+-- More complex examples using this module can be found in the+-- [Reflection example+-- module](https://github.com/tweag/capability/blob/master/examples/Reflection.hs).+--+-- For details on reflection refer to the tutorial at+-- <https://www.tweag.io/posts/2017-12-21-reflection-tutorial.html> and the+-- @reflection@ library at <https://hackage.haskell.org/package/reflection>.++module Capability.Reflection+ ( -- * Reflection+ interpret_+ , interpret+ , Reified+ , Reflected (..)+ , reified+ -- * Re-exported+ , Reifies+ , reify+ , reflect+ , Proxy (..)+ ) where++import Capability.Constraints+import Capability.Derive+import Data.Kind (Type)+import Data.Proxy+import Data.Reflection++-- | @interpret_ \@tag dict action@+--+-- Execute @action@ using the ad-hoc interpretation of a capability @c@ under+-- @tag@ defined by @dict@, where @dict@ is a value of type @'Reified' tag c@,+-- i.e. a record providing the implementation of the methods of capability @c@.+--+-- For example, the following provides an ad-hoc interpretation for the+-- 'Capability.Source.HasSource' capability.+--+-- >>> :{+-- interpret_ @"my-source"+-- ReifiedSource { _await = pure "capabilities" }+-- (replicateM 3 (await @"my-source"))+-- :}+-- ["capabilities", "capabilities", "capabilities"]+interpret_ ::+ forall tag c m a.+ ( Monad m,+ forall s. Reifies s (Reified tag c m) => c (Reflected s c m)+ ) =>+ Reified tag c m ->+ (forall m'. c m' => m' a) ->+ m a+interpret_ = interpret @tag @'[] @c+{-# INLINE interpret_ #-}++-- | @interpret \@tag \@ambient dict action@+--+-- Like 'interpret_' but forwards the ambient capabilities @ambient@ into the+-- context of @action@ as well.+--+-- For example, the following provides an ad-hoc interpretation for the+-- 'Capability.Source.HasSource' capability, while using an ambient+-- 'Capability.Sink.HasSink' capability.+--+-- >>> :{+-- interpret @"my-source" @'[HasSink "my-sink" String]+-- ReifiedSource { _await = pure "capabilities" }+-- (replicateM_ 3 (await @"my-source" >>= yield @"my-sink"))+-- :}+interpret ::+ forall tag (cs :: [Capability]) c m a.+ ( Monad m,+ All cs m,+ forall s. Reifies s (Reified tag c m) => c (Reflected s c m)+ ) =>+ Reified tag c m ->+ (forall m'. All (c ': cs) m' => m' a) ->+ m a+interpret dict action =+ reify dict $ \(_ :: Proxy s) ->+ derive @(Reflected s c) @'[c] @cs action+{-# INLINE interpret #-}++-- | @Reified tag capability m@+--+-- Defines the dictionary type for the methods of @capability@ under @tag@ in+-- the monad @m@. Refer to 'interpret_' for an example use-case.+--+-- For example, the 'Capability.Sink.HasSink' capability has the method+-- @'Capability.Sink.yield' :: a -> m ()@. The corresponding dictionary type is+-- defined as follows.+--+-- >>> :{+-- data instance Reified tag (HasSink tag a) m =+-- ReifiedSink { _yield :: forall a. a -> m () }+-- :}+--+-- Superclass dictionaries are represented as nested records. For example, the+-- 'Capability.State.HasState' capability has the superclasses+-- 'Capability.Source.HasSource' and 'Capability.Sink.HasSink' and the method+-- @'Capability.State.state' :: (s -> (a, s)) -> m a@. The corresponding+-- dictionary type is defined as follows.+--+-- >>> :{+-- data instance Reified tag (HasState tag s) m =+-- ReifiedState+-- { _stateSource :: Reified tag (HasSource tag s) m,+-- _stateSink :: Reified tag (HasSink tag s) m,+-- _state :: forall a. (s -> (a, s)) -> m a+-- }+-- :}+data family Reified (tag :: k) (c :: Capability) (m :: Type -> Type)++-- | @Reflected s capability m@+--+-- Carries the type class instance for @capability@ in the monad @m@ defined by+-- the dictionary reflected in @s@ in the type system.+--+-- For most use-cases it is not necessary to use this type directly. Use+-- 'interpret_' or 'interpret' instead.+--+-- If you wish to enable reflection for a new capability, then you will need to+-- define a type class instance for @Reflected@ for the new capability. Note,+-- you will also need to define an instance of 'Reified' which defines the+-- dictionary type of the new capability. Hint, you can use @'reified' \@s@ to+-- obtain the dictionary from the context in the instance implementation.+--+-- For example, the @Reflected@ instance for the 'Capability.Sink.HasSink'+-- capability can be defined as follows. Assuming the dictionary described in+-- 'Reified'.+--+-- >>> :{+-- instance+-- (Monad m, Reifies s (Reified tag (HasSink tag a) m)) =>+-- HasSink tag a (Reflected s (HasSink tag a) m)+-- where+-- yield a = Reflect $ _yield (reified @s) a+-- :}+newtype Reflected (s :: Type) (c :: Capability) (m :: Type -> Type) (a :: Type) = Reflect (m a)+ deriving (Functor, Applicative, Monad)++-- | @reified \@s@+--+-- Obtain the dictionary that is reflected in the type system under @s@.+--+-- This is a convenience wrapper around 'Data.Reflection.reflect'.+reified :: forall s tag c m. Reifies s (Reified tag c m) => Reified tag c m+reified = reflect (Proxy @s)+{-# INLINE reified #-}
src/Capability/Sink/Internal/Class.hs view
@@ -12,6 +12,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeInType #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UnboxedTuples #-}@@ -21,6 +22,9 @@ module Capability.Sink.Internal.Class where +import Capability.Reflection+import Data.Coerce (coerce)+import Data.Kind (Type) import GHC.Exts (Proxy#, proxy#) -- | Sinking capability.@@ -28,7 +32,7 @@ -- An instance does not need to fulfill any additional laws -- besides the monad laws. class Monad m- => HasSink (tag :: k) (a :: *) (m :: * -> *) | tag m -> a+ => HasSink (tag :: k) (a :: Type) (m :: Type -> Type) | tag m -> a where -- | For technical reasons, this method needs an extra proxy argument. -- You only need it if you are defining new instances of 'HasSink'.@@ -39,5 +43,18 @@ -- | @yield \@tag a@ -- emits @a@ in the sink capability @tag@. yield :: forall tag a m. HasSink tag a m => a -> m ()-yield = yield_ (proxy# @_ @tag)+yield = yield_ (proxy# @tag) {-# INLINE yield #-}++--------------------------------------------------------------------------------++data instance Reified tag (HasSink tag a) m = ReifiedSink {_yield :: a -> m ()}++instance+ ( Monad m,+ Reifies s (Reified tag (HasSink tag a) m)+ ) =>+ HasSink tag a (Reflected s (HasSink tag a) m)+ where+ yield_ _ = coerce $ _yield (reified @s)+ {-# INLINE yield_ #-}
src/Capability/Sink/Internal/Strategies.hs view
@@ -34,29 +34,27 @@ import Capability.State.Internal.Class import Capability.State.Internal.Strategies.Common import Control.Lens (set)-import Control.Monad.IO.Class (MonadIO)-import Control.Monad.Primitive (PrimMonad) import qualified Control.Monad.State.Class as State-import Control.Monad.Trans.Class (MonadTrans, lift) import Data.Coerce (Coercible, coerce) import Data.DList (DList) import qualified Data.DList as DList import qualified Data.Generics.Product.Fields as Generic import qualified Data.Generics.Product.Positions as Generic import Data.IORef+import Data.Kind (Type) import Data.Mutable import Streaming import qualified Streaming.Prelude as S -- | Accumulate sunk values in a reverse order list.-newtype SinkStack m (a :: *) = SinkStack (m a)+newtype SinkStack m (a :: Type) = SinkStack (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) instance HasState tag [a] m => HasSink tag a (SinkStack m) where yield_ _ a = coerce @(m ()) $ modify' @tag (a:) {-# INLINE yield_ #-} -- | Accumulate sunk values in forward order in a difference list.-newtype SinkDList m (a :: *) = SinkDList (m a)+newtype SinkDList m (a :: Type) = SinkDList (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) -- | This instance may seem a bit odd at first. All it does is wrap each -- 'yield'ed value in a single element difference list. How does re-yielding@@ -75,7 +73,7 @@ {-# INLINE yield_ #-} -- | Accumulate sunk values with their own monoid.-newtype SinkLog m (a :: *) = SinkLog (m a)+newtype SinkLog m (a :: Type) = SinkLog (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) instance (Monoid w, HasState tag w m) => HasSink tag w (SinkLog m) where yield_ _ w = coerce @(m ()) $ modify' @tag (<> w)@@ -105,7 +103,7 @@ {-# INLINE yield_ #-} -- | Compose two accessors.-deriving via ((t2 :: (* -> *) -> * -> *) ((t1 :: (* -> *) -> * -> *) m))+deriving via ((t2 :: (Type -> Type) -> Type -> Type) ((t1 :: (Type -> Type) -> Type -> Type) m)) instance ( forall x. Coercible (m x) (t2 (t1 m) x) , Monad m, HasSink tag a (t2 (t1 m)) )
src/Capability/Source/Internal/Class.hs view
@@ -27,6 +27,7 @@ {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeInType #-}+{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UndecidableInstances #-}@@ -35,6 +36,9 @@ module Capability.Source.Internal.Class where +import Capability.Reflection+import Data.Coerce (coerce)+import Data.Kind (Type) import GHC.Exts (Proxy#, proxy#) -- | Sourcing capability.@@ -42,7 +46,7 @@ -- An instance does not need to fulfill any additional laws -- besides the monad laws. class Monad m- => HasSource (tag :: k) (a :: *) (m :: * -> *) | tag m -> a+ => HasSource (tag :: k) (a :: Type) (m :: Type -> Type) | tag m -> a where -- | For technical reasons, this method needs an extra proxy argument. -- You only need it if you are defining new instances of 'HasSource'.@@ -53,7 +57,7 @@ -- | @await \@tag a@ -- takes @a@ from the source capability @tag@. await :: forall tag a m. HasSource tag a m => m a-await = await_ (proxy# @_ @tag)+await = await_ (proxy# @tag) {-# INLINE await #-} -- | @awaits \@tag@@@ -64,3 +68,16 @@ awaits :: forall tag r m a. HasSource tag r m => (r -> a) -> m a awaits f = f <$> await @tag {-# INLINE awaits #-}++--------------------------------------------------------------------------------++data instance Reified tag (HasSource tag a) m = ReifiedSource {_await :: m a}++instance+ ( Monad m,+ Reifies s (Reified tag (HasSource tag a) m)+ ) =>+ HasSource tag a (Reflected s (HasSource tag a) m)+ where+ await_ _ = coerce $ _await (reified @s)+ {-# INLINE await_ #-}
src/Capability/Source/Internal/Strategies.hs view
@@ -38,12 +38,12 @@ import Control.Lens (view) import Control.Monad.Catch (MonadMask) import Control.Monad.IO.Class (MonadIO, liftIO)-import Control.Monad.Primitive (PrimMonad) import qualified Control.Monad.Reader.Class as Reader import qualified Control.Monad.State.Class as State import Control.Monad.Trans.Class (MonadTrans, lift) import Data.Coerce (Coercible, coerce) import Data.IORef+import Data.Kind (Type) import Data.Mutable import qualified Data.Generics.Product.Fields as Generic import qualified Data.Generics.Product.Positions as Generic@@ -52,7 +52,7 @@ -- | Derive 'HasSource' from @m@'s 'Control.Monad.Reader.Class.MonadReader' -- instance.-newtype MonadReader (m :: * -> *) (a :: *) = MonadReader (m a)+newtype MonadReader (m :: Type -> Type) (a :: Type) = MonadReader (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) instance Reader.MonadReader r m => HasSource tag r (MonadReader m) where@@ -74,7 +74,7 @@ -- exceptions. -- -- See 'ReadState'.-newtype ReadStatePure (m :: * -> *) (a :: *) = ReadStatePure (m a)+newtype ReadStatePure (m :: Type -> Type) (a :: Type) = ReadStatePure (m a) deriving (Functor, Applicative, Monad) instance HasState tag r m => HasSource tag r (ReadStatePure m) where@@ -86,7 +86,7 @@ -- Use this if the monad stack allows catching exceptions. -- -- See 'ReadStatePure'.-newtype ReadState (m :: * -> *) (a :: *) = ReadState (m a)+newtype ReadState (m :: Type -> Type) (a :: Type) = ReadState (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) instance@@ -104,7 +104,7 @@ awaits @oldtag $ view (Generic.position' @pos) {-# INLINE await_ #-} -deriving via ((t2 :: (* -> *) -> * -> *) ((t1 :: (* -> *) -> * -> *) m))+deriving via ((t2 :: (Type -> Type) -> Type -> Type) ((t1 :: (Type -> Type) -> Type -> Type) m)) instance ( forall x. Coercible (m x) (t2 (t1 m) x) , Monad m, HasSource tag r (t2 (t1 m)) )
src/Capability/State/Internal/Class.hs view
@@ -1,5 +1,8 @@ {-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE InstanceSigs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-}@@ -22,13 +25,16 @@ , modify' , gets , zoom+ , Reified (..) ) where import Capability.Constraints import Capability.Derive (derive)+import Capability.Reflection import Capability.Source.Internal.Class import Capability.Sink.Internal.Class-import Data.Coerce (Coercible)+import Data.Coerce (Coercible, coerce)+import Data.Kind (Type) import GHC.Exts (Proxy#, proxy#) -- | State capability@@ -43,7 +49,7 @@ -- prop> put @t s >> get @t = put @t s >> pure s -- prop> state @t f = get @t >>= \s -> let (a, s') = f s in put @t s' >> pure a class (Monad m, HasSource tag s m, HasSink tag s m)- => HasState (tag :: k) (s :: *) (m :: * -> *) | tag m -> s+ => HasState (tag :: k) (s :: Type) (m :: Type -> Type) | tag m -> s where -- | For technical reasons, this method needs an extra proxy argument. -- You only need it if you are defining new instances of 'HasState.@@ -70,7 +76,7 @@ -- Given the current state @s@ of the state capability @tag@ -- and @(a, s') = f s@, update the state to @s'@ and return @a@. state :: forall tag s m a. HasState tag s m => (s -> (a, s)) -> m a-state = state_ (proxy# @_ @tag)+state = state_ (proxy# @tag) {-# INLINE state #-} -- | @modify \@tag f@@@ -129,3 +135,39 @@ zoom = derive @t @'[HasState innertag inner] @cs {-# INLINE zoom #-}++--------------------------------------------------------------------------------++data instance Reified tag (HasState tag s) m = ReifiedState+ { _stateSource :: Reified tag (HasSource tag s) m,+ _stateSink :: Reified tag (HasSink tag s) m,+ _state :: forall a. (s -> (a, s)) -> m a+ }++instance+ ( Monad m,+ Reifies s' (Reified tag (HasState tag s) m)+ ) =>+ HasSource tag s (Reflected s' (HasState tag s) m)+ where+ await_ _ = coerce $ _await $ _stateSource $ reified @s'+ {-# INLINE await_ #-}++instance+ ( Monad m,+ Reifies s' (Reified tag (HasState tag s) m)+ ) =>+ HasSink tag s (Reflected s' (HasState tag s) m)+ where+ yield_ _ = coerce $ _yield $ _stateSink $ reified @s'+ {-# INLINE yield_ #-}++instance+ ( Monad m,+ Reifies s' (Reified tag (HasState tag s) m)+ ) =>+ HasState tag s (Reflected s' (HasState tag s) m)+ where+ state_ :: forall a. Proxy# tag -> (s -> (a, s)) -> Reflected s' (HasState tag s) m a+ state_ _ = coerce @((s -> (a, s)) -> m a) $ _state (reified @s')+ {-# INLINE state_ #-}
src/Capability/State/Internal/Strategies.hs view
@@ -34,13 +34,13 @@ import Capability.Source.Internal.Strategies () import Capability.Sink.Internal.Strategies () import Control.Monad.IO.Class (MonadIO, liftIO)-import Control.Monad.Primitive (PrimMonad) import qualified Control.Monad.State.Class as State import Control.Monad.Trans.Class (MonadTrans, lift) import Data.Coerce (Coercible, coerce) import qualified Data.Generics.Product.Fields as Generic import qualified Data.Generics.Product.Positions as Generic import Data.IORef+import Data.Kind (Type) import Data.Mutable import GHC.Exts (Proxy#) @@ -106,7 +106,7 @@ {-# INLINE state_ #-} -- | Compose two accessors.-deriving via ((t2 :: (* -> *) -> * -> *) ((t1 :: (* -> *) -> * -> *) m))+deriving via ((t2 :: (Type -> Type) -> Type -> Type) ((t1 :: (Type -> Type) -> Type -> Type) m)) instance ( forall x. Coercible (m x) (t2 (t1 m) x) , Monad m, HasState tag s (t2 (t1 m)) )
src/Capability/State/Internal/Strategies/Common.hs view
@@ -31,10 +31,11 @@ import Control.Monad.IO.Class (MonadIO) import Control.Monad.Primitive (PrimMonad)+import Data.Kind (Type) -- | Derive 'HasState' from @m@'s -- 'Control.Monad.State.Class.MonadState' instance.-newtype MonadState (m :: * -> *) (a :: *) = MonadState (m a)+newtype MonadState (m :: Type -> Type) (a :: Type) = MonadState (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad) -- | Derive a state monad from a reader over an 'Data.IORef.IORef'.@@ -66,5 +67,5 @@ -- > => HasState "foo" Int (MyState m) -- -- See 'ReaderIORef' for a specialized version over 'Data.IORef.IORef'.-newtype ReaderRef m (a :: *) = ReaderRef (m a)+newtype ReaderRef m (a :: Type) = ReaderRef (m a) deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
src/Capability/TypeOf.hs view
@@ -4,12 +4,14 @@ module Capability.TypeOf where +import Data.Kind (Type)+ -- | Type family associating a tag to the corresponding type. It is intended to -- simplify constraint declarations, by removing the need to redundantly specify -- the type associated to a tag. -- -- It is poly-kinded, which allows users to define their own kind of tags.--- Standard haskell types can also be used as tags by specifying the '*' kind+-- Standard haskell types can also be used as tags by specifying the 'Type' kind -- when defining the type family instance. -- -- Defining 'TypeOf' instances for 'GHC.TypeLits.Symbol's (typelevel string@@ -26,11 +28,11 @@ -- -- data Foo -- data Bar--- type instance TypeOf * Foo = Int--- type instance TypeOf * Bar = String+-- type instance TypeOf Type Foo = Int+-- type instance TypeOf Type Bar = String -- -- -- Same as: foo :: HasReader Foo Int M => … -- foo :: HasReader' Foo m => … -- foo = … -- @-type family TypeOf k (s :: k) :: *+type family TypeOf k (s :: k) :: Type
src/Capability/Writer.hs view
@@ -33,6 +33,7 @@ {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeInType #-}+{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UndecidableInstances #-}@@ -55,14 +56,18 @@ , SinkLog (..) -- ** Modifiers , module Capability.Accessors+ -- * Reflection+ , Reified (..) ) where import Capability.Accessors+import Capability.Reflection import Capability.Sink import Capability.State -- import deprecated module to reexport deprecated item for back-compat. import Capability.Stream import Data.Coerce (Coercible, coerce)+import Data.Kind (Type) import GHC.Exts (Proxy#, proxy#) -- | Writer capability@@ -92,7 +97,7 @@ -- regardless of whatever additional methods it provides and laws by which it -- abides. class (Monoid w, Monad m, HasSink tag w m)- => HasWriter (tag :: k) (w :: *) (m :: * -> *) | tag m -> w+ => HasWriter (tag :: k) (w :: Type) (m :: Type -> Type) | tag m -> w where -- | For technical reasons, this method needs an extra proxy argument. -- You only need it if you are defining new instances of 'HasReader'.@@ -117,13 +122,13 @@ -- Appends @w@ to the output of the writer capability @tag@ -- and returns the value @a@. writer :: forall tag w m a. HasWriter tag w m => (a, w) -> m a-writer = writer @tag+writer = writer_ (proxy# @tag) {-# INLINE writer #-} -- | @tell \@tag w@ -- appends @w@ to the output of the writer capability @tag@. tell :: forall tag w m. HasWriter tag w m => w -> m ()-tell = yield_ (proxy# @_ @tag)+tell = yield_ (proxy# @tag) {-# INLINE tell #-} -- | @listen \@tag m@@@ -131,7 +136,7 @@ -- in the writer capability @tag@ along with result of @m@. -- Appends the output of @m@ to the output of the writer capability @tag@. listen :: forall tag w m a. HasWriter tag w m => m a -> m (a, w)-listen = listen_ (proxy# @_ @tag)+listen = listen_ (proxy# @tag) {-# INLINE listen #-} -- | @pass \@tag m@@@ -139,11 +144,11 @@ -- @w@ to the output of the writer capability @tag@. -- @pass \@tag m@ instead appends @w' = f w@ to the output and returns @a@. pass :: forall tag w m a. HasWriter tag w m => m (a, w -> w) -> m a-pass = pass_ (proxy# @_ @tag)+pass = pass_ (proxy# @tag) {-# INLINE pass #-} -- | Compose two accessors.-deriving via ((t2 :: (* -> *) -> * -> *) ((t1 :: (* -> *) -> * -> *) m))+deriving via ((t2 :: (Type -> Type) -> Type -> Type) ((t1 :: (Type -> Type) -> Type -> Type) m)) instance ( forall x. Coercible (m x) (t2 (t1 m) x) , Monad m, HasWriter tag w (t2 (t1 m)) )@@ -178,3 +183,39 @@ -- | Type synonym using the 'TypeOf' type family to specify 'HasWriter' -- constraints without having to specify the type associated to a tag. type HasWriter' (tag :: k) = HasWriter tag (TypeOf k tag)++--------------------------------------------------------------------------------++data instance Reified tag (HasWriter tag w) m = ReifiedWriter+ { _writerSink :: Reified tag (HasSink tag w) m,+ _writer :: forall a. (a, w) -> m a,+ _listen :: forall a. m a -> m (a, w),+ _pass :: forall a. m (a, w -> w) -> m a+ }++instance+ ( Monoid w,+ Monad m,+ Reifies s (Reified tag (HasWriter tag w) m)+ ) =>+ HasSink tag w (Reflected s (HasWriter tag w) m)+ where+ yield_ _ = coerce $ _yield $ _writerSink $ reified @s+ {-# INLINE yield_ #-}++instance+ ( Monad m,+ Monoid w,+ Reifies s (Reified tag (HasWriter tag w) m)+ ) =>+ HasWriter tag w (Reflected s (HasWriter tag w) m)+ where+ writer_ :: forall a. Proxy# tag -> (a, w) -> Reflected s (HasWriter tag w) m a+ writer_ _ = coerce @((a, w) -> m a) $ _writer (reified @s)+ {-# INLINE writer_ #-}+ listen_ :: forall a. Proxy# tag -> Reflected s (HasWriter tag w) m a -> Reflected s (HasWriter tag w) m (a, w)+ listen_ _ = coerce @(m a -> m (a, w)) $ _listen (reified @s)+ {-# INLINE listen_ #-}+ pass_ :: forall a. Proxy# tag -> Reflected s (HasWriter tag w) m (a, w -> w) -> Reflected s (HasWriter tag w) m a+ pass_ _ = coerce @(m (a, w -> w) -> m a) $ _pass (reified @s)+ {-# INLINE pass_ #-}