Decorator-style handler #298
Replies: 4 comments 3 replies
-
|
In a more generic fashion, the runLoggerAbove severity impl1 impl2 = interpret $ \_ -> \case
Log severity message -> do
if severity > minSeverity then do
-- use `impl1` as handler
else do
-- use `impl2` as handler |
Beta Was this translation helpful? Give feedback.
-
|
Ok, after some debugging I realized that runLoggerAbove :: (Logger :> es) => Severity -> Eff (Logger : es) a -> Eff es a
runLoggerAbove minSeverity = interpret $ \_ -> \case
Log severity message -> do
when (severity > minSeverity) $ do
log severity message |
Beta Was this translation helpful? Give feedback.
-
|
This leaves the question open as to how to deal with the "generic" approach that supports passing handlers as arguments, and decides which one to use based on the |
Beta Was this translation helpful? Give feedback.
-
Have a look at |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I'm trying to get the following code to compile:
{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} module Main where import Control.Monad import Effectful import Effectful.Dispatch.Dynamic import System.IO import Prelude hiding (log) data Severity = Info | Error | Fatal deriving (Show, Eq, Ord, Bounded) data Logger e :: Effect where Log :: Severity -> String -> (Logger e) m () type instance DispatchOf (Logger e) = Dynamic log :: (Logger e :> es) => Severity -> String -> Eff es () log severity message = send $ Log severity message runNoLogger :: Eff (Logger e : es) a -> Eff es a runNoLogger = interpret $ \_ -> \case Log _ _ -> return () runLoggerAbove :: (Logger e :> es) => Severity -> Eff (Logger e : es) a -> Eff es a runLoggerAbove minSeverity = interpret $ \_ -> \case Log severity message -> do when (severity > minSeverity) $ do log severity message runHandleLogger :: (IOE :> es) => Handle -> Eff (Logger e : es) a -> Eff es a runHandleLogger handle = interpret $ \_ -> \case Log severity message -> do liftIO $ hPutStrLn handle (prefix severity ++ " " ++ message) prefix :: Severity -> String prefix = \case Info -> "[INFO]" Error -> "[ERROR]" Fatal -> "[FATAL]" program :: (Logger e :> es) => Eff es () program = do log Info "Hello, world!" log Error "Something went wrong!" log Fatal "Everything is on fire!" main :: IO () main = do withFile "example.log" WriteMode $ \h -> runEff $ runHandleLogger h $ runLoggerAbove Info $ do programThe idea is that we have a single effect
Loggerwhich accepts multiple interpretations, for example, we might have a logger that does not log anything (runNoLogger) or a logger which writes to a file (runHandleLogger).Now, I would like to have a "handler" that works like a Decorator, that is, the "handler" requires another "handler" that actually does the work, but before (or after) this "inner" handle takes control the Decorator performs some kind of (pre/post)processing. In this case, I would like to have a decorator
runLoggerAbovethat when theseverityis some value it delegates the work to "inner", otherwise it does nothing (return ()).The code above does not compile with the following error:
My
.cabalfile looks like this:My environment:
Beta Was this translation helpful? Give feedback.
All reactions