Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>If you just want code, it's this:</p> <pre><code>procFile' iFile oFile = fileDriver (joinI $ enumLinesBS &gt;&lt;&gt; mapChunks (map rstrip) $ I.mapM_ (B.appendFile oFile)) iFile </code></pre> <p>Commentary:</p> <p>This is a three-stage process: first you transform the raw stream into a stream of lines, then you apply your function to convert that stream of lines, and finally you consume the stream. Since <code>rstrip</code> is in the middle stage, it will be creating a stream transformer (Enumeratee).</p> <p>You can use either <code>mapChunks</code> or <code>convStream</code>, but <code>mapChunks</code> is simpler. The difference is that <code>mapChunks</code> doesn't allow for you to cross chunk boundaries, whereas <code>convStream</code> is more general. I prefer <code>convStream</code> because it doesn't expose any of the underlying implementation, but if <code>mapChunks</code> is sufficient the resulting code is usually shorter.</p> <pre><code>rstripE :: Monad m =&gt; Enumeratee [ByteString] [ByteString] m a rstripE = mapChunks (map rstrip) </code></pre> <p>Note the extra <code>map</code> in <code>rstripE</code>. The outer stream (which is the input to rstrip) has type <code>[ByteString]</code>, so we need to map <code>rstrip</code> onto it.</p> <p>For comparison, this is what it would look like if implemented with convStream:</p> <pre><code>rstripE' :: Enumeratee [ByteString] [ByteString] m a rstripE' = convStream $ do mLine &lt;- I.peek maybe (return B.empty) (\line -&gt; I.drop 1 &gt;&gt; return (rstrip line)) mLine </code></pre> <p>This is longer, and it's less efficient because it will only apply the rstrip function to one line at a time, even though more lines may be available. It's possible to work on all of the currently available chunk, which is closer to the <code>mapChunks</code> version:</p> <pre><code>rstripE'2 :: Enumeratee [ByteString] [ByteString] m a rstripE'2 = convStream (liftM (map rstrip) getChunk) </code></pre> <p>Anyway, with the stripping enumeratee available, it's easily composed with the <code>enumLinesBS</code> enumeratee:</p> <pre><code>enumStripLines :: Monad m =&gt; Enumeratee ByteString [ByteString] m a enumStripLines = enumLinesBS &gt;&lt;&gt; rstripE </code></pre> <p>The composition operator <code>&gt;&lt;&gt;</code> follows the same order as the arrow operator <code>&gt;&gt;&gt;</code>. <code>enumLinesBS</code> splits the stream into lines, then <code>rstripE</code> strips them. Now you just need to add a consumer (which is a normal iteratee), and you're done:</p> <pre><code>writer :: FilePath -&gt; Iteratee [ByteString] IO () writer fp = I.mapM_ (B.appendFile fp) processFile iFile oFile = enumFile defaultBufSize iFile (joinI $ enumStripLines $ writer oFile) &gt;&gt;= run </code></pre> <p>The <code>fileDriver</code> functions are shortcuts for simply enumerating over a file and running the resulting iteratee (unfortunately the argument order is switched from enumFile):</p> <pre><code>procFile2 iFile oFile = fileDriver (joinI $ enumStripLines $ writer oFile) iFile </code></pre> <p>Addendum: here's a situation where you would need the extra power of convStream. Suppose you want to concatenate every 2 lines into one. You can't use <code>mapChunks</code>. Consider when the chunk is a singleton element, <code>[bytestring]</code>. <code>mapChunks</code> doesn't provide any way to access the next chunk, so there's nothing else to concatenate with this. With <code>convStream</code> however, it's simple:</p> <pre><code>concatPairs = convStream $ do line1 &lt;- I.head line2 &lt;- I.head return $ line1 `B.append` line2 </code></pre> <p>this looks even nicer in applicative style,</p> <pre><code>convStream $ B.append &lt;$&gt; I.head &lt;*&gt; I.head </code></pre> <p>You can think of <code>convStream</code> as continually consuming a portion of the stream with the provided iteratee, then sending the transformed version to the inner consumer. Sometimes even this isn't general enough, since the same iteratee is called at each step. In that case, you can use <code>unfoldConvStream</code> to pass state between successive iterations.</p> <p><code>convStream</code> and <code>unfoldConvStream</code> also allow for monadic actions, since the stream processing iteratee is a monad transformer.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload