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:
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.
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.
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.
Right now when someone
cat
s a file into a program usingStdinClock
, 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 anIORef
.) 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:StdinClock
's monad to a typeMonadIO io => ExceptT EOF io
whereEOF
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 wholeRhine
is automatically terminated onEOF
, and you either need to keep an outerState
monad or do clock erasure to recover any statistics you might have computed over the input.StdinClockExcept
. Advantage wrt 1.: No code breakage, simpler option stays available. Disadvantage: Code bloat, unclear user choice.StdinClock
'sTag
type toMaybe Text
or similar, whereNothing
encodes "this is EOF, no further ticks will occur". Advantage wrt 1.: Simpler monad, the state of theClSF
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 dogo = threadDelay maxBound >> go
to block the thread of the clock. This could even be a general utility. Also, wrapping a clock inExceptT
to handle exceptions might be a useful general utility.