Add Control.Monad.ST.Linear and implement IO.Linear in terms of it#496
Add Control.Monad.ST.Linear and implement IO.Linear in terms of it#496shlevy wants to merge 1 commit into
Conversation
| -- general projections of data types, which take an unrestricted argument. | ||
| unIO :: IO a %1 -> State# RealWorld %1 -> (# State# RealWorld, a #) | ||
| unIO (IO action) = action | ||
| {- Pattern synonyms do not support linear fields (GHC #18806) |
There was a problem hiding this comment.
This PR is a draft because of this. Options, assuming this PR is otherwise wanted:
- Accept the breaking change
- Keep IO a newtype (but derive instances and relevant functions via coercing from ST still)
- Fix GHC and wait
There was a problem hiding this comment.
Sorry I didn't come back to you earlier. I think that in regard of the discussion with @treeowl below and the fact that a linear ST monad is always going to be pretty niche, I say let's not break compatibility here.
It doesn't matter that IO is an instance of ST. And there's a potential future where they aren't. Let's honor that by IO and ST both be independent newtypes.
| -- There are potential difficulties coming from the fact that usage differs: | ||
| -- returned value in 'Control.Monad.ST' can be used unrestrictedly, which is not | ||
| -- typically possible of linear 'ST'. This means that 'Control.Monad.ST' action are | ||
| -- not actually mere translations of linear 'ST' action. Still I [aspiwack] |
There was a problem hiding this comment.
@aspiwack You said this about IO, I assume you believe it of ST as well?
There was a problem hiding this comment.
Yes, I don't see why ST or IO would make a difference here.
|
Hi @shlevy , I'll look at this more next week. In the meantime, can I ask you what use-case you imagine for a linear ST monad? It's been my assumption, so far, that a linear ST isn't particularly useful because ST is mostly about shared mutations, and linear types tend to preclude sharing and already support mutation in non-monadic code. |
|
In addition to the fact that |
|
The main motivation is for pure mocks that are backed by ST and involve cleanup (e.g. a mock of a filesystem where you want to be able to “close” the file). |
|
So you’d write code polymorphic over |
|
But why use |
I didn't know about that. Do you have some documentation on the issue? |
Not quite off the top of my head. GHC has gotten more conservative to make m >>= const _|_ = _|_However, in m >>= const _|_ ≥ _|_In particular, Another example: For any GHC needs special cases to deal with this aspect of |
So I can use STRefs to mock out the state. |
No description provided.