8000 Derive the MonadTrans instance for WithLogger by derelbenkoenig · Pull Request #70 · freckle/blammo · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Derive the MonadTrans instance for WithLogger #70

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

derelbenkoenig
Copy link

I'm maybe a little surprised this wasn't needed already. In any case it shouldn't be harmful to derive it
(it's truly the same thing as ReaderT modulo newtype wrappers)

@derelbenkoenig derelbenkoenig requested a review from a team as a code owner May 13, 2025 05:37
@derelbenkoenig derelbenkoenig requested review from jason-lieb and removed request for a team May 13, 2025 05:37
Copy link
Member
@pbrisbin pbrisbin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the reason we haven't needed these is that WithLogger is primarily used for DerivingVia. In that context, you are deriving for your own type, which would itself have/need instances like that. WithLogger only needs enough to implement the instance you're deriving through it.

runWithLogger is here though, and can be used directly, which is when more instances would be useful.

If that's a use-case we want to support better, I think I'd give this every reasonable instance that we're already taking on dependencies for: error, state, write, catch, throw, mask, unliftio...

Right?

@derelbenkoenig
Copy link
Author
derelbenkoenig commented May 13, 2025

the case in which I found this instance was needed was in this example
(I think it's because one of the other instances I needed depended on lift in its definition, probably MonadError which was required by the functions from jose)
(this example is not unlikely to be a totally bad way to write this but it was 2 AM)

-- Basic structure for our auth middleware
authMiddleware
  :: HasCallStack
  => Logger
  -> String
  -> (AppContext -> Application)
  -> Application
authMiddleware logger tokenServerUrl app request respond = do
  let maybeBearerToken = bearerTokenFromRequest request
  case maybeBearerToken of
    Nothing -> do
      runWithLogger logger $ logError $ "No bearer token found" :# []
      respond unauthorizedResponse
    Just token -> do
      claims <- runJOSE @JWTError $ runWithLogger logger $ do
        logInfo $ "decoding bearer token" :# ["token" .= decodeUtf8 token]
        jwt <- decodeCompact $ BS.fromStrict token
        logInfo $ "decoded bearer token" :# ["jwt" .= jwt]
        verifyClaims @_ @_ @JWTError -- without the MonadTrans this line gives a compile error
          insecureJWTValidationSettings
          (TokenServerUrl tokenServerUrl)
          jwt
      case claims of
        Left err -> do
          runWithLogger logger
            $ logError
            $ "error with token claims" :# ["error" .= show err]
          respond unauthorizedResponse
        Right claims -> do
          runWithLogger logger
            $ logInfo
            $ "successful token validation" :# ["claims" .= claims]
          app (AppContext logger claims) request respond
 where
  bearerTokenFromRequest :: Wai.Request -> Maybe BS.ByteString
  bearerTokenFromRequest =
    fmap ((!! 1) . BS.split (BS.c2w ' ')) . lookup "Authorization" . requestHeaders

8000

@pbrisbin
Copy link
Member
pbrisbin commented May 13, 2025

Yup, it's because you're using runWithLogger, which makes it WithLogger concretely.

That's fine, but uncommon. Usually, your code would look something like this:

newtype AppM a =  AppM
  { unwrap :: ReaderT AppContext IO a
  }
  deriving newtype
    ( -- ...
    , MonadError
    )
  deriving (MonadLogger, MonadLoggerIO) via (WithLogger IO)

runAppM :: AppContext -> AppM a -> IO a
runAppT = undefined

claims <- runAppT app $ do
  -- ...
  verifyClaims -- this is now AppM, not WithLogger so it can use any of AppM's instances

If lot of things get easier if you introduce and AppM (or AppT that's not concretely IO) like this, so we usually do that, which is why we've never needed these instances on WithLogger before.

@derelbenkoenig
Copy link
Author

I think I can make that work, I was getting kind of confused about how to wrap the entire middleware in runWhateverTransformer because the Application being returned by the Middleware is CPS and I thought I couldn't call the respond continuation in my transformed monad because it explicitly returns IO ResponseRecieved... but I think you actually just do lift $ respond ...? In which case I would still need to have MonadTrans for AppT, right? Or do I only need MonadIO and use liftIO instead of lift?

@pbrisbin
Copy link
Member

I was getting kind of confused about ... but I think you actually just ... ?

Yeah, this stuff is too confusing for me to know without playing with it myself.

In which case I would still need to have MonadTrans for AppT, right?

Correct, but that's the point: you need it (and can just newtype-derive it) on AppT, not WithLogger.

@derelbenkoenig
Copy link
Author
derelbenkoenig commented May 13, 2025

yeah those were the two steps of my reasoning

if I need MonadTrans I can have it on my AppT rather than on WithLogger

, but also,

I might not need MonadTrans at all if I only need MonadIO and liftIO

@derelbenkoenig
Copy link
Author

So sounds like the PR itself is not needed, but the discussion was helpful to me, thanks

@derelbenkoenig derelbenkoenig deleted the robert.benson/WithLogger-MonadTrans branch May 13, 2025 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
0