Haskell's missing mutable reference type

I still must be missing something, or not explaining myself well. foo is not expected to access x if x is not in scope in the body of foo. Passing a key is exactly what is expected. I don’t understand why that’s not an answer.

I don’t understand what that means.

I’m definitely explaining myself badly then. I am certainly suggesting nothing of the sort.

I believe you are proposing this interface to “contextual variables” which you believe is safe and implementable:

v :: Contextual t
contextual v = e :: t

getContextual :: Contextual t -> IO t

overriding :: Contextual t -> (t -> t) -> IO r -> IO r

If so, then I can implement what I want as follows

type IOScopedRef a = Vault.Key a

vault :: Contextual Vault.Vault
contextual vault = Vault.empty

withIOScopedRef :: a -> (IOScopedRef a -> IO r) -> IO r
withIOScopedRef a body = do
  key <- Vault.newKey
  overriding vault (Vault.insert key a) $ do
    body key

readIOScopedRef :: IOScopedRef a -> IO a
readIOScopedRef key =
  fmap (fromJust . Vault.lookup key) getContextual vault

modifyIOScopedRef ::
  (a -> a) -> IOScopedRef a -> IO r -> IO r
modifyIOScopedRef f key body =
  overriding vault (Vault.adjust f) key body

If your “contextual” API exists then my IOScopedRef can be layered on top. Seems straightforward. And it’s roughly what I explain at A reference implementation of IOScopedRef. What remains to be resolved?