-- | 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. -- -- == 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.Stream" streaming effect (aka generators) -- -- Some of the capability modules have a “discouraged” companion (such as -- "Capability.Writer.Discouraged"). These modules contain deriving-via -- combinators which you can use if you absolutely must: they are correct, but -- inefficient, so we recommend that you do not. -- -- == 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