turion / rhine

Haskell Functional Reactive Programming framework with type-level clocks
http://hackage.haskell.org/package/rhine
117 stars 21 forks source link

StdinClock throws IOError on EOF #286

Closed turion closed 1 month ago

turion commented 5 months ago

Right now when someone cats a file into a program using StdinClock, it will throw an error on EOF. This is unfortunate because it means that all state is lost and we can't e.g. compute statistics over the file's content and return them purely. (You'd have to use a workaround with an IORef.) But also interactively this is cumbersome because there is no standard way to quit an interactive loop built with this clock, the programmer has to remember to insert one into the handling of the input. There are several possible solutions:

  1. Change StdinClock's monad to a type MonadIO io => ExceptT EOF io where EOF is a singleton (but could later be extended to handle other errors if it makes sense). The clock then has to catch the IO exception and turn it into a pure exception. Glaring disadvantage: The whole Rhine is automatically terminated on EOF, and you either need to keep an outer State monad or do clock erasure to recover any statistics you might have computed over the input.
  2. Like 1., but instead of changing the existing clock, write a new one, StdinClockExcept. Advantage wrt 1.: No code breakage, simpler option stays available. Disadvantage: Code bloat, unclear user choice.
  3. Change StdinClock's Tag type to Maybe Text or similar, where Nothing encodes "this is EOF, no further ticks will occur". Advantage wrt 1.: Simpler monad, the state of the ClSF under this clock is not automatically lost. Disadvantage: Seems ad hoc, user can "miss" EOF and be surprised that the clock doesn't go on ticking.

Also, it's a bit unclear how to implement the "no further ticks" feature from 3.. In IO, one might do go = threadDelay maxBound >> go to block the thread of the clock. This could even be a general utility. Also, wrapping a clock in ExceptT to handle exceptions might be a useful general utility.

turion commented 5 months ago
  1. also has the disadvantage that simply flow $ ... @@ StdinClock will hang. It only makes sense in a multi rate context.
turion commented 1 month ago

The recommended way to deal with this is ExceptClock which was introduced in https://github.com/turion/rhine/pull/287.