co-log icon indicating copy to clipboard operation
co-log copied to clipboard

Convenience function for discarding logs

Open qwbarch opened this issue 4 years ago • 4 comments

Related to https://github.com/co-log/co-log/discussions/224.

I'm not sure if discarding logs is a common enough use-case to warrant this, but I'd like to propose a convenience function:

discardLogs :: LoggerT Message Identity a -> a
discardLogs = (.) runIdentity $ usingLoggerT (mempty :: LogAction Identity Message)

Body of the function is thanks to @chshersh's answer in the discussion. I couldn't figure it out on my own, I appreciate the help! If this proposal is welcome, I'd be happy to open a pull request for it as well. I assume this would belong in the Colog.Pure module

qwbarch avatar Nov 22 '21 16:11 qwbarch

I'm not sure about adding more functions that work with LoggerT. I wouldn't want to invest in this interface more. With time, it became clear that LoggerT brings more problems than it actually solves. The recommended way to use LogAction in the environment is to have your own ReaderT-like monad. See the following comment for more context:

  • https://github.com/co-log/co-log/issues/134#issuecomment-500290507

In case when you have only WithLog constraint for your functions, I would encourage passing LogAction explicitly. This will simplify the code significantly. And if you have some additional IO besides WithLog, such discardLogs function won't work.

Moreover, the proposed discardLogs function doesn't really work if LoggerT is not on the end of your transformer stack.

So, ideally, I'd like to remove LoggerT eventually and that's why I don't want to encourage it. But I don't have a nice migration story at the moment, so LoggerT stays in the library.

However, it's always great to improve documentation 🙂 So if you want to add this function and a usage example to LoggerT or usingLoggerT functions, such contribution would be more than welcome 👍🏻

chshersh avatar Nov 23 '21 13:11 chshersh

And if you have some additional IO besides WithLog, such discardLogs function won't work.

I forgot about this scenario. My use-case is that I have pure functions where I wanted to add logging for extra information. I ended up making monadic functions with only a WithLog constraint, and wanted to keep a completely "pure" version if one wanted to ignore logging (similar to zeroM and zero in #224). Would it make more sense to change the name from discardLogs to something else?

Moreover, the proposed discardLogs function doesn't really work if LoggerT is not on the end of your transformer stack.

I completely overlooked this as well since my use-case only had a WithLog constraint, whoops! Here's what I tried changing the type signature to:

discardLogs :: WithLog r msg m => m a -> a

But this no longer compiles. Here's the error:

Couldn't match type 'm' with 'LoggerT Message Identity'.

I assume this is due to m representing any monad, whereas usingLoggerT expects a LoggerT. How would I be able to avoid LoggerT in my type signature then? I feel like I'm missing something fundamental.

However, it's always great to improve documentation slightly_smiling_face So if you want to add this function and a usage example to LoggerT or usingLoggerT functions, such contribution would be more than welcome 👍🏻

I'd be happy to add it, as well as documentation for it! I hope you don't mind my questions. I'd like to try and move off of LoggerT for reasons you mentioned, but I'm yet again not adequate enough to figure it out on my own 😅

qwbarch avatar Nov 24 '21 02:11 qwbarch

The more I think about it, the more the above signature WithLogs r msg m => m a -> a wouldn't make sense anyways. If one wants to use IO while discarding logs, it indeed makes more sense to provide a LogAction that does nothing.

The reason I wanted a function like this is, as I mentioned before, I had pure functions where I wanted to add logging to it. There's no IO or anything else, so the signature LoggerT Message Identity a -> a makes the most sense in my head. Please let me know if there's something better I could do here!

qwbarch avatar Nov 24 '21 03:11 qwbarch

@qwbarch As I mentioned before, if you only use WithLog constraint, it could be better to just pass LogAction explicitly like this:

zeroM :: Monad m => LogAction m Message -> m Int

And then you'll be able to pass mempty to this function and use runIdentity to unwrap the monad.

However, I see two problems with this suggestion:

  • You don't have HasCallStack propagated automatically to other functions
  • You can't use functions like logInfo because they expect the WithLog constraint

There was a similar discussion in another place:

  • https://github.com/co-log/co-log-polysemy/issues/2

I'm afraid, I don't have a good answer or suggestion to these problems here. Need to think more about more convenient interface 🤔

chshersh avatar Nov 24 '21 13:11 chshersh