packages feed

co-log-core 0.1.0 → 0.1.1

raw patch · 6 files changed

+320/−28 lines, 6 filesdep +doctestdep ~basePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: doctest

Dependency ranges changed: base

API changes (from Hackage documentation)

+ Colog.Core.Action: (&>) :: msg -> LogAction m msg -> m ()
+ Colog.Core.Action: (<&) :: LogAction m msg -> msg -> m ()
+ Colog.Core.Action: infix 5 &>
+ Colog.Core.Action: instance Data.Functor.Contravariant.Contravariant (Colog.Core.Action.LogAction m)
+ Colog.Core.IO: liftLogIO :: MonadIO m => LogAction IO msg -> LogAction m msg
+ Colog.Core.IO: logPrint :: forall a m. (Show a, MonadIO m) => LogAction m a
+ Colog.Core.IO: logPrintHandle :: forall a m. (Show a, MonadIO m) => Handle -> LogAction m a
+ Colog.Core.IO: logPrintStderr :: forall a m. (Show a, MonadIO m) => LogAction m a
+ Colog.Core.IO: logStringHandle :: MonadIO m => Handle -> LogAction m String
+ Colog.Core.IO: logStringStderr :: MonadIO m => LogAction m String
+ Colog.Core.IO: logStringStdout :: MonadIO m => LogAction m String
+ Colog.Core.IO: withLogPrintFile :: forall a m r. (Show a, MonadIO m) => FilePath -> (LogAction m a -> IO r) -> IO r
+ Colog.Core.IO: withLogStringFile :: MonadIO m => FilePath -> (LogAction m String -> IO r) -> IO r
- Colog.Core.Action: (>*<) :: (Applicative m) => LogAction m a -> LogAction m b -> LogAction m (a, b)
+ Colog.Core.Action: (>*<) :: Applicative m => LogAction m a -> LogAction m b -> LogAction m (a, b)
- Colog.Core.Action: LogAction :: msg -> m () -> LogAction m msg
+ Colog.Core.Action: LogAction :: (msg -> m ()) -> LogAction m msg
- Colog.Core.Action: divide :: (Applicative m) => (a -> (b, c)) -> LogAction m b -> LogAction m c -> LogAction m a
+ Colog.Core.Action: divide :: Applicative m => (a -> (b, c)) -> LogAction m b -> LogAction m c -> LogAction m a

Files

CHANGELOG.md view
@@ -1,20 +1,36 @@-Change log-==========+# Change log  `co-log-core` uses [PVP Versioning][1]. The change log is available [on GitHub][2]. -0.1.0-=====+## 0.1.1 — Nov 15, 2018 +* [#63](https://github.com/kowainik/co-log/issues/63):+  Add `logPrint`, `logPrintStderr`, `logPrintHandle` and `withLogPrintFile` to `Colog.Core.IO`.+* [#46](https://github.com/kowainik/co-log/issues/46):+  Moves `logStringStdout`, `logStringStderr`, `logStringHandle`,+  `withLogStringFile` from `Colog.Actions` to `Colog.Core.IO`.+* [#48](https://github.com/kowainik/co-log/issues/48):+  Adds `liftLogIO` function.+* [#49](https://github.com/kowainik/co-log/issues/49):+  Add `<&` and `&>`operators for `unLogAction`.+* [#47](https://github.com/kowainik/co-log/issues/47):+  Add `doctest` tests.+* [#13](https://github.com/kowainik/co-log/issues/13):+  Add `.cabal` file description and improve documentation.+* [#39](https://github.com/kowainik/co-log/issues/39):+  Support GHC-8.2.2 and GHC-8.6.2.++## 0.1.0+ * [#38](https://github.com/kowainik/co-log/issues/38):   Rename `cbind` to `cmapM`.  * [#37](https://github.com/kowainik/co-log/issues/37):   Add `base` bounds. -0.0.0-=====+## 0.0.0+ * Initially created.  [1]: https://pvp.haskell.org
co-log-core.cabal view
@@ -1,7 +1,20 @@+cabal-version:       2.0 name:                co-log-core-version:             0.1.0-description:         Logging library-synopsis:            Logging library+version:             0.1.1+synopsis:            Composable Contravariant Comonadic Logging Library+description:+    This package provides core types and functions to work with the @LogAction@ data type which is both simple and powerful.+    .+    @+    __newtype__ LogAction m msg = LogAction+    \    { unLogAction :: msg -> m ()+    \    }+    @+    .+    The ideas behind this package are described in the following blog post:+    .+    * [co-log: Composable Contravariant Combinatorial Comonadic Configurable Convenient Logging](https://kowainik.github.io/posts/2018-09-25-co-log)+ homepage:            https://github.com/kowainik/co-log bug-reports:         https://github.com/kowainik/co-log/issues license:             MPL-2.0@@ -12,8 +25,9 @@ category:            Logging build-type:          Simple extra-doc-files:     CHANGELOG.md-cabal-version:       2.0-tested-with:         GHC == 8.4.3+tested-with:         GHC == 8.2.2+                   , GHC == 8.4.4+                   , GHC == 8.6.2  source-repository head   type:                git@@ -25,8 +39,9 @@                            Colog.Core.Action                            Colog.Core.Class                            Colog.Core.Severity+                           Colog.Core.IO -  build-depends:       base >= 4.11 && < 5+  build-depends:       base >= 4.10 && < 4.13    ghc-options:         -Wall                        -Wincomplete-uni-patterns@@ -38,4 +53,23 @@                        -freverse-errors    default-language:    Haskell2010-  default-extensions:  InstanceSigs+  default-extensions:  ConstraintKinds+                       DeriveGeneric+                       GeneralizedNewtypeDeriving+                       InstanceSigs+                       LambdaCase+                       OverloadedStrings+                       RecordWildCards+                       ScopedTypeVariables+                       StandaloneDeriving+                       TupleSections+                       TypeApplications+                       ViewPatterns++test-suite doctest+  type:                 exitcode-stdio-1.0+  build-depends:        base >= 4.10 && < 4.13+                      , doctest ^>= 0.16.0+  default-language:     Haskell2010+  hs-source-dirs:       test+  main-is:              Doctests.hs
src/Colog/Core.hs view
@@ -1,9 +1,11 @@ module Colog.Core        ( module Colog.Core.Action        , module Colog.Core.Class+       , module Colog.Core.IO        , module Colog.Core.Severity        ) where  import Colog.Core.Action import Colog.Core.Class+import Colog.Core.IO import Colog.Core.Severity
src/Colog/Core/Action.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE CPP #-}  {- | Implements core data types and combinators for logging actions. -}@@ -6,6 +6,8 @@ module Colog.Core.Action        ( -- * Core type and instances          LogAction (..)+       , (<&)+       , (&>)           -- * 'Semigroup' combinators        , foldActions@@ -14,7 +16,7 @@        , cfilter        , cmap        , (>$<)-       , (>$)+       , (Colog.Core.Action.>$)        , cmapM           -- * Divisible combinators@@ -37,12 +39,21 @@        ) where  import Control.Monad (when, (>=>))+import Data.Coerce (coerce) import Data.Foldable (for_) import Data.List.NonEmpty (NonEmpty (..)) import Data.Monoid (Monoid (..)) import Data.Semigroup (Semigroup (..), stimesMonoid) import Data.Void (Void, absurd) +#if MIN_VERSION_base(4,12,0)+import qualified Data.Functor.Contravariant as Contravariant+#endif++{- $setup+>>> import Colog.Core.IO+-}+ ---------------------------------------------------------------------------- -- Core data type with instances ----------------------------------------------------------------------------@@ -107,6 +118,59 @@     mconcat = foldActions     {-# INLINE mconcat #-} +#if MIN_VERSION_base(4,12,0)+instance Contravariant.Contravariant (LogAction m) where+    contramap = cmap+    {-# INLINE contramap #-}++    (>$) = (Colog.Core.Action.>$)+    {-# INLINE (>$) #-}+#endif++{- | Operator version of 'unLogAction'. Note that because of the types, something like:++@+action <& msg1 <& msg2+@++doesn't make sense. Instead you want:++@+action <& msg1 >> action <& msg2+@++In addition, because '<&' has higher precedence than the other operators in this+module, the following:++@+f >$< action <& msg+@++is equivalent to:++@+(f >$< action) <& msg+@+-}+infix 5 <&+(<&) :: LogAction m msg -> msg -> m ()+(<&) = coerce+{-# INLINE (<&) #-}++{- | A flipped version of '<&'.++It shares the same precedence as '<&', so make sure to surround lower precedence+operators in parentheses:++@+msg &> (f >$< action)+@+-}+infix 5 &>+(&>) :: msg -> LogAction m msg -> m ()+(&>) = flip unLogAction+{-# INLINE (&>) #-}+ ---------------------------------------------------------------------------- -- Combinators ----------------------------------------------------------------------------@@ -170,7 +234,11 @@ cmap f (LogAction action) = LogAction (action . f) {-# INLINE cmap #-} --- | Operator version of 'cmap'.+{- | Operator version of 'cmap'.++>>> 1 &> (show >$< logStringStdout)+1+-} infixr 3 >$< (>$<) :: (a -> b) -> LogAction m b -> LogAction m a (>$<) = cmap@@ -179,6 +247,11 @@ {- | This combinator is @>$@ from contravariant functor. Replaces all locations in the output with the same value. The default definition is @contramap . const@, so this is a more efficient version.++>>> "Hello?" &> ("OUT OF SERVICE" >$ logStringStdout)+OUT OF SERVICE+>>> ("OUT OF SERVICE" >$ logStringStdout) <& 42+OUT OF SERVICE -} infixl 4 >$ (>$) :: b -> LogAction m b -> LogAction m a@@ -230,17 +303,38 @@ cmapM f (LogAction action) = LogAction (f >=> action) {-# INLINE cmapM #-} --- | @divide@ combinator from @Divisible@ type class.+{- | @divide@ combinator from @Divisible@ type class.++>>> logInt = LogAction print+>>> "ABC" &> divide (\s -> (s, length s)) logStringStdout logInt+ABC+3+-} divide :: (Applicative m) => (a -> (b, c)) -> LogAction m b -> LogAction m c -> LogAction m a divide f (LogAction actionB) (LogAction actionC) = LogAction $ \(f -> (b, c)) ->     actionB b *> actionC c --- | @conquer@ combinator from @Divisible@ type class.+{- | @conquer@ combinator from @Divisible@ type class.++Concretely, this is a 'LogAction' that does nothing:++>>> conquer <& "hello?"+>>> "hello?" &> conquer+-} conquer :: Applicative m => LogAction m a-conquer = LogAction $ const (pure ())+conquer = mempty  --- | Operator version of @'divide' 'id'@.+{- | Operator version of @'divide' 'id'@.++>>> logInt = LogAction print+>>> (logStringStdout >*< logInt) <& ("foo", 1)+foo+1+>>> (logInt >*< logStringStdout) <& (1, "foo")+1+foo+-} infixr 4 >*< (>*<) :: (Applicative m) => LogAction m a -> LogAction m b -> LogAction m (a, b) (LogAction actionA) >*< (LogAction actionB) = LogAction $ \(a, b) ->@@ -248,12 +342,20 @@ {-# INLINE (>*<) #-}  infixr 4 >*+{-| Perform a constant log action after another.++>>> logHello = LogAction (const (putStrLn "Hello!"))+>>> "Greetings!" &> (logStringStdout >* logHello)+Greetings!+Hello!+-} (>*) :: Applicative m => LogAction m a -> LogAction m () -> LogAction m a (LogAction actionA) >* (LogAction actionB) = LogAction $ \a ->     actionA a *> actionB () {-# INLINE (>*) #-}  infixr 4 *<+-- | A flipped version of '>*' (*<) :: Applicative m => LogAction m () -> LogAction m a -> LogAction m a (LogAction actionA) *< (LogAction actionB) = LogAction $ \a ->     actionA () *> actionB a@@ -263,11 +365,26 @@ lose :: (a -> Void) -> LogAction m a lose f = LogAction (absurd . f) --- | @choose@ combinator from @Decidable@ type class.+{- | @choose@ combinator from @Decidable@ type class.++>>> logInt = LogAction print+>>> f = choose (\a -> if a < 0 then Left "Negative" else Right a)+>>> f logStringStdout logInt <& 1+1+>>> f logStringStdout logInt <& (-1)+Negative+-} choose :: (a -> Either b c) -> LogAction m b -> LogAction m c -> LogAction m a choose f (LogAction actionB) (LogAction actionC) = LogAction (either actionB actionC . f) --- | Operator version of @'choose' 'id'@.+{- | Operator version of @'choose' 'id'@.++>>> dontPrintInt = LogAction (const (putStrLn "Not printing Int"))+>>> Left 1 &> (dontPrintInt >|< logStringStdout)+Not printing Int+>>> (dontPrintInt >|< logStringStdout) <& Right ":)"+:)+-} infixr 3 >|< (>|<) :: LogAction m a -> LogAction m b -> LogAction m (Either a b) (LogAction actionA) >|< (LogAction actionB) = LogAction (either actionA actionB)@@ -275,6 +392,10 @@  {- | If @msg@ is 'Monoid' then 'extract' performs given log action by passing 'mempty' to it.++>>> logPrint :: LogAction IO [Int]; logPrint = LogAction print+>>> extract logPrint+[] -} extract :: Monoid msg => LogAction m msg -> m () extract action = unLogAction action mempty@@ -282,21 +403,19 @@ -- TODO: write better motivation for comonads {- | This is a /comonadic extend/. It allows you to chain different transformations on messages. ->>> logToStdout = LogAction putStrLn >>> f (LogAction l) = l ".f1" *> l ".f2" >>> g (LogAction l) = l ".g"->>> unLogAction logToStdout "foo"+>>> unLogAction logStringStdout "foo" foo->>> unLogAction (extend f logToStdout) "foo"+>>> unLogAction (extend f logStringStdout) "foo" foo.f1 foo.f2->>> unLogAction (extend g $ extend f logToStdout) "foo"+>>> unLogAction (extend g $ extend f logStringStdout) "foo" foo.g.f1 foo.g.f2->>> unLogAction (logToStdout =>> f =>> g) "foo"+>>> unLogAction (logStringStdout =>> f =>> g) "foo" foo.g.f1 foo.g.f2- -} extend :: Semigroup msg => (LogAction m msg -> m ()) -> LogAction m msg -> LogAction m msg extend f (LogAction action) = LogAction $ \m -> f $ LogAction $ \m' -> action (m <> m')
+ src/Colog/Core/IO.hs view
@@ -0,0 +1,108 @@+module Colog.Core.IO+       ( -- * 'String' actions+         logStringStdout+       , logStringStderr+       , logStringHandle+       , withLogStringFile++         -- * 'Show' actions+       , logPrint+       , logPrintStderr+       , logPrintHandle+       , withLogPrintFile++         -- * Various combinators+       , liftLogIO+       ) where++import Colog.Core.Action (LogAction (..))+import Control.Monad.IO.Class (MonadIO, liftIO)+import System.IO (Handle, IOMode (AppendMode), hPrint, hPutStrLn, stderr, withFile)++----------------------------------------------------------------------------+-- String+----------------------------------------------------------------------------++{- | Action that prints 'String' to stdout.++>>> unLogAction logStringStdout "foo"+foo+-}+logStringStdout :: MonadIO m => LogAction m String+logStringStdout = LogAction (liftIO . putStrLn)++{- | Action that prints 'String' to stderr.++>>> unLogAction logStringStderr "foo"+foo+-}+logStringStderr :: MonadIO m => LogAction m String+logStringStderr = logStringHandle stderr++{- | Action that prints 'String' to 'Handle'.++>>> unLogAction (logStringHandle stderr) "foo"+foo+-}+logStringHandle :: MonadIO m => Handle -> LogAction m String+logStringHandle handle = LogAction $ liftIO . hPutStrLn handle++{- | Action that prints 'String' to file. Instead of returning 'LogAction' it's+implemented in continuation-passing style because it's more efficient to open+file only once at the start of the application and write to 'Handle' instead of+opening file each time we need to write to it.++Opens file in 'AppendMode'.++>>> logger action = unLogAction action "foo"+>>> withLogStringFile "/dev/stdout" logger+foo+-}+withLogStringFile :: MonadIO m => FilePath -> (LogAction m String -> IO r) -> IO r+withLogStringFile path action = withFile path AppendMode $ action . logStringHandle++----------------------------------------------------------------------------+-- Show+----------------------------------------------------------------------------++{- | Action that prints to stdout using 'Show'.++>>> unLogAction logPrint 5+5+-}+logPrint :: forall a m . (Show a, MonadIO m) => LogAction m a+logPrint = LogAction $ liftIO . print++{- | Action that prints to stderr using 'Show'.++>>> unLogAction logPrintStderr 5+5+-}+logPrintStderr :: forall a m . (Show a, MonadIO m) => LogAction m a+logPrintStderr = logPrintHandle stderr++{- | Action that prints to a 'Handle' using 'Show'.++>>> unLogAction (logPrintHandle stderr) 5+5+-}+logPrintHandle :: forall a m . (Show a, MonadIO m) => Handle -> LogAction m a+logPrintHandle handle = LogAction $ liftIO . hPrint handle++{- | Action that prints to a file using 'Show'. See 'withLogStringFile' for details.+-}+withLogPrintFile :: forall a m r . (Show a, MonadIO m) => FilePath -> (LogAction m a -> IO r) -> IO r+withLogPrintFile path action = withFile path AppendMode $ action . logPrintHandle++----------------------------------------------------------------------------+-- Misc+----------------------------------------------------------------------------++{- | Lifts a LogAction over IO into a more general Monad.++>>> logToStdout = LogAction putStrLn+>>> unLogAction (liftLogIO logToStdout) "foo"+foo+-}+liftLogIO :: MonadIO m => LogAction IO msg -> LogAction m msg+liftLogIO (LogAction action) = LogAction (liftIO . action)
+ test/Doctests.hs view
@@ -0,0 +1,13 @@+module Main+       ( main+       ) where++import Test.DocTest (doctest)++main :: IO ()+main = doctest+    [ "-XInstanceSigs"+    , "-XScopedTypeVariables"+    , "-XViewPatterns"+    , "src"+    ]