40
co-log: Composable Contravariant Comonadic Logging Component by Dmitrii Kovanikov 15 May 2019

15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

  • Upload
    others

  • View
    3

  • Download
    0

Embed Size (px)

Citation preview

Page 1: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

co-log: Composable Contravariant Comonadic Logging Component by Dmitrii Kovanikov

15 May 2019

Page 3: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Decomposinglogging task

■ What to log: raw text, message data type, Map Key Value■ Where to log: stdout/stderr, file, database, external service■ How to format output: raw text, coloured text, JSON■ Context: IO, pure, custom monad

3 / 38

Page 4: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Structure of theco-log logging framework

■ co-log-core: fundamental reusable abstractions■ co-log: monadic tagless final implementation of logging■ co-log-polysemy: logging based on extensible effects■ co-log-benchmarks: performance measurements

4 / 38

Page 5: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Core data type:LogAction

newtype LogAction m msg = LogAction

{ unLogAction :: msg -> m ()

}

logStringStdout :: LogAction IO String

logStringStdout = LogAction putStrLn

5 / 38

Page 6: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

LOGGER — VALUEThe main idea is to treat logging action as a value instead of a function that performs some side effects when called.

6 / 38

Page 7: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

How to use loggerif it is a value?

1. Pass explicitly as an argument.2. Store inside monadic context so it can be extracted and used automatically.

7 / 38

Page 8: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

1.ComposabilitySemigroup and Monoid typeclasses

8 / 38

<>

Page 9: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Semigrouptypeclass

class Semigroup m where

(<>) :: m -> m -> m

instance Applicative m => Semigroup (LogAction m msg) where

(<>) :: LogAction m msg -> LogAction m msg -> LogAction m msg

LogAction l1 <> LogAction l2 =

LogAction $ \msg -> l1 msg *> l2 msg

Semigroup: perform multiple actions over the same message9 / 38

Page 10: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Monoidtypeclass

class Semigroup m => Monoid m where

mempty :: m

instance Applicative m => Monoid (LogAction m msg) where

mempty :: LogAction m msg

mempty = LogAction $ \_ -> pure ()

Monoid: empty logging action that does nothing10 / 38

Page 11: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

2.ContravarianceContravariant, Divisible and Decidable typeclasses

11 / 38

Page 12: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Contravarianttypeclass

class Contravariant f where

contramap :: (a -> b) -> f b -> f a

class Functor f where

fmap :: (a -> b) -> f a -> f b

fmap :: (a -> b) -> f a -> f b

contramap :: (a -> b) -> f b -> f a12 / 38

Page 13: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Contravariantinstance for LogAction

instance Contravariant (LogAction m) where

contramap :: (a -> b) -> LogAction m b -> LogAction m a

contramap f (LogAction action) = LogAction (action . f)

Contravariant: ability to change type of the consumed message

13 / 38

Page 14: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Contravariant use case:Formatting

data Message = Message

{ messageText :: String

, messageTags :: [Tag]

}

formatMessage :: Message -> String

logMessageStdout :: LogAction IO MessagelogMessageStdout = contramap formatMessage logStringStdout

14 / 38

Page 15: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

MonadicContravariant

cmapM :: Monad m

=> (a -> m b) -> LogAction m b -> LogAction m acmapM f (LogAction action) = LogAction (action <=< f)

15 / 38

Compare:

contramap f (LogAction action) = LogAction (action . f)cmapM f (LogAction action) = LogAction (action <=< f)

Page 16: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

MonadicContravariant use casedata Message = Message

{ messageText :: String

, messageTime :: UTCTime

}

withTime :: String -> IO Message

withTime txt = Message txt <$> getCurrentTime

logUtcStringStdout :: LogAction IO StringlogUtcStringStdout = cmapM withTime logMessageStdout

16 / 38

Page 17: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Contravariantfilter

cfilter

:: Applicative m

=> (msg -> Bool)

-> LogAction m msg

-> LogAction m msg

cfilter p (LogAction action) = LogAction $ \msg -> when (p msg) (action msg)

17 / 38

Page 18: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Contravariantfilter use case

data Severity = Debug | Info | Warning | Error ...

data Message = Message

{ messageSeverity :: Severity

, messageText :: String

}

logWarningMessageStdout :: LogAction IO Message

logWarningMessageStdout = cfilter

(\(Message sev _) -> sev >= Warning) logMessageStdout 18 / 38

Page 19: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Divisibletypeclass

class Contravariant f => Divisible f where

conquer :: f a

divide :: (a -> (b, c)) -> f b -> f c -> f a

Divisible: split a message into two parts and log each piece

19 / 38

Page 20: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Divisibleinstance

instance Applicative m => Divisible (LogAction m) where

conquer :: LogAction m a

