{-# LANGUAGE RankNTypes #-} -- | Examples using "Pipes.Cliff". You will want to look at the -- source code itself; viewing just the Haddocks will not help you -- much. You can view the source using Haddock if you used -- @--hyperlink-source@ when building the library or if you are -- viewing this on Hackage; look for the @Source@ link. Or, you can -- find the source at -- -- -- -- __Be sure to use the @-threaded@ option__ when compiling code -- that uses "Pipes.Cliff", including this code; see the warning in -- "Pipes.Cliff" for more details. -- -- Notice throughout how pipelines that move data from one process to -- another typically are run in the background using 'conveyor', which -- spawns a thread. You have to make sure all these threads are -- running concurrently so that data flows through your pipeline (a -- shell does this sort of thing for you.) module Pipes.Cliff.Examples where import Pipes.Cliff import qualified Pipes.Prelude as P import qualified Data.ByteString.Char8 as BS8 -- | Produces a stream of 'BS8.ByteString', where each -- 'BS8.ByteString' is a shown integer. This is an infinite stream. -- In the examples below we'll send this infinite stream off into a -- Unix pipeline, a feat that would be very difficult and clumsy -- without a framework like @pipes@. produceNumbers :: Monad m => Producer BS8.ByteString m r produceNumbers = iterate' succ (0 :: Int) >-> P.show >-> P.map BS8.pack >-> P.map (`BS8.snoc` '\n') where iterate' nxt i = yield i >> iterate' nxt (nxt i) -- | Streams an infinite list of numbers to @less@. Shows off how -- you can use "Pipes.Cliff" even for non-finite 'Producer's. Don't -- try to go to the end of the input in @less@, though. When you -- quit @less@, you will get broken pipe warnings printed to -- standard error. This is normal. To suppress them, see the -- 'handler' option. numsToLess :: IO ExitCode numsToLess = do (toLess, _) <- pipeInput Inherit Inherit (procSpec "less" []) safeEffect $ produceNumbers >-> toLess -- | Streams an infinite list of numbers to @tr@ and then to @less@. -- Perfectly useless, but shows how to build pipelines. Also -- squlches warning messages using the 'handler' option. -- -- Note that, consistent with usual @pipes@ usage, the value of -- @code1@ and @code2@ is not necessarily the last exit code in the -- pipeline. Rather, it is the exit code of the process that -- terminated first. Use 'waitForProcess' if you need to determine -- the exit value of a particular process. It's also possible to use -- a bit of 'fmap' to see which process in a pipeline did terminate -- first; for an example of that, search the "Pipes.Tutorial" module -- for @echo3.hs@. alphaNumbers :: IO (ExitCode, ExitCode) alphaNumbers = do ((toTr, fromTr), _) <- pipeInputOutput Inherit (procSpec "tr" ["[0-9]", "[a-z]"]) { handler = squelch } (toLess, _) <- pipeInput Inherit Inherit (procSpec "less" []) { handler = squelch } toTrAsync <- conveyor $ produceNumbers >-> toTr toLessAsync <- conveyor $ fromTr >-> toLess code1 <- wait toTrAsync code2 <- wait toLessAsync return (code1, code2) -- | Produces an infinite stream of numbers, sends it to @tr@ for some -- mangling, and then to @sh@, which will copy each line both to -- standard output and to standard error. From @sh@, standard output -- is then sent off to @less@, and standard error is sent to a -- separate thread which will collect the results and return them. -- -- This example shows you how to write a pipeline that deals with -- both standard output and standard error. -- -- It's also interesting to note here that because of the buffering -- that happens in various places throughout the pipeline, and because -- less itself also buffers its input, the output you will see from -- the @sh@ process's standard error will be much longer than the -- output the user actually viewed in @less@. standardOutputAndError :: IO BS8.ByteString standardOutputAndError = do ((toTr, fromTr), _) <- pipeInputOutput Inherit (procSpec "tr" ["[0-9]", "[a-z]"]) ((toSh, fromShOut, fromShErr), _) <- pipeInputOutputError (procSpec "sh" ["-c", script]) (toLess, _) <- pipeInput Inherit Inherit (procSpec "less" []) _ <- conveyor $ produceNumbers >-> toTr _ <- conveyor $ fromTr >-> toSh _ <- conveyor $ fromShOut >-> toLess runSafeT $ P.fold BS8.append BS8.empty id (fromShErr >> return ()) where script = "while read line; do echo $line; echo $line 1>&2; done" -- | Like 'alphaNumbers' but just sends a limited number -- of numbers to @cat@. A useful test to make sure that pipelines -- shut down automatically. limitedAlphaNumbers :: IO ExitCode limitedAlphaNumbers = do ((toTr, fromTr), _) <- pipeInputOutput Inherit (procSpec "tr" ["[0-9]", "[a-z]"]) (toCat, _) <- pipeInput Inherit Inherit (procSpec "cat" []) _ <- async . safeEffect $ produceNumbers >-> P.take 300 >-> (toTr >> return ()) safeEffect $ fromTr >-> toCat -- | Produces a finite list of numbers, sends it to @tr@ for some -- mangling, and then puts the results into a 'BS8.ByteString' for -- further processing. This example shows how you can use this -- library to place the results of a pipeline into a simple strict -- data type. alphaNumbersByteString :: IO BS8.ByteString alphaNumbersByteString = do ((toTr, fromTr), _) <- pipeInputOutput Inherit (procSpec "tr" ["[0-9]", "[a-z]"]) _ <- conveyor $ produceNumbers >-> P.take 300 >-> (toTr >> return ()) runSafeT $ P.fold BS8.append BS8.empty id (fromTr >> return ()) -- | So far, all examples have ignored the issue of exception safety. -- Here's an example that properly uses 'bracket' to make sure that -- all resource allocations are cleaned up if there is an exception. -- Otherwise, it's identical to 'standardOutputAndError'. You can put -- some @do@ notation sugar in here and eliminate all the hanging -- lambdas and '$'s by using the 'ContT' monad from @transformers@ (I -- did not write the example that way to avoid incurring a direct -- dependency on @transformers@). standardOutputAndErrorBracketed :: IO BS8.ByteString standardOutputAndErrorBracketed = withProcess (pipeInputOutput Inherit (procSpec "tr" ["[0-9]", "[a-z]"])) $ \(toTr, fromTr) -> withProcess (pipeInputOutputError (procSpec "sh" ["-c", script])) $ \(toSh, fromShOut, fromShErr) -> withProcess (pipeInput Inherit Inherit (procSpec "less" [])) $ \toLess -> withConveyor (produceNumbers >-> toTr) $ withConveyor (fromTr >-> toSh) $ withConveyor (fromShOut >-> toLess) $ runSafeT $ P.fold BS8.append BS8.empty id (fromShErr >> return ()) where script = "while read line; do echo $line; echo $line 1>&2; done"