Sometimes it is necessary to perform some complex routines in order to retrieve or save data, which is being processed. In this case one wants to separate data generation and data processing logic. The common way is to use iteratee-like functionality. There are lots of decent libraries: pipes, conduit, etc. In most cases they will do the thing. But AFAIK they are (except, maybe, pipes) limited by the order of processing.
But consider a log viewer example: human may desire to ramble back and forth randomly. He also may zoom in and out. I fear iteratees can't help here.
A straightforward solution may look like this:
-- True is for 'right', 'up', etc. and vice versa
type Direction = Bool
class Frame (f :: * -> *) where
type Dimension f :: *
type Origin f :: * -> *
grow', shrink' move' :: Monad m => Dimension f -> Direction -> f a -> m (f a)
move' dim dir f = grow' dim dir f >>= shrink' dim (not dir)
liftF' :: (Origin f a -> b) -> f a -> b
class Frame f => MFrame f where
liftMF' :: (Origin f a -> (b, Origin f a)) -> f a -> (b, f a)
-- Example instance: infinite stream.
data LF a = LF [a] [a] [a]
instance Frame LF where
type Dimension LF = () -- We have only one dimension to move in...
type Origin LF = [] -- User see piece of stream as a plain list
liftF' f (LF _ m _) = f m
grow' () True (LF l m (h:r)) = return $ LF l (m++[h]) r
...
Then one may wrap this into StateT and so on. So, the questions:
0) Did I miss the point of iteratees completely, and they are applicable here?
1) Did I just reinvent a well-known wheel?
2) It is obvious, that grow and shrink operations are pretty uneffective, as their complexity is proportional to the frame size. Is there a better way to extend zippers like this?