@@ -34,6 +34,9 @@ module Data.Text.Fusion
3434 -- ** Scans
3535 , reverseScanr
3636
37+ -- ** Accumulating maps
38+ , mapAccumL
39+
3740 -- ** Generation and unfolding
3841 , unfoldrN
3942
@@ -199,3 +202,30 @@ findIndex = S.findIndexI
199202countChar :: Char -> Stream Char -> Int
200203countChar = S.countCharI
201204{-# INLINE [0] countChar #-}
205+
206+-- | /O(n)/ Like a combination of 'map' and 'foldl''. Applies a
207+-- function to each element of a 'Text', passing an accumulating
208+-- parameter from left to right, and returns a final 'Text'.
209+mapAccumL :: (a -> Char -> (a,Char)) -> a -> Stream Char -> (a, Text)
210+mapAccumL f z0 (Stream next0 s0 len) = (nz,I.textP na 0 nl)
211+ where
212+ (na,(nz,nl)) = A.run2 (A.unsafeNew mlen >>= \arr -> outer arr mlen z0 s0 0)
213+ where mlen = upperBound 4 len
214+ outer arr top = loop
215+ where
216+ loop !z !s !i =
217+ case next0 s of
218+ Done -> return (arr, (z,i))
219+ Skip s' -> loop z s' i
220+ Yield x s'
221+ | j >= top -> {-# SCC "mapAccumL/resize" #-} do
222+ let top' = (top + 1) `shiftL` 1
223+ arr' <- A.unsafeNew top'
224+ A.copyM arr' 0 arr 0 top
225+ outer arr' top' z s i
226+ | otherwise -> do let (z',c) = f z x
227+ d <- unsafeWrite arr i c
228+ loop z' s' (i+d)
229+ where j | ord x < 0x10000 = i
230+ | otherwise = i + 1
231+{-# INLINE [0] mapAccumL #-}