Hi Chris,<br><br>Thanks a bunch for the new angle.<br><br>Question &amp; comments:<br><br>* I like the simplicity of using a single TVar whose state reflects the not-computed/computed state of the IVal.<br><br>* I also like the public interface of taking an STM argument (newTIVal(IO)) over returning a sink (newEmptyTIVal(IO)), which came from some non-STM thinking.&nbsp; In fact, maybe &#39;cached&#39; is a better public interface yet.&nbsp; I&#39;m going to try it out, renaming &quot;cached&quot; to &quot;ival&quot;.&nbsp; (Oh yeah, I&#39;m shortening &quot;TIVal&quot; to &quot;IVal&quot;.)<br>
<br>* Why tryPutTMVar in place of putTMVar?&nbsp; Perhaps to encourage checking that var hasn&#39;t been written?<br><br>* A perhaps prettier version of force:<br><br>&nbsp;&nbsp;&nbsp; force (TIVal tv) = readTVar tv &gt;&gt;= either compute return<br>
&nbsp;&nbsp;&nbsp;&nbsp; where<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; compute wait = do a &lt;- wait<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;writeTVar tv (Right a)<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return a<br><br>* The Applicative STM instance can be simplified:<br><br>&nbsp;&nbsp;&nbsp; instance Applicative STM where { pure = return; (&lt;*&gt;) = ap }<br>
<br>Cheers,&nbsp; - Conal<br><br><div class="gmail_quote">On Mon, Apr 28, 2008 at 7:40 AM, ChrisK &lt;<a href="mailto:haskell@list.mightyreason.com">haskell@list.mightyreason.com</a>&gt; wrote:<br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
The garbage collector never gets to collect either the action used to populate the cached value, or the private TMVar used to hold the cached value.<br>
<br>
A better type for TIVal is given below. &nbsp;It is a newtype of a TVal. &nbsp;The contents are either a delayed computation or the previously forced value.<br>
<br>
Thew newTIVal(IO) functions immediately specify the delayed action.<br>
<br>
The newEmptyTIVal(IO) functions create a private TMVar that allows the delayed action to be specified once later. &nbsp;Note the use of tryPutTMVar to return a Bool instead of failing, in the event that the user tries to store more that one action.<br>

<br>
When force is called, the previous action (and any private TMVar) are forgotten. &nbsp;The garbage collector might then be free to collect them.<br>
<br>
-- <br>
Chris<br>
<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
-- By Chris Kuklewicz (April 2008), public domain<br>
module TIVal(TIVal,newTIVal,newTIValIO,force,cached) where<br>
<br>
import Control.Applicative(Applicative(..))<br>
import Control.Concurrent.STM(STM,TVar,newTVar,newTVarIO,readTVar,writeTVar<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ,TMVar,newEmptyTMVar,newEmptyTMVarIO,tryPutTMVar,readTMVar)<br>
import Control.Monad(Monad(..),join,liftM2)<br>
import System.IO.Unsafe(unsafePerformIO)<br>
<br>
newtype TIVal a = TIVal (TVar (Either (STM a) a))<br>
<br>
-- the non-empty versions take a computation to delay<br>
<br>
newTIVal :: STM a -&gt; STM (TIVal a)<br>
newTIVal = fmap TIVal . newTVar . Left<br>
<br>
newTIValIO :: STM a -&gt; IO (TIVal a)<br>
newTIValIO = fmap TIVal . newTVarIO . Left<br>
<br>
-- The empty versions stage things with a TMVar, note the use of join<br>
-- Plain values &#39;a&#39; can be stored with (return a)<br>
<br>
newEmptyTIVal :: STM ( TIVal a, STM a -&gt; STM Bool)<br>
newEmptyTIVal = do<br>
 &nbsp;private &lt;- newEmptyTMVar<br>
 &nbsp;tv &lt;- newTVar (Left (join $ readTMVar private))<br>
 &nbsp;return (TIVal tv, tryPutTMVar private)<br>
<br>
newEmptyTIValIO :: IO ( TIVal a, STM a -&gt; STM Bool )<br>
newEmptyTIValIO = do<br>
 &nbsp;private &lt;- newEmptyTMVarIO<br>
 &nbsp;tv &lt;- newTVarIO (Left (join $ readTMVar private))<br>
 &nbsp;return (TIVal tv, tryPutTMVar private)<br>
<br>
-- force will clearly let go of the computation (and any private TMVar)<div class="Ih2E3d"><br>
<br>
force :: TIVal a -&gt; STM a<br></div>
force (TIVal tv) = do<br>
 &nbsp;v &lt;- readTVar tv<br>
 &nbsp;case v of<br>
 &nbsp; &nbsp;Right a -&gt; return a<br>
 &nbsp; &nbsp;Left wait -&gt; do a &lt;- wait<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;writeTVar tv (Right a)<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return a<br>
<br>
-- Conal&#39;s &quot;cached&quot; function. This is actually safe.<div class="Ih2E3d"><br>
<br>
cached :: STM a -&gt; TIVal a<br></div>
cached = unsafePerformIO . newTIValIO<br>
<br>
-- The instances<div class="Ih2E3d"><br>
<br>
instance Functor TIVal where<br>
 &nbsp;f `fmap` tiv = cached (f `fmap` force tiv)<br>
<br>
instance Applicative TIVal where<br>
 &nbsp;pure x &nbsp; &nbsp; &nbsp;= cached (pure x)<br>
 &nbsp;ivf &lt;*&gt; ivx = cached (force ivf &lt;*&gt; force ivx)<br>
<br>
instance Monad TIVal where<br>
 &nbsp;return x &nbsp;= cached (return x)<br>
 &nbsp;tiv &gt;&gt;= k = cached (force tiv &gt;&gt;= force . k)<br>
<br></div>
instance Applicative STM where<br>
 &nbsp;pure x = return x<br>
 &nbsp;ivf &lt;*&gt; ivx = liftM2 ($) ivf ivx <br>
</blockquote>
</blockquote></div><br>