Separation of data loading / unloading and processing logic

Sometimes it may be necessary to perform some complex procedures to extract or save the data that is being processed. In this case, you need to separate the logic of data generation and data processing. A common way is to use iterative functions. There are many decent libraries: pipes, conduits, etc. In most cases, they will do this. But AFAIK they (except, perhaps, pipes) are limited by the processing order.

But consider an example of viewing a magazine: a person may want to run back and forth randomly. It can also zoom in and out. I am afraid that iterations will not help here.

A simple solution might 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 -> fa -> m (fa) move' dim dir f = grow' dim dir f >>= shrink' dim (not dir) liftF' :: (Origin fa -> b) -> fa -> b class Frame f => MFrame f where liftMF' :: (Origin fa -> (b, Origin fa)) -> fa -> (b, fa) -- 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 _) = fm grow' () True (LF lm (h:r)) = return $ LF l (m++[h]) r ... 

Then you can wrap it in StateT and so on. So the questions are:

0) I missed the goal iteration completely and are they applicable here?

1) I just invented the famous wheel?

2) Obviously, the operations of growing and shrinking are rather inefficient, since their complexity is proportional to the size of the frame. Is there a better way to expand zippers like this?

+7
haskell haskell-pipes
source share
1 answer

You need lenses, in particular the sequenceOf function. Here is an example of a target load of 3 tuples:

  sequenceOf _2 :: (IO a, IO b, IO c) -> IO (IO a, b, IO c) 

sequenceOf transfers the lens to a polymorphic field that contains the load action, triggers the action, and then replaces the field with the result of the action. You can use sequenceOf for your own types by simply making your type polymorphic in the fields you want to load, for example:

 data Asset ab = Asset { _art :: a , _sound :: b } 

... and also for creating your lenses using four parameters (this is one of the reasons for their existence):

 art :: Lens (Asset a1 b) (Asset a2 b) a1 a2 art k (Asset xy) = fmap (\x' -> Asset x' y) (kx) sound :: Lens (Asset a b1) (Asset a b2) b1 b2 sound k (Asset xy) = fmap (\y' -> Asset x y') (ky) 

... or you can automatically generate lenses using makeLenses , and they will be fairly general.

Then you can simply write:

 sequenceOf art :: Asset (IO Art) b -> IO (Asset Art b) 

... and loading multiple assets is as simple as drawing up Kleisli arrows ::

 sequenceOf art >=> sequenceOf sound :: Asset (IO Art) (IO Sound) -> IO (Asset Art Sound) 

... and, of course, you can invest assets and create lenses for access to invested resources, and it still "just works."

Now you have a pure Asset type that you can process using pure functions, and all the loading logic is taken into account in lenses.

I wrote this on my phone, so there may be some errors, but I will fix them later.

+5
source share

All Articles