conquer = mempty

divide :: (a -> (b, c))

-> LogAction m b

-> LogAction m c

-> LogAction m a

divide f (LogAction logB) (LogAction logC) = LogAction $ \(f -> (b, c)) -> logB b *> logC c

20 / 38

Page 21: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Decidabletypeclass

class Divisible f => Decidable f where

lose :: (a -> Void) -> f a choose :: (a -> Either b c) -> f b -> f c -> f a

Decidable: decide what part of the message to log

21 / 38

Page 22: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

22 / 38

Page 23: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Decidableinstanceinstance Applicative m => Decidable (LogAction m) where

lose :: (a -> Void) -> LogAction m a

lose f = LogAction (absurd . f)

choose :: (a -> Either b c)

-> LogAction m b

-> LogAction m c

-> LogAction m a

choose f (LogAction logB) (LogAction logC) = LogAction (either logB logC . f)

23 / 38

Page 24: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Decidableuse case

cfilter

:: Applicative m

=> (a -> Bool)

-> LogAction m a

-> LogAction m a

cfilter p action = choose

(\msg -> if p msg then Left () else Right msg)

mempty action

24 / 38

Page 25: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

3.ComonadsComonad typeclass

25 / 38

Page 26: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Comonadtypeclass

class Functor w => Comonad w where

extract :: w a -> a

extend :: (w a -> b) -> w a -> w b duplicate :: w a -> w (w a)

26 / 38

Page 27: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Comonad instancefor the function arrow

instance Monoid m => Comonad ((->) m) where

extract :: (m -> a) -> a

extract f = f mempty

duplicate :: (m -> a) -> (m -> m -> a) duplicate f = \m1 m2 -> f (m1 <> m2)

27 / 38

Page 28: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Implementing comonadic ideasfor LogAction: extract

extract :: Monoid msg => LogAction m msg -> m ()extract (LogAction action) = action mempty

28 / 38

Page 29: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Implementing comonadic ideasfor LogAction: duplicate

duplicate

:: Semigroup msg

=> LogAction m msg

-> LogAction m (msg, msg)

duplicate (LogAction action) = LogAction $ \(m1, m2) -> action (m1 <> m2)

29 / 38

Page 30: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Implementing comonadic ideasfor LogAction: multiplicate

multiplicate

:: (Foldable f, Semigroup msg)

=> LogAction m msg

-> LogAction m (f msg)

multiplicate (LogAction action) = LogAction $ \msgs -> action (fold msgs)

30 / 38

Page 31: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Abstractionsrecap

■ Semigroup: multiple actions over a single message (concurrency)■ Monoid: empty logger that does nothing (disabling logging)■ Contravariant: change the type of the message (formatting)■ Divisible: log batch of messages independently (modularity)■ Decidable: decide what to log (message filtering)■ Comonad: combine multiple messages into one (optimization)

31 / 38

Page 32: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

4.co-log in production applicationsHow to use co-log in your Haskell application?

32 / 38

Page 33: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

How we use co-logStep 1: Application environment

Create an environment that stores LogAction

33 / 38

data Env m = Env

{ envPort :: Port

, envLogAction :: LogAction m Message }

Page 34: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

How we use co-logStep 2: Implement HasLog instance

Lens-like typeclass for accessing and modifying LogAction

34 / 38

instance HasLog (Env m) Message m where

getLogAction :: Env m -> LogAction m Message

getLogAction = envLogAction

setLogAction :: LogAction m msg -> Env m -> Env m

setLogAction newAction env =

env { envLogAction = newAction }

Page 35: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

How we use co-logStep 3: Create monad for your application

newtype App a = App

{ unApp :: ReaderT (Env App) IO a

} deriving ( Functor, Applicative, Monad , MonadIO, MonadReader (Env App))

35 / 38

Page 36: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

How we use co-logStep 4: Write function that uses logger

smsSendTestHandler

:: (MonadSms m, WithLog env Message m)

=> Phone

-> m ()

smsSendTestHandler phone = do

log D $ "Sending test sms to: " <> show phone sendSms phone "Test message"

36 / 38

Page 37: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Output example

37 / 38

Page 38: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Ecosystem

■ co-log: 4 core Haskell packages■ co-log-sys: Syslog implementation of co-log■ scala-colog: Scala implementation of the co-log ideas■ three-layer: Example of using co-log with the Three

Layer Cake architecture■ Blog post: co-log: Composable Contravariant

Combinatorial Comonadic Configurable Convenient Logging

38 / 38

Page 39: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Thanks!!Questions?

Page 40: 15 May 2019 co-log: Composable by Dmitrii Kovanikov ... · scala-colog: Scala implementation of the co-log ideas three-layer: Example of using co-log with the Three Layer Cake architecture

Credits

Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY.