<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">I think Ben's eagerlyBlackhole is what I called noDup. And it is indeed a bit tricky to use correctly. It needs the same sort of care around inlining that unsafePerformIO does. A bit of summary:</p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; "> </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">1. It's okay to duplicate a runST thunk or to enter it twice, because each copy will have its own set of references and arrays. This is important, because we have absolutely no control over what the user will do with such a thunk.</p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; "> </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2. With the exception of a runST thunk, we must never duplicate or double-enter a thunk if it performs or suspends ST work.</p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; "> </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Based on my tests and the intuition I've developed about what's going on here, (2) breaks down into two pieces:<br /></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2a. Any time we perform or suspend ST work, we must use NOINLINE to avoid duplication.</p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; "> </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2b. Any time we suspend ST work, we must set up the thunk involved with noDuplicate# or similar.</p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; "> </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">For example, the code I wrote yesterday for the Applicative instance looks like this:</p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; "> </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> fm <*> xm = ST $ \ s -></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> let</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> {-# NOINLINE res1 #-}</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> !res1 = unST fm s</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> !(f, s') = res1</p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; "> </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> {-# NOINLINE res2 #-}</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> res2 = noDup (unST xm s')</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> (x, s'') = res2</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> in (f x, s'')</p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; "> </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">I NOINLINE res1. If it were to inline (could that happen?), we'd get</p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; "> </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> let</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> res2 = noDup (unST xm (snd (unST fm s)))</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> (x, s'') = res2</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> in (fst (unST fm s) x, s')</p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; "> </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">and that would run the fm computation twice. But I don't noDup res1, because we force it immediately on creation; no one else ever handles it.</p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; "> </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">I NOINLINE res2 for a similar reason, but I also use noDup on it. The res2 thunk escapes into the wild via x and s'' in the result; we need to make sure that it is not entered twice.</p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; "> </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">I believe can use a few rewrite rules to reduce costs substantially in some situations. I will add those to the differential. </p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; "> </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000000;">-------- Original message --------</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000000;">From: Simon Marlow <marlowsd@gmail.com> </span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000000;">Date: 1/31/17 3:59 AM (GMT-05:00) </span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000000;">To: Simon Peyton Jones <simonpj@microsoft.com> </span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000000;">Cc: David Feuer <david@well-typed.com>, ghc-devs@haskell.org </span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000000;">Subject: Re: Lazy ST vs concurrency </span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000000;"><br /></span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">On 30 January 2017 at 22:56, Simon Peyton Jones <<a href="mailto:simonpj@microsoft.com"><span style=" text-decoration: underline; color:#2980b9;">simonpj@microsoft.com</span></a>> wrote:<br /></p>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;">We don’t want to do this on a per-module basis do we, as -fatomic-eager-blackholing would suggest. Rather, on per-thunk basis, no? Which thunks, precisely? I think perhaps </span><span style=" font-family:'calibri,sans-serif'; font-size:0.92em; font-weight:600;">precisely thunks one of whose free variables has type (Sttate# s) for some s.</span><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;"> These are thunks that consume a state token, and must do so no more than once.</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If we could identify exactly the thunks we wanted to be atomic, then yes, that would be better than a whole-module solution. However I'm not sure how to do that - doing it on the basis of a free variable with State# type doesn't work if the State# is buried in a data structure or a function closure, for instance.</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;">If entering such thunks was atomic, could we kill off noDuplicate#?</span></p>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;"> </span></p>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;">I still don’t understand exactly what noDuplicate# does, what problem it solves, and how the problem it solves relates to this LazyST problem.</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Back in our "Haskell on a Shared Memory Multiprocessor" paper (<a href="http://simonmar.github.io/bib/papers/multiproc.pdf"><span style=" text-decoration: underline; color:#2980b9;">http://simonmar.github.io/bib/papers/multiproc.pdf</span></a>) we described a scheme to try to avoid duplication of work when multiple cores evaluate the same thunk. This is normally applied lazily, because it involves walking the stack and atomically black-holing thunks pointed to by update frames. The noDuplicate# primop just invokes the stack walk immediately; the idea is to try to prevent multiple threads from evaluating a thunk containing unsafePerformIO.</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">It's expensive. It's also not foolproof, because if you already happened to create two copies of the unsafePerformIO thunk then noDuplicate# can't help. I've never really liked it for these reasons, but I don't know a better way. We have unsafeDupablePerformIO that doesn't call noDuplicate#, and the programmer can use when the unsafePerformIO can safely be executed multiple times.</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;"> </span></p>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;">We need some kind of fix for 8.2. Simon what do you suggest?</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">David's current fix would be OK (along with a clear notice in the release notes etc. to note that the implementation got slower). I think -fatomic-eager-blackholing might "fix" it with less overhead, though.</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Ben's suggestion:</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; -qt-user-state:0;">> eagerlyBlackhole :: a -> a</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">is likely to be unreliable I think. We lack the control in the source language to tie it to a particular thunk.</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Cheers</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Simon</p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;"> </span></p>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;">Simon</span></p>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="m_-7754832595177235694__MailEndCompose"></a><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;"> </span> </p>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'calibri,sans-serif'; font-size:0.92em; font-weight:600;">From:</span><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;"> Simon Marlow [mailto:</span><a href="mailto:marlowsd@gmail.com"><span style=" font-family:'calibri,sans-serif'; font-size:0.92em; text-decoration: underline; color:#2980b9;">marlowsd@gmail.com</span></a><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;">] <br /></span><span style=" font-family:'calibri,sans-serif'; font-size:0.92em; font-weight:600;">Sent:</span><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;"> 30 January 2017 21:51<br /></span><span style=" font-family:'calibri,sans-serif'; font-size:0.92em; font-weight:600;">To:</span><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;"> David Feuer <</span><a href="mailto:david@well-typed.com"><span style=" font-family:'calibri,sans-serif'; font-size:0.92em; text-decoration: underline; color:#2980b9;">david@well-typed.com</span></a><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;">><br /></span><span style=" font-family:'calibri,sans-serif'; font-size:0.92em; font-weight:600;">Cc:</span><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;"> Simon Peyton Jones <</span><a href="mailto:simonpj@microsoft.com"><span style=" font-family:'calibri,sans-serif'; font-size:0.92em; text-decoration: underline; color:#2980b9;">simonpj@microsoft.com</span></a><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;">>; </span><a href="mailto:ghc-devs@haskell.org"><span style=" font-family:'calibri,sans-serif'; font-size:0.92em; text-decoration: underline; color:#2980b9;">ghc-devs@haskell.org</span></a><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;"><br /></span><span style=" font-family:'calibri,sans-serif'; font-size:0.92em; font-weight:600;">Subject:</span><span style=" font-family:'calibri,sans-serif'; font-size:0.92em;"> Re: Lazy ST vs concurrency</span></p>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">On 30 January 2017 at 16:18, David Feuer <<a href="mailto:david@well-typed.com"><span style=" text-decoration: underline; color:#2980b9;">david@well-typed.com</span></a>> wrote:</p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">I forgot to CC ghc-devs the first time, so here's another copy.</p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br />I was working on #11760 this weekend, which has to do with concurrency<br />breaking lazy ST. I came up with what I thought was a pretty decent solution (<br /><a href="https://phabricator.haskell.org/D3038"><span style=" text-decoration: underline; color:#2980b9;">https://phabricator.haskell.org/D3038</span></a> ). Simon Peyton Jones, however, is quite<br />unhappy about the idea of sticking this weird unsafePerformIO-like code<br />(noDup, which I originally implemented as (unsafePerformIO . evaluate), but<br />which he finds ugly regardless of the details) into fmap and (>>=). He's also<br />concerned that the noDuplicate# applications will kill performance in the<br />multi-threaded case, and suggests he would rather leave lazy ST broken, or<br />even remove it altogether, than use a fix that will make it slow sometimes,<br />particularly since there haven't been a lot of reports of problems in the<br />wild.</p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">In a nutshell, I think we have to fix this despite the cost - the implementation is incorrect and unsafe.</p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Unfortunately the mechanisms we have right now to fix it aren't ideal - noDuplicate# is a bigger hammer than we need. All we really need is some way to make a thunk atomic, it would require some special entry code to the thunk which did atomic eager-blackholing. Hmm, now that I think about it, perhaps we could just have a flag, -fatomic-eager-blackholing. We already do this for CAFs, incidentally. The idea is to compare-and-swap the blackhole info pointer into the thunk, and if we didn't win the race, just re-enter the thunk (which is now a blackhole). We already have the cmpxchg MachOp, so It shouldn't be more than a few lines in the code generator to implement it. It would be too expensive to do by default, but doing it just for Control.Monad.ST.Lazy should be ok and would fix the unsafety.</p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">(I haven't really thought this through, just an idea off the top of my head, so there could well be something I'm overlooking here...)</p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Cheers</p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Simon</p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">My view is that leaving it broken, even if it only causes trouble<br />occasionally, is simply not an option. If users can't rely on it to always<br />give correct answers, then it's effectively useless. And for the sake of<br />backwards compatibility, I think it's a lot better to keep it around, even if<br />it runs slowly multithreaded, than to remove it altogether.<br /><br />Note to Simon PJ: Yes, it's ugly to stick that noDup in there. But lazy ST has<br />always been a bit of deep magic. You can't *really* carry a moment of time<br />around in your pocket and make its history happen only if necessary. We can<br />make it work in GHC because its execution model is entirely based around graph<br />reduction, so evaluation is capable of driving execution. Whereas lazy IO is<br />extremely tricky because it causes effects observable in the real world, lazy<br />ST is only *moderately* tricky, causing effects that we have to make sure<br />don't lead to weird interactions between threads. I don't think it's terribly<br />surprising that it needs to do a few more weird things to work properly.<br /><br />David</p>
<p style=" margin-top:12px; margin-bottom:0px; margin-left:10px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> </p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html>