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?