(It would be nice to have some concurrency in nhc98, of course, but I don't foresee that happening soon.)
I remember implementing cooperative concurrency in Hugs as being rather easy. Of course, that was building on a base which already used a continuation passing IO monad... If you want to try, I could dig out some details but the main issues are: 1) When you have a choice of which thread to wake next, it matters which you wake next. IIRC, waking the wrong one makes it impossible to implement a producer-consumer pattern. 2) There's a lot of interaction with non-deterministic exception handling. 3) unsafePerformIO foo had better not block since we have no way to block the non-monadic code that invoked it. In that case, we raise an exception or give up (don't remember which).
Actually, I'm just wondering whether I can use the GC as a poor-man's scheduler. If a finaliser blocks on an MVar, save its state, keep the finaliser in the pending queue, and return to the main thread. Then on the next GC, try the same finaliser again, ad infinitum until it succeeds.
The finalizer won't become runnable until someone does a putMVar so you can deal with this case by storing the finalizer's state in a queue of blocked threads attached to the MVar and having putMVar do a context switch to the thread at the head of the MVar's queue of blocked threads. If you want finalizers to have priority over other threads, have two queues: one for finalizers, one for normal threads. -- Alastair