
Conal Elliott said:
Can we implement a type 'TIVal a' (preferably without unsafePerformIO) with the following interface:
newIVal :: STM (TIVal a, a -> STM ()) -- or IO (...) force :: TIVal a -> STM a
instance Functor IVal instance Applicative IVal instance Monad IVal
where
* 'newIVal' makes something like an IVar that can be written/defined (just once) with the returned a->STM(). * 'force' gets the value, retrying if not yet defined; once force is able to succeed, it always yields the same value. * 'fmap f tiv' becomes defined (force yields a value instead of retrying) when tiv does. Similarly for (<*>) and join. * Forcing 'fmap f tiv' more than once results in f being called only once, i.e., the result is cached and reused, as in pure values. Similarly for (<*>) and join.
Perhaps what you and Jake are looking for are fully-fledged "triggers" on transactional memory. To solve the fmap problem, have each TIVar backed by a separate TVar. Then the TIVar returned by fmap would act as a cache for the original TIVar. A trigger would watch the TVar which backs the original TIVar, updating the cache TVar when the original TIVar is written. Nested fmaps would work simply as a cascade of triggers. The STM authors considered the possibility of triggers in their paper on invariants [1], but instead took the safer option of read-only invariants. [1]http://research.microsoft.com/~simonpj/papers/stm/stm-invariants.pdf