packages feed

capability 0.1.0.0 → 0.2.0.0

raw patch · 8 files changed

+87/−45 lines, 8 filesdep ~generic-lensPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: generic-lens

API changes (from Hackage documentation)

- Capability.Stream: instance forall k (tag :: k) a (m :: * -> *). Capability.Writer.HasWriter tag (Data.DList.DList a) m => Capability.Stream.HasStream tag a (Capability.Stream.StreamDList m)
- Capability.Writer: WriterLog :: m a -> WriterLog m a
- Capability.Writer: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Capability.Writer.WriterLog m)
- Capability.Writer: instance Control.Monad.Primitive.PrimMonad m => Control.Monad.Primitive.PrimMonad (Capability.Writer.WriterLog m)
- Capability.Writer: instance GHC.Base.Applicative m => GHC.Base.Applicative (Capability.Writer.WriterLog m)
- Capability.Writer: instance GHC.Base.Functor m => GHC.Base.Functor (Capability.Writer.WriterLog m)
- Capability.Writer: instance GHC.Base.Monad m => GHC.Base.Monad (Capability.Writer.WriterLog m)
- Capability.Writer: newtype WriterLog m a
- Capability.Writer: tell_ :: HasWriter tag w m => Proxy# tag -> w -> m ()
+ Capability.Stream: StreamLog :: m a -> StreamLog m
+ Capability.Stream: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Capability.Stream.StreamLog m)
+ Capability.Stream: instance Control.Monad.Primitive.PrimMonad m => Control.Monad.Primitive.PrimMonad (Capability.Stream.StreamLog m)
+ Capability.Stream: instance GHC.Base.Applicative m => GHC.Base.Applicative (Capability.Stream.StreamLog m)
+ Capability.Stream: instance GHC.Base.Functor m => GHC.Base.Functor (Capability.Stream.StreamLog m)
+ Capability.Stream: instance GHC.Base.Monad m => GHC.Base.Monad (Capability.Stream.StreamLog m)
+ Capability.Stream: instance forall k (tag :: k) a (m :: * -> *). Capability.Stream.HasStream tag (Data.DList.DList a) m => Capability.Stream.HasStream tag a (Capability.Stream.StreamDList m)
+ Capability.Stream: instance forall k w (tag :: k) (m :: * -> *). (GHC.Base.Monoid w, Capability.State.Internal.Class.HasState tag w m) => Capability.Stream.HasStream tag w (Capability.Stream.StreamLog m)
+ Capability.Stream: newtype StreamLog m (a :: *)
+ Capability.Writer: StreamLog :: m a -> StreamLog m
+ Capability.Writer: newtype StreamLog m (a :: *)
+ Capability.Writer: type WriterLog = StreamLog
- Capability.Writer: class (Monoid w, Monad m) => HasWriter (tag :: k) (w :: *) (m :: * -> *) | tag m -> w
+ Capability.Writer: class (Monoid w, Monad m, HasStream tag w m) => HasWriter (tag :: k) (w :: *) (m :: * -> *) | tag m -> w

Files

