{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE PatternSynonyms #-} -- | A streaming library build around the 'Jet' type, which behaves as a kind of \"effectful list\". -- -- For example, here's a way to print the first ten lines of a file to @stdout@: -- -- >>> action = J.jet @Line (File "foo.txt") & J.limit 10 & J.sink stdout -- -- The code is using the 'J.jet' function to create a 'Jet' of 'Line' values -- (read using the default system encoding). 'J.jet' is part of the -- 'J.JetSource' helper typeclass. Meanwhile, 'J.sink' is part of the -- complementary 'J.JetSink' typeclass. -- -- Note also the use of '(&)', which is simply a flipped '($)'. I've found it -- useful to define forward-chained pipelines. -- -- If instead of printing to @stdout@ we wanted to store the lines in a list: -- -- >>> action = J.jet @Line (File "foo.txt") & J.limit 10 & J.toList -- -- Imagine we wanted to print the combined lines of two files, excepting the -- first 10 lines of each: -- -- >>> :{ -- action = -- do file <- J.each [File "foo.txt", File "bar.txt"] -- jet @Line file & J.drop 10 -- & J.sink stdout -- :} -- -- Here we are making use of the 'Monad' instance of 'Jet', which resembles -- that of conventional lists. We are mixing monadic do-blocks and conventional -- function application. Also we use 'J.each', a function which creates a 'Jet' -- out of any 'Foldable' container. -- -- 'Jet's are 'Monoid's too, so we could have written: -- -- >>> action = [File "foo.txt", File "bar.txt"] & foldMap (J.drop 10 . J.jet @Line) & J.sink stdout -- -- Here's an interesting use of 'sink'. Imagine we have a big utf8-encoded file -- and we want to split it into a number of files of no more than 100000 bytes -- each, with the extra condition that we don't want to split any line between -- two files. We could do it like this: -- -- >>> :{ -- action = -- let buckets = BoundedSize 100000 . File . ("result.txt." ++) . show <$> [1..] -- in jet (File "12999.txt.utf-8") -- & J.decodeUtf8 -- & J.lines -- <&> (\line -> J.lineToUtf8 line <> J.textToUtf8 J.newline) -- & J.sink buckets -- :} -- -- In this example we aren't using the default system encoding: instead of -- that, we are reading bytes, explicity decoding them with 'J.decodeUtf8' and -- finding 'J.lines'. Then we create a 'ByteBundle' for each 'Line' to signify -- that it shouldn't be broken, and end by writing to a sequence of -- 'BoundedSize' 'File's. -- module Jet ( -- * The Jet type Jet, J.run, J.consume, J.drain, -- J.flatMap, -- * Building Jets J.each, J.repeat, J.repeatIO, J.replicate, J.replicateIO, J.iterate, J.iterateIO, J.unfold, J.unfoldIO, J.untilEOF, J.untilNothing, -- * List-like functions -- $listlike J.toList, J.length, J.traverse, J.traverse_, J.for, J.for_, J.filter, J.filterIO, J.take, J.limit, J.takeWhile, J.takeWhileIO, J.drop, J.dropWhile, J.dropWhileIO, J.mapAccum, J.mapAccumIO, J.intersperse, -- * Zips -- $zips J.zip, J.zipWith, J.zipIO, J.zipWithIO, -- * Control operations -- $control J.withFile, J.bracket, J.bracket_, J.bracketOnError, J.finally, J.onException, -- ** Building your own -- $doityourself J.control, J.unsafeCoerceControl, J.control_, J.unsafeCoerceControl_, -- * Folding Jets -- $folding J.fold, J.foldIO, -- * Byte utils J.bytes, J.ChunkSize (..), J.ByteBundle, J.bundle, J.bundleLength, J.bundleBytes, -- * Text and line utils J.decodeUtf8, J.encodeUtf8, J.Line (Line), J.lines, J.unlines, J.newline, J.lineToText, J.lineToUtf8, J.textToLine, J.textToUtf8, J.stringToLine, J.lineContains, J.lineBeginsWith, J.prefixLine, -- * Concurrency traverseConcurrently, PoolConf, defaults, inputQueueSize, numberOfWorkers, outputQueueSize, -- * Process invocation throughProcess, linesThroughProcess, utf8LinesThroughProcess, ProcConf, bufferStdin, readFromStderr, handleExitCode, -- * Conversion helpers J.JetSource (..), J.JetSink (..), J.Sink (..), J.File (..), J.BoundedSize (..), J.BucketOverflow (..), -- * Some complicated stuff -- $complicated recast, Splitter (..), MealyIO(..), SplitStepResult(..), bytesOverBuckets, byteBundlesOverBuckets, Combiners, combiners, withCombiners, withCombiners_, combineIntoLists, -- * Re-exports -- $pipelines (&), (<&>), -- $standardstreams stdin, stdout, stderr, -- $exceptions T.UnicodeException, -- $process proc, shell, ) where import Data.Text.Encoding.Error qualified as T import System.IO (stdin, stdout, stderr) import System.Process import Jet.Internal import Jet.Internal qualified as J import Data.Function ((&)) import Data.Functor ((<&>)) -- $setup -- -- >>> :set -XTypeApplications -- >>> :set -XImportQualifiedPost -- >>> :set -XScopedTypeVariables -- >>> :set -XLambdaCase -- >>> :set -XNumDecimals -- >>> import Jet (Jet, (&)) -- >>> import Jet qualified as J -- >>> import Control.Foldl qualified as L -- >>> import Control.Concurrent -- >>> import Data.IORef -- >>> import Data.Text qualified as T -- $zips -- -- It's not possible to zip two 'Jet's together. But 'Jet's can be zipped with -- pure lists, or with lists of 'IO' actions. -- -- -- $complicated -- -- I didn't manage to make this stuff simpler. -- -- $pipelines -- I've found that the 'Data.Function.&' (reverse application) and 'Data.Functor.<&>' (reverse 'fmap') -- operators feel quite natural for building pipelines. -- $standardstreams -- The standard streams, useful with functions like 'sink'. -- -- $exceptions -- Thrown when decoding UTF8. -- -- $process -- Functions that create process specs for use with 'throughProcess'. For more control, import the whole of "System.Process". -- -- $folding These functions can be used directly, but they're also useful for -- interfacing with the @Applicative@ folds from the -- [foldl](https://hackage.haskell.org/package/foldl) library, with the help of -- functions like @Control.Foldl.purely@ and @Control.Foldl.impurely@. -- -- @Applicative@ folds are useful because they let you run multiple -- \"analyses\" of a 'Jet' while going through it only once. -- $doityourself -- These are for advanced usage. -- -- Sometimes we want to lift some existing -- resource-handling operation not already covered, one that works with plain -- 'IO' values. These functions help with that. -- -- They have a linear type to statically forbid -- [\"funny\"](http://blog.ezyang.com/2012/01/monadbasecontrol-is-unsound/) -- operations like @\\x -> x *> x@ that disrupt proper threading of the -- consumer state. -- -- $control -- Some 'Jet's must allocate resources to do its work. For example, opening a -- text file and yielding its lines. These resources must be promptly released -- when the 'Jet' itself finishes or the consumers stops it (for example, by -- using 'limit' on the 'Jet'). They must also be released in the face of -- exceptions. -- -- Here are various control operations like those from "Control.Exception", but -- lifted to work on 'Jet's. -- -- When put in a do-block, these operations \"protect\" every statement in the -- do-block below the operation itself. -- -- $listlike -- -- In these functions, the 'Jet' is working as a kind of \"effectful list\". -- The effects which produce the elements, and the effects with which we -- transform and consume the elements, are always 'IO' effects. -- -- Don't confuse these functions with similarly named functions from -- 'Data.Traversable' or 'Control.Monad', for which 'Jet' doesn't work as the -- \"container\", but as the Applicative/Monadic effect itself. --