-- | A capability is a type class over a monad which specifies the effects that -- a function is allowed to perform. Capabilities differ from traditional monad -- transformer type classes in that they are completely independent of the way -- the monad is constructed. A state capability can for instance be implemented -- as a lens on a field in a larger state monad, or an error capability could -- provide for throwing only a subset of the errors of an error monad. -- -- This library defines several standard, reusable capabilities that replace the -- mtl's monad-transformer type classes. Because capabilities are not tied to -- a particular implementation of the monad, they cannot be discharged by -- instance resolution. Instead this library provides combinators in the form of -- newtypes with instances, to be used with deriving-via. To learn about -- deriving via, watch Baldur Blondal's introductory video -- <https://skillsmatter.com/skillscasts/10934-lightning-talk-stolen-instances-taste-just-fine>. -- -- By way of comparison, with the mtl you would write something like -- -- @ -- foo :: (MonadReader E, MonadState S) => a -> m () -- @ -- -- You can use @foo@ at type @a -> ReaderT E (State S)@. But you can't use @foo@ -- with the @ReaderT@ pattern -- <https://www.fpcomplete.com/blog/2017/06/readert-design-pattern>. With this -- library, you would instead have: -- -- @ -- foo :: (HasReader "conf" E, HasState "st" S) => a -> m () -- @ -- -- Where @"conf"@ and @"st"@ are the names (also referred to as tags) of the -- capabilities demanded by foo. Contrary to the mtl, capabilities are named, -- rather than disambiguated by the type of their implied state, or exception. -- This makes it easy to have multiple state capabilities. -- -- To /provide/ these capabilities, for instance with the ReaderT pattern, do as -- follows (for a longer form tutorial, check the -- <https://github.com/tweag/capability#readme README>): -- -- @ -- newtype MyM a = MyM (ReaderT (E, IORef s)) -- deriving (Functor, Applicative, Monad) -- deriving (HasState "st" Int) via -- ReaderIORef (Rename 2 (Pos 2 () -- (MonadReader (ReaderT (E, IORef s) IO)))) -- deriving (HasReader "conf" Int) via -- (Rename 1 (Pos 1 () -- (MonadReader (ReaderT (E, IORef s) IO)))) -- @ -- -- Then you can use @foo@ at type @MyM@. Or any other type which can provide -- these capabilites. -- -- === Functional capabilities -- -- When writing applications, as opposed to libraries, a capability /name/ often -- determines its type parameters. It can be tiresome to write -- -- @ -- f :: HasReader "config" Config m => … -- @ -- -- over and over again. -- -- To avoid this, each capability comes with a /functional/—here -- @HasReader\'@—variant (in this terminology @HasReader@ is -- /relational/). Where the type is deduced from the capability's name. The -- mapping from name to type is done with the @'Capability.TypeOf.TypeOf'@ -- family, which is re-exported by every capability module. -- -- @ -- type instance TypeOf Symbol "config" = Config -- -- f :: HasReader' "config" m => … -- @ -- -- == Module structure -- -- Each module introduces a capability type class (or several related type -- classes). Each class comes with a number of instances on newtypes (each -- newtype should be seen as a combinator to be used with deriving-via to -- provide the capability). Many newtypes come from the common -- "Capability.Accessors" module (re-exported by each of the other modules), -- which in particular contains a number of ways to address components of a data -- type using the generic-lens library. -- -- * "Capability.Reader" reader effects -- * "Capability.State" state effects -- * "Capability.Writer" writer effects -- * "Capability.Error" throw and catch errors -- * "Capability.Source" streaming in effect -- * "Capability.Sink" streaming out effect (aka generators) -- -- The effects are not all independent: -- -- > Source Sink -- > / \ / \ -- > / \ / \ -- > Reader State Writer -- -- "Capability.Source" and "Capability.Sink" have just a method each, and no laws. -- The bottom three, familiar from mtl, add methods and laws relating them. -- The use of tags allows one to have independent effects that share a superclass. -- E.g. @HasState "foo" Int@ and @HasWriter "bar" String@. -- -- 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 -- -- The tags of capabilities can be of any kind, they are not restricted to -- symbols. When exporting functions demanding capabilities in libraries, it is -- recommended to use a type as follows: -- -- @ -- data Conf -- -- foo :: HasReader Conf C => m () -- @ -- -- This way, @Conf@ can be qualified in case of a name conflict with another -- library. module Capability where