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?
The conclusion would be that dynamic allocation of these kinds of references is unnecessary, as the language being pure already solves for the wanted behavior.
If it’s unnecessary then what is the alternative way that I can instantiate the Logger argument to writeUserData to get the behaviour I want, in haskells-missing-mutable-ref?
OK, so are we agreed, then, that if IOScopedRef existed then I could instantiate the Logger argument to writeUserData to get the behaviour I desire? And that with only the primitives provided by Haskell as it currently stands, the Logger argument to writeUserData cannot be instantiated in a way that gives me the behaviour that I desire. Instead Logger or writeUserData or both would need to be changed?
Sorry, what feature? Do you mean I shouldn’t use IOScopedRef (if it existed) to get the behaviour I desire, or IOScopedRef shouldn’t be implemented? In either case, what do you object to?
With “references”, the libraries providing Logger and writeUserData need not know anything about the existence of “references”. Only the caller need use them.
Not exactly, my point is that tacking dynamic allocations onto this feature is at best unnecessary and at worst would pollute the language with a second way to do something it already excels at.
A static reference table is the “missing” thing in the language as I see it.
I don’t think this has anything to do with whether the references are static or dynamic, you can wrap the library either way.
I don’t understand. What is “this feature”? Your “contextual”?
OK, fine, and if that missing thing is added, I’ll add the API that I want on top. What’s wrong with that?
I agree. But you said “With references the library would declare an implicit configuration” (my emphasis). I’m pointing out it’s not the library that declares the implicit configuration, it’s the caller.
Yes, worker threads carrying tables of references with table size defined at compilation time.
Would this make for a good point of abstraction though? I’d expect a given library to need several internal values be adjustable, not some interface type like Logger.
Consider something simpler with no extra inputs whatsoever, like adjustable numeric precision or getArgs.
Ah, well now it’s a question of API design, an area where I suspect, based on this comment, we will disagree, but not really the point of the original article. Happy to continue in a fresh thread if you create one. Thanks for the discussion so far!