I was trying to figure this out and noticed I got a pretty good 
optimization and also a unification of code if I started from this 
unpleasantly typed function:


    evertMaster
      :: (Monad m, Monad m1) =>
         (Stream (Of a) (Stream ((->) (Feed a)) m) () -> Stream ((->) (Feed 
a')) m1 (Of b ()))
         -> FoldM m1 a' b
    evertMaster consumer = FoldM step begin done
        where
        begin = return (consumer cat)
        step str i = case str of       
          Return _ -> error stoppedBeforeEOF
          Step f   -> return (f (Input i))
          Effect m -> m >>= \str' -> step str' i
        done str = do
          e <- inspect str
          case e of
            Left _ -> error stoppedBeforeEOF
            Right f -> do
              e <- inspect (f EOF)
              case e of
                Left (a :> ()) -> return a
                Right _ -> error continuedAfterEOF 
        cat :: Monad m => Stream (Of a) (Stream ((->)(Feed a)) m) ()        
        cat = do
          r <- Effect (Step (Return . Return))
          case r of
              Input a -> Step (a :> cat_ )
              EOF     -> Return ()  

    evert_ :: Eversible a b -> Fold a b
    evert_ (Eversible psi) = Foldl.simplify (evertMaster psi)
    evertM_ :: Monad m => EversibleM m a b -> FoldM m a b
    evertM_ (EversibleM psi) = evertMaster psi
    evertMIO_ :: MonadIO m => EversibleMIO m a b -> FoldM m a b
    evertMIO_ (EversibleMIO psi) = evertMaster psi


This uses the constructors directly for the internal `step` function, which 
is repeatedly applied, and the internal `cat` stream, which is repeatedly 
deconstructed. Using the constructors directly is black magic, so some 
gruesome mistake could emerge on cases more complicated than the trivial 
one I was testing. It was about 10 times as fast for this case but less of 
a win in the pure `Fold a b` case

        main = print =<< L.foldM (evertM_ (EversibleM S.sum)) 
[1..1000000::Int]
    -- main = print =<< L.foldM (evertM (EversibleM S.sum)) 
[1..1000000::Int]

The more prudent implementation would use `inspect` (~ `runFreeT`) in 
`step`, pattern matching on the Either, like in `done` above. I think that 
was still distinctly faster.  Similarly, the 'correct' `cat` / 
`evertedStreamM` would be like so

    cat :: Monad m => Stream (Of a) (Stream ((->)(Feed a)) m) ()
    cat = do
        r <- lift (yields id)
        case r of
            Input a -> do
                yield a
                cat
            EOF -> return ()


The idea of using `evertedStreamM` like this is pretty amazing. In the 
`Streaming.Prelude` module there is the dubious `store` function 

      store :: Monad m => (Stream (Of a) (Stream (Of a) m) r -> t) -> 
Stream (Of a) m r -> t

      store f str = f (copy str)



This is meant to be used at types like 


        

      S.store    :: Monad m => EvertibleM m a b -> Stream (Of a) m r -> Stream 
(Of a) m (Of b r)

      S.store    :: Monad m => Evertible a b -> Stream (Of a) m r -> Stream 
(Of a) m (Of b r)

(taking `Evertible` and company as rank-2 type synonyms, not newtypes). 
 The idea was to permit  you to apply more than one eliminating operation to 
the same stream of items. You apply a fold and still have your stream! Of 
course if the eliminations of the stream that you envisage are with 
`Control.Foldl.Fold(M)`s there is no point in since we already know how to 
do apply them together, but (as I was thinking) not every elimination 
reduces to a `Fold` or `FoldM`  The way it uses the `copy` function 

     S.copy :: Monad m =>  Stream (Of a) m r -> Stream (Of a) (Stream (Of 
a) m) r

is some sort of dual to the way `evert` uses `evertedStream`  I couldn't 
figure out a simple way of dealing with the differences parallel to `evert` 
`evertM` `evertMIO`, so basically
I just ended up exporting the equivalent of my `evertMaster` above with 
some comments on use. I'm not sure there would be any harm by the way in 
exporting three functions corresponding to `evert(M(IO))` that just take 
the rank two functions directly. I wasn't having trouble with the 'raw'

    everted_ :: (forall m r. Monad m => Stream (Of a) m r -> m (Of x r)) -> 
Fold a x
    everted_ phi = evert_ (Eversible phi)

    evertedM_ :: Monad m => (forall t r. (MonadTrans t, Monad (t m)) => 
Stream (Of a) (t m) r -> t m (Of x r)) -> FoldM m a x
    evertedM_ phi = evertM_ (EversibleM phi)

    evertedMIO_ :: MonadIO m => (forall t r. (MonadTrans t, MonadIO (t m)) 
=> Stream (Of a) (t m) r -> t m (Of x r)) 
                 -> FoldM m a x
    evertedMIO_ phi = evertMIO_ (EversibleMIO phi) 

Then treat the wrapped versions as making complicated cases easier to get 
past the compiler: you separate out the task of  `Eversible` so that it 
type checks and so on.  I think edwardk does this sometimes, using both a 
direct rank-2 version and a wrapped version. 

Sorry, this is a bit of a mess, I still haven't got to figuring out the 
`Transvertible` bit!

-- 
You received this message because you are subscribed to the Google Groups 
"Haskell Pipes" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to haskell-pipes+unsubscr...@googlegroups.com.
To post to this group, send email to haskell-pipes@googlegroups.com.

Reply via email to