2-- Module : BenchmarkOps
3-- Copyright : (c) 2018 Harendra Kumar
6-- Maintainer : harendra.kumar@gmail.com
9{-# LANGUAGE FlexibleContexts #-}
10{-# LANGUAGE ScopedTypeVariables #-}
11{-# LANGUAGE DeriveAnyClass #-}
12{-# LANGUAGE DeriveGeneric #-}
16import Control.Monad (when)
17import Control.Monad.IO.Class (MonadIO)
18import Data.Functor.Identity (Identity, runIdentity)
19import Data.Maybe (fromJust)
21 (Monad, Int, (+), ($), (.), return, fmap, even, (>), (<=), (==), (>=),
22 subtract, undefined, Maybe(..), odd, Bool, not, (>>=), mapM_, curry,
23 maxBound, div, IO, compare, Double, fromIntegral, Integer, (<$>),
24 (<*>), flip, (**), (/))
25import qualified Prelude as P
26import qualified Data.Foldable as F
27import qualified GHC.Exts as GHC
28import Control.DeepSeq (NFData)
29import GHC.Generics (Generic)
31import qualified Streamly as S hiding (foldMapWith, runStream)
32import qualified Streamly.Prelude as S
33import qualified Streamly.Pipe as Pipe
35value, maxValue, value2 :: Int
42value2 = P.round (P.fromIntegral value**(1/2::P.Double)) -- double nested loop
44-------------------------------------------------------------------------------
46-------------------------------------------------------------------------------
48-------------------------------------------------------------------------------
49-- Stream generation and elimination
50-------------------------------------------------------------------------------
52type Stream m a = S.SerialT m a
55source :: (S.MonadAsync m, S.IsStream t) => Int -> t m Int
56source n = sourceUnfoldrM n
58{-# INLINE sourceIntFromTo #-}
59sourceIntFromTo :: (Monad m, S.IsStream t) => Int -> t m Int
60sourceIntFromTo n = S.enumerateFromTo n (n + value)
62{-# INLINE sourceIntFromThenTo #-}
63sourceIntFromThenTo :: (Monad m, S.IsStream t) => Int -> t m Int
64sourceIntFromThenTo n = S.enumerateFromThenTo n (n + 1) (n + value)
66{-# INLINE sourceFracFromTo #-}
67sourceFracFromTo :: (Monad m, S.IsStream t) => Int -> t m Double
69 S.enumerateFromTo (fromIntegral n) (fromIntegral (n + value))
71{-# INLINE sourceFracFromThenTo #-}
72sourceFracFromThenTo :: (Monad m, S.IsStream t) => Int -> t m Double
73sourceFracFromThenTo n = S.enumerateFromThenTo (fromIntegral n)
74 (fromIntegral n + 1.0001) (fromIntegral (n + value))
76{-# INLINE sourceIntegerFromStep #-}
77sourceIntegerFromStep :: (Monad m, S.IsStream t) => Int -> t m Integer
78sourceIntegerFromStep n =
79 S.take value $ S.enumerateFromThen (fromIntegral n) (fromIntegral n + 1)
81{-# INLINE sourceFromList #-}
82sourceFromList :: (Monad m, S.IsStream t) => Int -> t m Int
83sourceFromList n = S.fromList [n..n+value]
85{-# INLINE sourceFromListM #-}
86sourceFromListM :: (S.MonadAsync m, S.IsStream t) => Int -> t m Int
87sourceFromListM n = S.fromListM (Prelude.fmap return [n..n+value])
89{-# INLINE sourceFromIndices #-}
90sourceFromIndices :: (Monad m, S.IsStream t) => Int -> t m Int
91sourceFromIndices n = S.take value $ S.fromIndices (+ n)
93{-# INLINE sourceFromIndicesM #-}
94sourceFromIndicesM :: (S.MonadAsync m, S.IsStream t) => Int -> t m Int
95sourceFromIndicesM n = S.take value $ S.fromIndicesM (Prelude.fmap return (+ n))
97{-# INLINE sourceFromFoldable #-}
98sourceFromFoldable :: S.IsStream t => Int -> t m Int
99sourceFromFoldable n = S.fromFoldable [n..n+value]
101{-# INLINE sourceFromFoldableM #-}
102sourceFromFoldableM :: (S.IsStream t, S.MonadAsync m) => Int -> t m Int
103sourceFromFoldableM n = S.fromFoldableM (Prelude.fmap return [n..n+value])
105{-# INLINE sourceFoldMapWith #-}
106sourceFoldMapWith :: (S.IsStream t, S.Semigroup (t m Int))
108sourceFoldMapWith n = S.foldMapWith (S.<>) S.yield [n..n+value]
110{-# INLINE sourceFoldMapWithM #-}
111sourceFoldMapWithM :: (S.IsStream t, Monad m, S.Semigroup (t m Int))
113sourceFoldMapWithM n = S.foldMapWith (S.<>) (S.yieldM . return) [n..n+value]
115{-# INLINE sourceFoldMapM #-}
116sourceFoldMapM :: (S.IsStream t, Monad m, P.Monoid (t m Int))
118sourceFoldMapM n = F.foldMap (S.yieldM . return) [n..n+value]
120{-# INLINE sourceUnfoldr #-}
121sourceUnfoldr :: (Monad m, S.IsStream t) => Int -> t m Int
122sourceUnfoldr n = S.unfoldr step n
127 else Just (cnt, cnt + 1)
129{-# INLINE sourceUnfoldrN #-}
130sourceUnfoldrN :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
131sourceUnfoldrN upto start = S.unfoldr step start
134 if cnt > start + upto
136 else Just (cnt, cnt + 1)
138{-# INLINE sourceUnfoldrM #-}
139sourceUnfoldrM :: (S.IsStream t, S.MonadAsync m) => Int -> t m Int
140sourceUnfoldrM n = S.unfoldrM step n
145 else return (Just (cnt, cnt + 1))
147{-# INLINE sourceUnfoldrMN #-}
148sourceUnfoldrMN :: (S.IsStream t, S.MonadAsync m) => Int -> Int -> t m Int
149sourceUnfoldrMN upto start = S.unfoldrM step start
152 if cnt > start + upto
154 else return (Just (cnt, cnt + 1))
156{-# INLINE sourceUnfoldrMAction #-}
157sourceUnfoldrMAction :: (S.IsStream t, S.MonadAsync m) => Int -> t m (m Int)
158sourceUnfoldrMAction n = S.serially $ S.unfoldrM step n
163 else return (Just (return cnt, cnt + 1))
165-------------------------------------------------------------------------------
166-- Pure stream generation
167-------------------------------------------------------------------------------
169{-# INLINE sourceIsList #-}
170sourceIsList :: Int -> S.SerialT Identity Int
171sourceIsList n = GHC.fromList [n..n+value]
173{-# INLINE sourceIsString #-}
174sourceIsString :: Int -> S.SerialT Identity P.Char
175sourceIsString n = GHC.fromString (P.replicate (n + value) 'a')
177-------------------------------------------------------------------------------
179-------------------------------------------------------------------------------
181{-# INLINE runStream #-}
182runStream :: Monad m => Stream m a -> m ()
186toList :: Monad m => Stream m Int -> m [Int]
190{-# INLINE maximum #-}
191{-# INLINE minimum #-}
193{-# INLINE findIndex #-}
194{-# INLINE elemIndex #-}
195{-# INLINE foldl1'Reduce #-}
196head, last, minimum, maximum, find, findIndex, elemIndex, foldl1'Reduce
197 :: Monad m => Stream m Int -> m (Maybe Int)
199{-# INLINE minimumBy #-}
200{-# INLINE maximumBy #-}
201minimumBy, maximumBy :: Monad m => Stream m Int -> m (Maybe Int)
203{-# INLINE foldl'Reduce #-}
204{-# INLINE foldl'ReduceMap #-}
205{-# INLINE foldlM'Reduce #-}
206{-# INLINE foldrMReduce #-}
209{-# INLINE product #-}
210foldl'Reduce, foldl'ReduceMap, foldlM'Reduce, foldrMReduce, length, sum, product
212 => Stream m Int -> m Int
214{-# INLINE foldl'Build #-}
215{-# INLINE foldlM'Build #-}
216{-# INLINE foldrMBuild #-}
217foldrMBuild, foldl'Build, foldlM'Build
219 => Stream m Int -> m [Int]
227{-# INLINE notElem #-}
228null, elem, notElem, all, any, and, or :: Monad m => Stream m Int -> m Bool
231toNull :: Monad m => (t m a -> S.SerialT m a) -> t m a -> m ()
232toNull t = runStream . t
235uncons :: Monad m => Stream m Int -> m ()
240 Just (_, t) -> uncons t
243init :: Monad m => Stream m a -> m ()
244init s = S.init s >>= Prelude.mapM_ S.drain
247tail :: Monad m => Stream m a -> m ()
248tail s = S.tail s >>= Prelude.mapM_ tail
250{-# INLINE nullHeadTail #-}
251nullHeadTail :: Monad m => Stream m Int -> m ()
256 S.tail s >>= Prelude.mapM_ nullHeadTail
259mapM_ :: Monad m => Stream m Int -> m ()
260mapM_ = S.mapM_ (\_ -> return ())
265{-# INLINE toRevList #-}
266toRevList :: Monad m => Stream m Int -> m [Int]
267toRevList = S.toRevList
270foldrMBuild = S.foldrM (\x xs -> xs >>= return . (x :)) (return [])
271foldl'Build = S.foldl' (flip (:)) []
272foldlM'Build = S.foldlM' (\xs x -> return $ x : xs) []
274foldrMReduce = S.foldrM (\x xs -> xs >>= return . (x +)) (return 0)
275foldl'Reduce = S.foldl' (+) 0
276foldl'ReduceMap = P.fmap (+1) . S.foldl' (+) 0
277foldl1'Reduce = S.foldl1' (+)
278foldlM'Reduce = S.foldlM' (\xs a -> return $ a + xs) 0
283elem = S.elem maxValue
284notElem = S.notElem maxValue
286all = S.all (<= maxValue)
287any = S.any (> maxValue)
288and = S.and . S.map (<= maxValue)
289or = S.or . S.map (> maxValue)
290find = S.find (== maxValue)
291findIndex = S.findIndex (== maxValue)
292elemIndex = S.elemIndex maxValue
297minimumBy = S.minimumBy compare
298maximumBy = S.maximumBy compare
300-------------------------------------------------------------------------------
302-------------------------------------------------------------------------------
304{-# INLINE transform #-}
305transform :: Monad m => Stream m a -> m ()
308{-# INLINE composeN #-}
311 => Int -> (Stream m Int -> Stream m Int) -> Stream m Int -> m ()
315 2 -> transform . f . f
316 3 -> transform . f . f . f
317 4 -> transform . f . f . f . f
320-- polymorphic stream version of composeN
321{-# INLINE composeN' #-}
323 :: (S.IsStream t, Monad m)
324 => Int -> (t m Int -> Stream m Int) -> t m Int -> m ()
328 2 -> transform . f . S.adapt . f
329 3 -> transform . f . S.adapt . f . S.adapt . f
330 4 -> transform . f . S.adapt . f . S.adapt . f . S.adapt . f
334{-# INLINE scanl1' #-}
337{-# INLINE mapMaybe #-}
338{-# INLINE filterEven #-}
339{-# INLINE filterAllOut #-}
340{-# INLINE filterAllIn #-}
341{-# INLINE takeOne #-}
342{-# INLINE takeAll #-}
343{-# INLINE takeWhileTrue #-}
344{-# INLINE takeWhileMTrue #-}
345{-# INLINE dropOne #-}
346{-# INLINE dropAll #-}
347{-# INLINE dropWhileTrue #-}
348{-# INLINE dropWhileMTrue #-}
349{-# INLINE dropWhileFalse #-}
350{-# INLINE findIndices #-}
351{-# INLINE elemIndices #-}
352{-# INLINE insertBy #-}
353{-# INLINE deleteBy #-}
354{-# INLINE reverse #-}
355{-# INLINE reverse' #-}
357{-# INLINE foldrSMap #-}
359{-# INLINE foldrTMap #-}
360scan, scanl1', map, fmap, mapMaybe, filterEven, filterAllOut,
361 filterAllIn, takeOne, takeAll, takeWhileTrue, takeWhileMTrue, dropOne,
362 dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse,
363 findIndices, elemIndices, insertBy, deleteBy, reverse, reverse',
364 foldrS, foldrSMap, foldrT, foldrTMap
366 => Int -> Stream m Int -> m ()
368{-# INLINE mapMaybeM #-}
369{-# INLINE intersperse #-}
370mapMaybeM, intersperse :: S.MonadAsync m => Int -> Stream m Int -> m ()
373{-# INLINE transformMapM #-}
374{-# INLINE transformComposeMapM #-}
375{-# INLINE transformTeeMapM #-}
376{-# INLINE transformZipMapM #-}
379mapM, map', transformMapM, transformComposeMapM, transformTeeMapM,
380 transformZipMapM :: (S.IsStream t, S.MonadAsync m)
381 => (t m Int -> S.SerialT m Int) -> Int -> t m Int -> m ()
383fmap' :: (S.IsStream t, S.MonadAsync m, P.Functor (t m))
384 => (t m Int -> S.SerialT m Int) -> Int -> t m Int -> m ()
386{-# INLINE sequence #-}
387sequence :: (S.IsStream t, S.MonadAsync m)
388 => (t m Int -> S.SerialT m Int) -> t m (m Int) -> m ()
390scan n = composeN n $ S.scanl' (+) 0
391scanl1' n = composeN n $ S.scanl1' (+)
392fmap n = composeN n $ Prelude.fmap (+1)
393fmap' t n = composeN' n $ t . Prelude.fmap (+1)
394map n = composeN n $ S.map (+1)
395map' t n = composeN' n $ t . S.map (+1)
396mapM t n = composeN' n $ t . S.mapM return
398transformMapM t n = composeN' n $ t . S.transform (Pipe.mapM return)
399transformComposeMapM t n = composeN' n $ t . S.transform
400 (Pipe.mapM (\x -> return (x + 1))
401 `Pipe.compose` Pipe.mapM (\x -> return (x + 2)))
402transformTeeMapM t n = composeN' n $ t . S.transform
403 (Pipe.mapM (\x -> return (x + 1))
404 `Pipe.tee` Pipe.mapM (\x -> return (x + 2)))
405transformZipMapM t n = composeN' n $ t . S.transform
406 (Pipe.zipWith (+) (Pipe.mapM (\x -> return (x + 1)))
407 (Pipe.mapM (\x -> return (x + 2))))
409mapMaybe n = composeN n $ S.mapMaybe
410 (\x -> if Prelude.odd x then Nothing else Just x)
411mapMaybeM n = composeN n $ S.mapMaybeM
412 (\x -> if Prelude.odd x then return Nothing else return $ Just x)
413sequence t = transform . t . S.sequence
414filterEven n = composeN n $ S.filter even
415filterAllOut n = composeN n $ S.filter (> maxValue)
416filterAllIn n = composeN n $ S.filter (<= maxValue)
417takeOne n = composeN n $ S.take 1
418takeAll n = composeN n $ S.take maxValue
419takeWhileTrue n = composeN n $ S.takeWhile (<= maxValue)
420takeWhileMTrue n = composeN n $ S.takeWhileM (return . (<= maxValue))
421dropOne n = composeN n $ S.drop 1
422dropAll n = composeN n $ S.drop maxValue
423dropWhileTrue n = composeN n $ S.dropWhile (<= maxValue)
424dropWhileMTrue n = composeN n $ S.dropWhileM (return . (<= maxValue))
425dropWhileFalse n = composeN n $ S.dropWhile (> maxValue)
426findIndices n = composeN n $ S.findIndices (== maxValue)
427elemIndices n = composeN n $ S.elemIndices maxValue
428intersperse n = composeN n $ S.intersperse maxValue
429insertBy n = composeN n $ S.insertBy compare maxValue
430deleteBy n = composeN n $ S.deleteBy (>=) maxValue
431reverse n = composeN n $ S.reverse
432reverse' n = composeN n $ S.reverse'
433foldrS n = composeN n $ S.foldrS S.cons S.nil
434foldrSMap n = composeN n $ S.foldrS (\x xs -> x + 1 `S.cons` xs) S.nil
435foldrT n = composeN n $ S.foldrT S.cons S.nil
436foldrTMap n = composeN n $ S.foldrT (\x xs -> x + 1 `S.cons` xs) S.nil
438-------------------------------------------------------------------------------
440-------------------------------------------------------------------------------
442iterStreamLen, maxIters :: Int
446{-# INLINE iterateSource #-}
449 => (Stream m Int -> Stream m Int) -> Int -> Int -> Stream m Int
450iterateSource g i n = f i (sourceUnfoldrMN iterStreamLen n)
453 f x m = g (f (x P.- 1) m)
455{-# INLINE iterateMapM #-}
456{-# INLINE iterateScan #-}
457{-# INLINE iterateScanl1 #-}
458{-# INLINE iterateFilterEven #-}
459{-# INLINE iterateTakeAll #-}
460{-# INLINE iterateDropOne #-}
461{-# INLINE iterateDropWhileFalse #-}
462{-# INLINE iterateDropWhileTrue #-}
463iterateMapM, iterateScan, iterateScanl1, iterateFilterEven, iterateTakeAll,
464 iterateDropOne, iterateDropWhileFalse, iterateDropWhileTrue
466 => Int -> Stream m Int
469iterateScan = iterateSource (S.scanl' (+) 0) (maxIters `div` 10)
471iterateScanl1 = iterateSource (S.scanl1' (+)) (maxIters `div` 10)
473iterateMapM = iterateSource (S.mapM return) maxIters
474iterateFilterEven = iterateSource (S.filter even) maxIters
475iterateTakeAll = iterateSource (S.take maxValue) maxIters
476iterateDropOne = iterateSource (S.drop 1) maxIters
477iterateDropWhileFalse = iterateSource (S.dropWhile (> maxValue)) maxIters
478iterateDropWhileTrue = iterateSource (S.dropWhile (<= maxValue)) maxIters
480-------------------------------------------------------------------------------
482-------------------------------------------------------------------------------
486{-# INLINE mergeBy #-}
487zip, zipM, mergeBy :: Monad m => Stream m Int -> m ()
491 let src1 = fromJust r
492 transform (S.zipWith (,) src src1)
495 let src1 = fromJust r
496 transform (S.zipWithM (curry return) src src1)
500 let src1 = fromJust r
501 transform (S.mergeBy P.compare src src1)
503{-# INLINE isPrefixOf #-}
504{-# INLINE isSubsequenceOf #-}
505isPrefixOf, isSubsequenceOf :: Monad m => Stream m Int -> m Bool
507isPrefixOf src = S.isPrefixOf src src
508isSubsequenceOf src = S.isSubsequenceOf src src
510{-# INLINE stripPrefix #-}
511stripPrefix :: Monad m => Stream m Int -> m ()
513 _ <- S.stripPrefix src src
516{-# INLINE zipAsync #-}
517{-# INLINE zipAsyncM #-}
518{-# INLINE zipAsyncAp #-}
519zipAsync, zipAsyncAp, zipAsyncM :: S.MonadAsync m => Stream m Int -> m ()
523 let src1 = fromJust r
524 transform (S.zipAsyncWith (,) src src1)
528 let src1 = fromJust r
529 transform (S.zipAsyncWithM (curry return) src src1)
533 let src1 = fromJust r
534 transform (S.zipAsyncly $ (,) <$> S.serially src
538eqBy :: (Monad m, P.Eq a) => Stream m a -> m P.Bool
539eqBy src = S.eqBy (==) src src
542cmpBy :: (Monad m, P.Ord a) => Stream m a -> m P.Ordering
543cmpBy src = S.cmpBy P.compare src src
545-- The worst case for concatMap, concat a 1 element stream n times.
546{-# INLINE concatMap #-}
547concatMap :: S.MonadAsync m => Int -> Stream m Int
548concatMap n = S.concatMap (\_ -> sourceUnfoldrMN 1 n)
549 (sourceUnfoldrMN value n)
551{-# INLINE concatMapPure1xN #-}
552concatMapPure1xN :: S.MonadAsync m => Int -> Stream m Int
553concatMapPure1xN n = S.concatMap (\_ -> sourceUnfoldrN 1 n)
554 (sourceUnfoldrN value n)
556-- We use a (sqrt n) element stream as source and then generate and concat a
557-- (sqrt n) element stream for each element of the source to produce an n
559{-# INLINE concatMapNxN #-}
560concatMapNxN :: S.MonadAsync m => Int -> Stream m Int
561concatMapNxN n = S.concatMap (\_ -> sourceUnfoldrMN value2 n)
562 (sourceUnfoldrMN value2 n)
564{-# INLINE concatMapRepl4xN #-}
565concatMapRepl4xN :: S.MonadAsync m => Int -> Stream m Int
566concatMapRepl4xN n = S.concatMap (S.replicate 4)
567 (sourceUnfoldrMN (value `div` 4) n)
569-------------------------------------------------------------------------------
571-------------------------------------------------------------------------------
573{-# INLINE scanMap #-}
574{-# INLINE dropMap #-}
575{-# INLINE dropScan #-}
576{-# INLINE takeDrop #-}
577{-# INLINE takeScan #-}
578{-# INLINE takeMap #-}
579{-# INLINE filterDrop #-}
580{-# INLINE filterTake #-}
581{-# INLINE filterScan #-}
582{-# INLINE filterScanl1 #-}
583{-# INLINE filterMap #-}
584scanMap, dropMap, dropScan, takeDrop, takeScan, takeMap, filterDrop,
585 filterTake, filterScan, filterScanl1, filterMap
586 :: MonadIO m => Int -> Stream m Int -> m ()
588scanMap n = composeN n $ S.map (subtract 1) . S.scanl' (+) 0
589dropMap n = composeN n $ S.map (subtract 1) . S.drop 1
590dropScan n = composeN n $ S.scanl' (+) 0 . S.drop 1
591takeDrop n = composeN n $ S.drop 1 . S.take maxValue
592takeScan n = composeN n $ S.scanl' (+) 0 . S.take maxValue
593takeMap n = composeN n $ S.map (subtract 1) . S.take maxValue
594filterDrop n = composeN n $ S.drop 1 . S.filter (<= maxValue)
595filterTake n = composeN n $ S.take maxValue . S.filter (<= maxValue)
596filterScan n = composeN n $ S.scanl' (+) 0 . S.filter (<= maxBound)
597filterScanl1 n = composeN n $ S.scanl1' (+) . S.filter (<= maxBound)
598filterMap n = composeN n $ S.map (subtract 1) . S.filter (<= maxValue)
600data Pair a b = Pair !a !b deriving (Generic, NFData)
602{-# INLINE sumProductFold #-}
603sumProductFold :: Monad m => Stream m Int -> m (Int, Int)
604sumProductFold = S.foldl' (\(s,p) x -> (s + x, p P.* x)) (0,1)
606{-# INLINE sumProductScan #-}
607sumProductScan :: Monad m => Stream m Int -> m (Pair Int Int)
608sumProductScan = S.foldl' (\(Pair _ p) (s0,x) -> Pair s0 (p P.* x)) (Pair 0 1)
609 . S.scanl' (\(s,_) x -> (s + x,x)) (0,0)
611-------------------------------------------------------------------------------
612-- Pure stream operations
613-------------------------------------------------------------------------------
615{-# INLINE eqInstance #-}
616eqInstance :: Stream Identity Int -> Bool
617eqInstance src = src == src
619{-# INLINE eqInstanceNotEq #-}
620eqInstanceNotEq :: Stream Identity Int -> Bool
621eqInstanceNotEq src = src P./= src
623{-# INLINE ordInstance #-}
624ordInstance :: Stream Identity Int -> Bool
625ordInstance src = src P.< src
627{-# INLINE ordInstanceMin #-}
628ordInstanceMin :: Stream Identity Int -> Stream Identity Int
629ordInstanceMin src = P.min src src
631{-# INLINE showInstance #-}
632showInstance :: Stream Identity Int -> P.String
633showInstance src = P.show src
635{-# INLINE showInstanceList #-}
636showInstanceList :: [Int] -> P.String
637showInstanceList src = P.show src
639{-# INLINE readInstance #-}
640readInstance :: P.String -> Stream Identity Int
645 _ -> P.error "readInstance: no parse"
647{-# INLINE readInstanceList #-}
648readInstanceList :: P.String -> [Int]
649readInstanceList str =
653 _ -> P.error "readInstance: no parse"
655{-# INLINE pureFoldl' #-}
656pureFoldl' :: Stream Identity Int -> Int
657pureFoldl' = runIdentity . S.foldl' (+) 0
659{-# INLINE foldableFoldl' #-}
660foldableFoldl' :: Stream Identity Int -> Int
661foldableFoldl' = F.foldl' (+) 0
663{-# INLINE foldableSum #-}
664foldableSum :: Stream Identity Int -> Int
667{-# INLINE traversableMapM #-}
668traversableMapM :: Stream Identity Int -> IO (Stream Identity Int)
669traversableMapM = P.mapM return
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list