foo, bar :: Stream (Of Char) [] Char
foo = lift ['a','b'] >>= lift . (: [])
bar = lift (['a','b'] >>= (: []))
By the monad transformer laws, foo = bar, but in fact destroy foo P.show P.show P.show /= destroy bar P.show P.show P.show. I blame destroy. Indeed, Streaming.Internal.destroyExposed carefully documents the dangers and necessary restrictions, but destroy is (presumably accidentally) implemented using identical code:
destroy
:: (Functor f, Monad m) =>
Stream f m r -> (f b -> b) -> (m b -> b) -> (r -> b) -> b
destroy stream0 construct effect done = loop stream0 where
loop stream = case stream of
Return r -> done r
Effect m -> effect (liftM loop m)
Step fs -> construct (fmap loop fs)
destroyExposed stream0 construct effect done = loop stream0 where
loop stream = case stream of
Return r -> done r
Effect m -> effect (liftM loop m)
Step fs -> construct (fmap loop fs)
The most obvious fix is to simply define
destroy = destroyExposed . unexposed
but there may be a slightly more efficient way.
By the monad transformer laws,
foo = bar, but in factdestroy foo P.show P.show P.show /= destroy bar P.show P.show P.show. I blamedestroy. Indeed,Streaming.Internal.destroyExposedcarefully documents the dangers and necessary restrictions, butdestroyis (presumably accidentally) implemented using identical code:The most obvious fix is to simply define
but there may be a slightly more efficient way.