How to write a Haskell Pipes sum function?

I am trying to learn the pipes package by writing my own sum function, and I am getting dumb. I would not want to use the utility functions from Pipes.Prelude (since it has sum and fold and other functions that make it trivial) and use only the information as described in Pipes.Tutorial . The tutorial does not talk about Proxy constructors, but if I look at sum and fold in the source, it uses these constructors, and I wonder if it is possible to write my sum function without knowing these low-level details.

I am having problems with how this function can continue to take values ​​as long as there are available values, and then somehow return this amount to the user. I assume the type will be:

 sum' :: Monad m => Consumer Int m Int 

It seems to me that this may work, because this function can consume values ​​until there are no more, and then returns the final amount. I would use it as follows:

 mysum <- runEffect $ inputs >-> sum' 

However, the function from Pipes.Prelude has the following signature instead:

 sum :: (Monad m, Num a) => Producer am () -> ma 

So, I think this is my first hurdle. Why does the sum function accept Producer as an argument and not use >-> to connect?


FYI I received the following after a response from danidiaz:

 sum' = go 0 where go np = next p >>= \x -> case x of Left _ -> return n Right (_, p') -> go (n + 1) p' 
+8
pipe haskell
source share
1 answer

Consumers are actually quite limited in what they can do. They cannot detect the end of the input ( pipes-parse uses a different method for this) and when any other part of the pipeline stops (for example, Producer upstream), this part is the one that should provide the result value for the pipeline. Thus, the amount in the returned value of Consumer will not work at all.

Some alternatives:

  • Deploy a function that is directly related to Producer internals, or possibly uses a helper function, such as next . There are adapters of this type that can transmit Producer data to β€œsmarter” consumers, for example the Fold from foldl .

  • Continue to use Consumer , but instead of putting the amount in the Consumer return value, use WriterT as the base monad with the Sum Int monoid as the battery. Thus, even if Producer stops first, you can still run the author to get to the battery. This solution is likely to be less effective.

Sample code for the WriterT approach:

 import Data.Monoid import Control.Monad import Control.Monad.Trans.Writer import Pipes producer :: Monad m => Producer Int m () producer = mapM_ yield [1..10] summator :: Monad n => Consumer Int (WriterT (Sum Int) n) () summator = forever $ await >>= lift . tell . Sum main :: IO () main = do Sum r <- execWriterT . runEffect $ producer >-> summator print r 
+5
source share

All Articles