diff --git a/Symbols.md b/Symbols.md index 28620fe6e..3900a3337 100644 --- a/Symbols.md +++ b/Symbols.md @@ -781,6 +781,7 @@ * runStateT * scaleFloat * scanl +* scanl' * scanr * second * selName diff --git a/src/Protolude.hs b/src/Protolude.hs index 6142bccc9..585187c82 100644 --- a/src/Protolude.hs +++ b/src/Protolude.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-} {-# LANGUAGE Trustworthy #-} {-# LANGUAGE FlexibleContexts #-} @@ -27,6 +28,7 @@ module Protolude ( liftIO2, #if !MIN_VERSION_base(4,8,0) (&), + scanl', #endif die, ) where @@ -177,6 +179,9 @@ import Data.List as X ( , subsequences , permutations , scanl +#if MIN_VERSION_base(4,8,0) + , scanl' +#endif , scanr , iterate , repeat @@ -197,6 +202,12 @@ import Data.List as X ( , genericReplicate ) +#if !MIN_VERSION_base(4,8,0) +-- These imports are required for the scanl' rewrite rules +import GHC.Exts (build) +import Data.List (tail) +#endif + -- Hashing import Data.Hashable as X ( Hashable @@ -619,3 +630,32 @@ die err = System.Exit.die (toS err) die :: Text -> IO a die err = hPutStrLn stderr err >> exitFailure #endif + +#if !MIN_VERSION_base(4,8,0) +-- This is a literal copy of the implementation in GHC.List in base-4.10.1.0. + +-- | A strictly accumulating version of 'scanl' +{-# NOINLINE [1] scanl' #-} +scanl' :: (b -> a -> b) -> b -> [a] -> [b] +scanl' = scanlGo' + where + scanlGo' :: (b -> a -> b) -> b -> [a] -> [b] + scanlGo' f !q ls = q : (case ls of + [] -> [] + x:xs -> scanlGo' f (f q x) xs) + +{-# RULES +"scanl'" [~1] forall f a bs . scanl' f a bs = + build (\c n -> a `c` foldr (scanlFB' f c) (flipSeqScanl' n) bs a) +"scanlList'" [1] forall f a bs . + foldr (scanlFB' f (:)) (flipSeqScanl' []) bs a = tail (scanl' f a bs) + #-} + +{-# INLINE [0] scanlFB' #-} +scanlFB' :: (b -> a -> b) -> (b -> c -> c) -> a -> (b -> c) -> b -> c +scanlFB' f c = \b g -> \x -> let !b' = f x b in b' `c` g b' + +{-# INLINE [0] flipSeqScanl' #-} +flipSeqScanl' :: a -> b -> a +flipSeqScanl' a !_b = a +#endif