
Henning Thielemann wrote:
Heinrich Apfelmus wrote:
Could you expand a little on your arrow-like stream processors? What do the arrows look like,
data SF a b = SF (a -> (b, SF a b))
?
My stream processors are not Arrows, because 'first' cannot be implemented. However, 'arr' and '.' can be implemented.
[..]
For example, an arpeggiator stream processor consists of two functions: 1. Receive key up/down events and keep track of the currently pressed keys. 2. Receive a MIDI controller that controls the tempo of the arpeggiator. The stream processor sets the alarm according the current tempo and at every alarm event it sends a single key chosen from the set of the currently pressed keys.
This example shows, that a stream processor cannot support Arrow.first, that is extending (arrow a b) to (arrow (a,c) (b,c)), since for an alarm event, we have no input of type c that could be passed through.
Ok, that sounds nasty.
And of course, I am particularly interested in the nasty examples that you came up with. :)
For example I want to write a Guitar simulator: You press a set of keys together and the processor converts this into successive tones on a guitar.
Technically I like to do it this way: When a key is pressed, collect all key press events in the following 10ms. After this period emit all pressed keys according to a certain pattern. When one of the pressed keys is released, then send key-press(!) events for all currently pressed keys according to another pattern. Repeat this cycle. Manage somehow the keys that are pressed after the first key-down-collecting phase and the keys that are released during this initial phase. Ignore them or do something more sensible about them, but make sure that in the output all key-down events are eventually matched with a key-up event and that for the same key you never send two successive key-down events or two successive key-up events. That is, for the same key, key-up and key-down events must alternate. An exception might be if you receive bogus input. But even then, the number of key-up and key-down events for one note in the output shall match, whenever this is true for the input.
The guitar simulator is a great example! And much to my surprise, it's already possible to implement (parts of) it in reactive-banana 0.4.1! At least in the real-time version, where we have access to timers from wxHaskell. In particular, have a look at the new Wave.hs example, to be found at the bottom of http://haskell.org/haskellwiki/Reactive-banana/Examples It generates a wave-like pattern whenever you click on of the buttons (strum a chord). When you click two buttons in rapid succession (think left = key-down, right = key-up), the second wave will not start after the first one has completed. (I think this example also makes it clear that it's already to possible to implement the arpeggiator as you described with timers.) Of course, the code is not particularly high-level; after all, I am essentially duplicating event streams as [(Duration,a)]. Then again, we cannot describe each wave as Event a because we need a notion of "this event stream has finished" in order to be able to stall the second wave. In other words, the vanilla Event a is potentially infinite, there is no way to test whether it has "finished", so *some* other notion is needed anyway. Question: how would you actually like to describe the guitar simulator at a high-level? Did you already wish for some specific combinators? Assume that you had something like reactive-banana available and imagine that there were a benevolent djinn granting you three new primitive combinators of your choice. Best regards, Heinrich Apfelmus -- http://apfelmus.nfshost.com