save/restore of STRef state

I was using STRefs the other day and I ran across a case where I really wanted the ability to save the state of all my references, execute some action, and then restore the state later if needed. I didn't find anything that does this on hackage so I implemented a small wrapper around STRefs which does what I want. Before I put it on hackage I thought I'd run it by haskell-cafe. It works as follows: test :: (String, Bool) test = runContext $ do a <- newRef "foo" b <- newRef False restore <- save writeRef a "bar" writeRef b True when someCondition restore x <- readRef a y <- readRef b return (x, y) If someCondition, the existing state is restored and test returns ("foo", False) otherwise nothing special happens and test returns ("bar", True) Also each reference has a unique key that can make it much easier to convert structures that use STRefs to pure stuctures. Reads, writes and creating refs are still constant time operations (with only a very very small overhead) and garbage collecting behavior should be the same as with regular STRefs. My implementation is here: http://gist.github.com/316738 What do you think? Any suggestions? Does anything like this already exist in hackage? Does this seem useful to other people besides me? :) Any glaring purity issues that I overlooked? Thanks for your input, - Job

Job Vranish wrote:
I was using STRefs the other day and I ran across a case where I really wanted the ability to save the state of all my references, execute some action, and then restore the state later if needed. I didn't find anything that does this on hackage so I implemented a small wrapper around STRefs which does what I want.
If you use something like the State or Reader monad, it becomes trivial to temporarily modify the carried state. But maybe something like this is occasionally useful. (In particular, it seems to allow you to restore to a point not necessarily matching the most recent save.)
What do you think? Any suggestions?
Deriving the Eq instance for ContextRef means that it will compare the key *and* the IORef. Which gives the right answer, but seems rather redundant. Comparing the key alone should be sufficient.
Any glaring purity issues that I overlooked?
Why an IORef? Why not an STRef? Then you won't need unsafeIOToST. (And since the type system forces a ContextRef to exist in only one state thread, worrying about thread isolation with atomicModifyIORef seems unecessary.) Using a state monad with a mutable structure as the state looks highly dubious. (The whole point of a state monad is, after all, to avoid needing to mutate stuff.) I can see 2 calls to "get", but none to "put". I would suggest you either use a reader monad with mutable state, or a state monad with immutable state. One or the other. (Personally, I'd go for the latter.) I'm also not 100% sure how the saving and restoring part works. Map Int (IO (IO ())) sounds fruity though.

On Sat, Feb 27, 2010 at 11:53 AM, Andrew Coppin wrote: If you use something like the State or Reader monad, it becomes trivial to
temporarily modify the carried state. But maybe something like this is
occasionally useful. (In particular, it seems to allow you to restore to a
point not necessarily matching the most recent save.) Yeah, in cases where you only need "references" to values of the same type,
then a Map in a state or Reader works really well.
But in my case I need to reference values of several different types, which
would make things messy in a state monad, and saving/restoring even messier.
I'm also using MonadFix quite a bit and a Map in a State monad was a lot
harder to make lazy (in my case, sometimes it's not to bad). Deriving the Eq instance for ContextRef means that it will compare the key
*and* the IORef. Which gives the right answer, but seems rather redundant.
Comparing the key alone should be sufficient. Agree, will fix.
Why an IORef? Why not an STRef? Then you won't need unsafeIOToST. (And since the type system forces a ContextRef to exist in only one state thread,
worrying about thread isolation with atomicModifyIORef seems unecessary.) I use the IORefs because I wanted to use mkWeakIORef (maybe mkWeak would
work just as well?) and atomicModifyIORef. The thread isolation is needed
because of the the finalizers that clean out the map when the references get
GC'd.
Although, it _is_ kinda ugly. I'm thinking I might switch back STRefs and
just use unsafeCoerce *flinch* when I want to use atomicModifyIORef. (IORef
is just a newtype around STRef) Using a state monad with a mutable structure as the state looks highly
dubious. (The whole point of a state monad is, after all, to avoid needing
to mutate stuff.) I can see 2 calls to "get", but none to "put". I would
suggest you either use a reader monad with mutable state, or a state monad
with immutable state. One or the other. (Personally, I'd go for the latter.) Yeah, I'll switch to Reader, but the state needs to be mutable so that the
finalizers can get to it. I'm also not 100% sure how the saving and restoring part works. Map Int (IO
(IO ())) sounds fruity though. _______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe Thanks for the feedback :)
- Job

Job Vranish wrote:
On Sat, Feb 27, 2010 at 11:53 AM, Andrew Coppin
mailto:andrewcoppin@btinternet.com> wrote: Why an IORef? Why not an STRef? Then you won't need unsafeIOToST. (And since the type system forces a ContextRef to exist in only one state thread, worrying about thread isolation with atomicModifyIORef seems unecessary.)
I use the IORefs because I wanted to use mkWeakIORef (maybe mkWeak would work just as well?) and atomicModifyIORef. The thread isolation is needed because of the the finalizers that clean out the map when the references get GC'd.
Hmm, I see. (I can't actually figure out what the map does, so I can't really comment further about that. You'd think there's a way around this though...)
Using a state monad with a mutable structure as the state looks highly dubious. (The whole point of a state monad is, after all, to avoid needing to mutate stuff.) I can see 2 calls to "get", but none to "put". I would suggest you either use a reader monad with mutable state, or a state monad with immutable state. One or the other. (Personally, I'd go for the latter.)
Yeah, I'll switch to Reader, but the state needs to be mutable so that the finalizers can get to it.
Ah, I see. That at least makes sense...
participants (2)
-
Andrew Coppin
-
Job Vranish