Writing a Simple WriterP Handset Battery

Using the channel library, I want to write a program to read data from some source and copy it monoidally (say, using Sum ). The easiest way to do this is

  import Control.Proxy as import Data.Monoid (Sum) main = do let source = enumFromToS (0::Int) 5 a <- runWriterT $ runProxy $ source >-> foldD Sum print a 

Of course, although this works for small sources, large inputs will lead to a scary stack overflow due to the lazy nature of the WriterT battery.

Fortunately, pipes seem to be expecting this, providing the WriterP proxy with a strict battery. Unfortunately, the documentation surrounding this proxy is pretty scarce. After some conversation (and simplifying the problem, to instead accumulate 1 for each descending element), I came to this program,

 import Control.Proxy import Control.Proxy.Trans.Writer import Data.Monoid (Sum) main = do let source = enumFromToS (0::Int) 5 a <- runProxy $ runWriterK $ source >-> \x->tell (Sum 1::Sum Int) print a 

Of course, this program does not even perform the simplified task correctly, since it accumulates to 1 instead of 6. If I am not mistaken, this behavior is explained by the fact that the pipe only reads one element before terminating. To repeat to the end of the input, I came up with the following:

 import Control.Proxy import Control.Proxy.Trans.Writer import Data.Monoid (Sum) main = do let source = enumFromToS (0::Int) 5 a <- runProxy $ runWriterK $ source >-> fold Sum print a fold :: (Monad m, Proxy p, Monoid w) => (a -> w) -> a' -> WriterP wpa' aa' amr fold f = go where go x = do a <- request x tell $ fa x' <- respond a go x' 

This code, however, returns battery 0. Why is this? Is there a function like my fold in pipes ?

Given that many use cases for pipes are lengthy processes that work with large datasets, does it not make sense for folds in Control.Proxy.Prelude be built around strict WriterP instead of WriterT ? Currently, it seems that the proxy transformers in pipes are second-class citizens, are present, but do not have a large number of combinators that make WriterT so convenient.

+4
source share
2 answers

I am adding a new answer because I fixed this problem in pipes-3.3 , which I just uploaded to Hackage. The theory behind the pipes shows that the global behavior you expected was the right behavior, and WriterP now behaves globally, so you can stack within the pipe.

I modified your example to show that you are implementing it with pipes-3.3 :

 import Control.Proxy import Control.Proxy.Trans.Writer main = do let source = enumFromToS (0::Int) 5 a <- runProxy $ execWriterK $ source >-> sumD print a 

Now you can also get the crease results in the conveyor. For example, this is perfectly true:

 chunksOf :: (Monad m, Proxy p) => Int -> () -> Pipe pa [a] mr chunksOf n () = runIdentityP $ forever $ do -- The unitU discards the values that 'toListD' reforwards as <- execWriterK (takeB_ n >-> toListD >-> unitU) () respond as 

Here's a usage example:

 >>> runProxy $ getLineS >-> takeWhileD (/= "quit") >-> chunksOf 3 >-> printD 1<Enter> 2<Enter> 3<Enter> ["1","2","3"] 4<Enter> 5<Enter> 6<Enter> ["4","5","6"] quit 

Sorry for the wrong answer for the first time!

+6
source

Remember that proxy transformers behave locally, while the base monad behaves globally. This means that WriterP has each proxy server that supports its own drive, and the proxy server that shuts down first determines which battery is being returned. The reason the battery returned 0 is because your enumeration pipe returned first and did not accumulate anything.

The complexity of WriterP is strict only because I control this type (and not the one that is in transformers ). I never imagined that it would be a strict alternative to lazy folds in the proxy foreplay. The correct strict alternative to use is foldlD' .

Note that foldlD' mappend is basically a strict Writer fold, especially if you run the base State monad using mempty as the initial state.

In your case, you can make it even easier by using:

 main = do let source = enumFromToS (0::Int) 5 a <- (`runStateT` 0) $ runProxy $ source >-> foldlD' (+) print a 

This will reset the input strictly.

+4
source

All Articles