
Andrew Coppin wrote:
So I want some sort of sequencing primitives. Sequencing generally suggests a monad. something = do { action1; delay 10; action2}
I had a go at writing what I thought the interface might look like. (Fortunately, I made no attempt to *implement* it - otherwise I would doubtless have wasted huge amounts of time implementing something that isn't designed right yet!) Unfortunately Haskell doesn't really provide a way to write an "interface", and then write the implementation behind it seperately somewhere else. So the "code" I wrote wasn't actually compilable at all, but it was useful to sketch something out. When I do this I generally write functions like foo = error "foo: Not implemented yet"
My initial idea was that I could have some kind of monad for controlling adding and removing stuff. The monad could provide an "add" action that adds a visual object to the frame and returns a unique ID. Then you could have a "remove" action that removes the specified ID again. And a "wait" action that makes the display stay the same for so many seconds. (But the visual objects may internally be animated.) I'd suggest that each object has its own action to animate it. You will need to write a custom monad to interleave actions. See http://www.cs.chalmers.se/~koen/pubs/jfp99-monad.ps for something along the right lines. Then I hit upon the idea that maybe one thread of control could "spawn" a second one - so that for example one thread could generate a bunch of snowflakes raining down the screen while a seperate thread rotates a geometric figure in the center. Or something. Sounds right. Of course, these "threads" have no need (or use) for actually running concurrently - they are only "concurrent" in the sence that they both affect the same frame, rather than their actions happening one after another on consecutive frames.
Next I got to thinking that maybe these threads of control might need to communicate for synchronisation. E.g., when a rotating line reaches 90° with another line, a signal is sent to another thread, which then adds another visual element or stops the animation or something. The parent thread *could* algebraicly _compute_ what time this will happen, but sending a signal is much simpler. (E.g., if you change the speed of an animation, the threads still stay synchronised without you having to remember to adjust parameters in your calculations all over the place...) Yup. I did exactly this, albeit for a very different application. Unfortunately the code belongs to my employer so I can't post it. But if you look at the paper above and also read about the "ContT" monad you will get the right idea. Its a bit mind-bending, but you suspend a thread by getting its continuation (using callCC) and stuffing it into whatever data structure is being used to hold pending threads (e.g. a semaphore queue).
Or you could use the existing concurrent threads mechanism, which is kludgier but less work.
There's still one little problem though. The "threads of control" are for sequencing stuff. They are inherantly discrete; *add* this thing, *remove* this other thing, *send* this signal, *wait* to receive a signal, etc. But something like, say, rotating a line, is inherantly continuous. So there's a discrete system for sequencing stuff - which I seem to have worked out fairly well - and there also needs to be a continuous system for doing all the things with are smooth functions of time. Thats where Reactive stuff comes in.
So maybe the continuous stuff should just be a type alias to a regular Haskell function? Ah, but wait... I said I might want to send a signal when an animation reaches a specific stage, right? So these "functions" need to do more than just map time to some other variable; they need to be able to send signals. And hey, actually, what are the chances of a time sample exactly lining up with the instant that the notable event occurs? How do I want to handle that? Events are part of reactive frameworks.
Paul.