Haskell's missing mutable reference type
OK, I think I probably understand what you mean. You mean that if you’re passing the logger around everywhere you may as well replace all instances of
modifySeverity logger f $ do
...
with
logger <- modifySeverity logger f
...
and so you’re asking for an example where you can’t do that transformation. Sure. First, notice that you can only do that transformation if you can see the call to modifySeverity. So the answer is that you can’t do that transformation anywhere that modifySeverity is being used in a way that the caller can’t see. For example, suppose I have a function from another library with this fype
libFun ::
(String -> IO ()) ->
(forall r. Int -> IO r -> IO r) ->
IO T
Then if I have a logger :: Logger implemented with an IOScopedRef I can call libFun like
libFun
(\s -> logMsg logger 0 s)
(\i body -> modifySeverity (const i) body)
There’s no way I can do that with lexical scoping. libFun doesn’t know that modifySeverity might be used within it! I’m guessing that’s what you meant by “having a computation that already somehow captures the IOScopedRef”?
prophet:
Everything that (indirectly) uses this
IOScopedRefneeds to go through theloggervariable anyway so you might as well use it to carry the state in a lexically scoped way.
Yeah, I think didn’t explain well what I meant. There are two subtly different questions:
- Can you get something with the same API as
Loggerbut without usingIOScopedRef? (i.e.modifySeveritytakes an explicit body)
I think the answer is “no” and I suspect you don’t disagree with me, but if not then please do say because that would be very interesting!
- Can you get something with a different API to
Loggerthat does the same job in some cases? (i.e.modifySeveritydoesn’t take a body, rather the modification is observable in the continuation)
The answer is “yes”, as you demonstrate above.
The difference is important because only something with the same API (i.e. modifySeverity takes a body) can be used with libFun.
Your original question was
Do you have an example where this is useful to have?
My claim is that Logger is an API that is unachievable without IOScopedRef. That is, my loggerExample cannot be written as written without IOScopedRef. Your original point was: but it can be written in a different way without IOScopedRef. That’s a fair point. I can say “the API itself is useful” but you could always come back and ask why it’s more useful than the lexical scoping way. In which case, the response would be that the lexical scoping way is not sufficient to integrate with libFun. (Then someone might say "well libFun should be different, which may be so, but I’d like to know how.) I’ll see if I can think of a way of making this clear in the article.
Discussion in the ATmosphere