ChangeLog.md view
@@ -1,5 +1,12 @@ # Revision history for capability +## 0.2.0.0 -- 2019-03-22++* Make HasStream a superclass of HasWriter.+  See [#64](https://github.com/tweag/capability/pull/64)+* Bumped version bounds on generic-lens.+  See [#67](https://github.com/tweag/capability/pull/67)+ ## 0.1.0.0 -- 2018-10-04  * Initial release.
README.md view
@@ -90,11 +90,8 @@ For more complex examples, see the [Examples section](#examples) and the [`examples` subtree](./examples). -This package is not available on Hackage yet, as some of its-dependencies have not been updated to GHC 8.6, yet.--API documentation can be found in the artifacts tab of any successful-build in the [CircleCI project][circleci].+API documentation can be found on+[Hackage](http://hackage.haskell.org/package/capability).  [circleci]: https://circleci.com/gh/tweag/capabilities-via/tree/master [mtl]: http://hackage.haskell.org/package/mtl
capability.cabal view
@@ -1,5 +1,5 @@ name: capability-version: 0.1.0.0+version: 0.2.0.0 homepage: https://github.com/tweag/capability license: BSD3 license-file: LICENSE@@ -47,7 +47,7 @@       base >= 4.12 && < 5.0     , dlist >= 0.8 && < 0.9     , exceptions >= 0.6 && < 0.11-    , generic-lens >= 1.0 && < 1.1+    , generic-lens >= 1.0 && < 1.2     , lens >= 4.16 && < 5.0     , monad-control >= 1.0 && < 1.1     , monad-unlift >= 0.2 && < 0.3
examples/WordCount.hs view
@@ -14,7 +14,7 @@  import Capability.Reader import Capability.State-import Capability.Writer+import Capability.Stream import Control.Lens (ifor_) import Data.Coerce (coerce) import Data.Map.Strict (Map)@@ -56,21 +56,21 @@  -- | Count the occurrence of a single letter. countLetter ::-  HasWriter "letterCount" (Occurrences Char) m+  HasStream "letterCount" (Occurrences Char) m   => Char -> m ()-countLetter letter = tell @"letterCount" (oneOccurrence letter)+countLetter letter = yield @"letterCount" (oneOccurrence letter)  -- | Count the occurrence of a single word. countWord ::-  HasWriter "wordCount" (Occurrences Text) m+  HasStream "wordCount" (Occurrences Text) m   => Text -> m ()-countWord word = tell @"wordCount" (oneOccurrence word)+countWord word = yield @"wordCount" (oneOccurrence word)   -- | Count the occurrence of a single word and all the letters in it. countWordAndLetters ::-  ( HasWriter "letterCount" (Occurrences Char) m-  , HasWriter "wordCount" (Occurrences Text) m )+  ( HasStream "letterCount" (Occurrences Char) m+  , HasStream "wordCount" (Occurrences Text) m )   => Text -> m () countWordAndLetters word = do   countWord word@@ -80,8 +80,8 @@ -- | Count the occurrences of words and letters in a text, -- excluding white space. countWordsAndLettersInText ::-  ( HasWriter "letterCount" (Occurrences Char) m-  , HasWriter "wordCount" (Occurrences Text) m )+  ( HasStream "letterCount" (Occurrences Char) m+  , HasStream "wordCount" (Occurrences Text) m )   => Text -> m () countWordsAndLettersInText text =   mapM_ countWordAndLetters (Text.words text)@@ -98,14 +98,14 @@ -- | Counter application monad. newtype Counter a = Counter { runCounter :: CounterCtx -> IO a }   deriving (Functor, Applicative, Monad) via (ReaderT CounterCtx IO)-  deriving (HasWriter "letterCount" (Occurrences Char)) via-    (WriterLog  -- Generate HasWriter using HasState of Monoid+  deriving (HasStream "letterCount" (Occurrences Char)) via+    (StreamLog  -- Generate HasStream using HasState of Monoid     (ReaderIORef  -- Generate HasState from HasReader of IORef     (Field "letterCount" "ctx"  -- Focus on the field letterCount     (MonadReader  -- Generate HasReader using mtl MonadReader     (ReaderT CounterCtx IO)))))  -- Use mtl ReaderT newtype-  deriving (HasWriter "wordCount" (Occurrences Text)) via-    WriterLog (ReaderIORef+  deriving (HasStream "wordCount" (Occurrences Text)) via+    StreamLog (ReaderIORef     (Field "wordCount" "ctx" (MonadReader (ReaderT CounterCtx IO))))  
examples/Writer.hs view
@@ -9,6 +9,7 @@ module Writer where  import Capability.State+import Capability.Stream import Capability.Writer import Control.Monad.State.Strict (State, StateT (..), runState) import Data.Monoid (Sum (..))@@ -20,9 +21,10 @@ -- | Increase a counter using a writer monad. useWriter :: HasWriter "count" (Sum Int) m => m () useWriter = do-  tell @"count" 1-  tell @"count" 2-  tell @"count" 3+  -- Add 3 and retrieve result+  ((), count) <- listen @"count" (tell @"count" 3)+  -- Duplicate+  tell @"count" count   -- | Mix writer and state monad operations on the same tag.@@ -49,7 +51,7 @@ -- via clause. newtype WriterM a = WriterM (State Int a)   deriving (Functor, Applicative, Monad)-  deriving (HasWriter "count" (Sum Int))+  deriving (HasStream "count" (Sum Int), HasWriter "count" (Sum Int))     via WriterLog (Coerce (Sum Int) (MonadState (State Int)))  runWriterM :: WriterM a -> (a, Int)@@ -63,7 +65,7 @@ -- See caveat on 'mixWriterState'. newtype BadWriterM a = BadWriterM (State Int a)   deriving (Functor, Applicative, Monad)-  deriving (HasWriter "count" (Sum Int))+  deriving (HasStream "count" (Sum Int), HasWriter "count" (Sum Int))     via WriterLog (Coerce (Sum Int) (MonadState (State Int)))   deriving (HasState "count" Int)     via MonadState (State Int)
src/Capability/Stream.hs view
@@ -40,13 +40,13 @@     -- * Strategies   , StreamStack(..)   , StreamDList(..)+  , StreamLog(..)     -- ** Modifiers   , module Capability.Accessors   ) where  import Capability.Accessors import Capability.State-import Capability.Writer import Control.Monad.IO.Class (MonadIO) import Control.Monad.Primitive (PrimMonad) import Control.Monad.Trans.Class (MonadTrans, lift)@@ -86,15 +86,46 @@ -- | Accumulate streamed values in forward order in a difference list. newtype StreamDList m (a :: *) = StreamDList (m a)   deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)-instance HasWriter tag (DList a) m => HasStream tag a (StreamDList m) where-  yield_ _ = coerce @(a -> m ()) $ tell @tag . DList.singleton+-- | 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+-- something else constitute a strategy for implementing 'HasStream' in the+-- first place? The answer is that difference lists form a monoid, which allows+-- a second stragegy to be used which accumulates all 'yield's in a single+-- value, actually eliminating the 'HasStream' constraint this time.+--+-- 'StreamLog' below in fact does this, so the easiest way to fully eliminate+-- the 'HasStream' constraint as described above is:+--+-- > deriving (HasStream tag w) via+-- >   StreamDList (StreamLog (MonadState SomeStateMonad))+instance HasStream tag (DList a) m => HasStream tag a (StreamDList m) where+  yield_ _ = coerce @(a -> m ()) $ yield @tag . DList.singleton   {-# INLINE yield_ #-} +-- | Accumulate streamed values with their own monoid.+newtype StreamLog m (a :: *) = StreamLog (m a)+  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)+instance (Monoid w, HasState tag w m) => HasStream tag w (StreamLog m) where+    yield_ _ w = coerce @(m ()) $ modify' @tag (<> w)+    {-# INLINE yield_ #-}+ instance Monad m => HasStream tag a (S.Stream (Of a) m) where   yield_ _ = S.yield   {-# INLINE yield_ #-}  -- | Lift one layer in a monad transformer stack.+--+-- Note, that if the 'HasStream' instance is based on 'HasState', then it is+-- more efficient to apply 'Lift' to the underlying state capability. E.g.+-- you should favour+--+-- > deriving (HasStream tag w) via+-- >   StreamLog (Lift (SomeTrans (MonadState SomeStateMonad)))+--+-- over+--+-- > deriving (HasStream tag w) via+-- >   Lift (SomeTrans (StreamLog (MonadState SomeStateMonad))) instance (HasStream tag a m, MonadTrans t, Monad (t m))   => HasStream tag a (Lift (t m))   where
src/Capability/Writer.hs view
@@ -46,15 +46,15 @@   , listen   , pass     -- * Strategies-  , WriterLog(..)+  , WriterLog+  , StreamLog (..)     -- ** Modifiers   , module Capability.Accessors   ) where  import Capability.Accessors import Capability.State-import Control.Monad.IO.Class (MonadIO)-import Control.Monad.Primitive (PrimMonad)+import Capability.Stream import Data.Coerce (Coercible, coerce) import GHC.Exts (Proxy#, proxy#) @@ -69,7 +69,22 @@ -- prop> listen @t (m >>= k) = listen @t m >>= \(a, w1) -> listen @t (k a) >>= \(b, w2) -> pure (b, w1 `mappend` w2) -- prop> pass @t (tell @t w >> pure (a, f)) = tell @t (f w) >> pure a -- prop> writer @t (a, w) = tell @t w >> pure a-class (Monoid w, Monad m)+--+-- = A note on the 'HasStream' super class.+--+-- 'HasStream' offers one 'yield' method with the same signature as 'tell'.+-- Many people's intuition, however, wouldn't connect the two: 'yield'ing+-- tosses the value down some black-box chute, while 'tell'ing grows and+-- accumulation via the monoid. The connection is since the 'chute' is opaque,+-- the tosser cannot rule out there being such an accumulation at the chutes+-- other end.+--+-- Formally, we reach the same conclusion. 'HasStream' has no laws,+-- indicating the user can make no assumptions beyond the signature of 'yield'.+-- 'HasWriter', with 'tell' defined as 'yield', is thus always compatable+-- regardless of whatever additional methods it provides and laws by which it+-- abides.+class (Monoid w, Monad m, HasStream tag w m)   => HasWriter (tag :: k) (w :: *) (m :: * -> *) | tag m -> w   where     -- | For technical reasons, this method needs an extra proxy argument.@@ -79,11 +94,6 @@     writer_ :: Proxy# tag -> (a, w) -> m a     -- | For technical reasons, this method needs an extra proxy argument.     -- You only need it if you are defining new instances of 'HasReader'.-    -- Otherwise, you will want to use 'tell'.-    -- See 'tell' for more documentation.-    tell_ :: Proxy# tag -> w -> m ()-    -- | For technical reasons, this method needs an extra proxy argument.-    -- You only need it if you are defining new instances of 'HasReader'.     -- Otherwise, you will want to use 'listen'.     -- See 'listen' for more documentation.     listen_ :: Proxy# tag -> m a -> m (a, w)@@ -106,7 +116,7 @@ -- | @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 = tell_ (proxy# @_ @tag)+tell = yield_ (proxy# @_ @tag) {-# INLINE tell #-}  -- | @listen \@tag m@@@ -132,16 +142,13 @@   , Monad m, HasWriter tag w (t2 (t1 m)) )   => HasWriter tag w ((t2 :.: t1) m) -newtype WriterLog m a = WriterLog (m a)-  deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)+type WriterLog = StreamLog  instance (Monoid w, HasState tag w m)   => HasWriter tag w (WriterLog m)   where-    writer_ tag (a, w) = tell_ tag w >> pure a+    writer_ tag (a, w) = yield_ tag w >> pure a     {-# INLINE writer_ #-}-    tell_ _ w = coerce @(m ()) $ modify' @tag (<> w)-    {-# INLINE tell_ #-}     listen_ :: forall a. Proxy# tag -> WriterLog m a -> WriterLog m (a, w)     listen_ _ m = coerce @(m (a, w)) $ do       w0 <- get @tag
src/Capability/Writer/Discouraged.hs view
@@ -43,8 +43,6 @@     writer_ :: forall a. Proxy# tag -> (a, w) -> Lift (t m) a     writer_ _ = coerce @((a, w) -> t m a) $ lift . writer @tag     {-# INLINE writer_ #-}-    tell_ _ = coerce @(w -> t m ()) $ lift . tell @tag-    {-# INLINE tell_ #-}     listen_ :: forall a. Proxy# tag -> Lift (t m) a -> Lift (t m) (a, w)     listen_ _ = coerce @(t m a -> t m (a, w)) $ \m -> do       u <- askUnlift