Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tails, inits, initTails example implementation #214

Merged
merged 8 commits into from
Feb 28, 2024
6 changes: 6 additions & 0 deletions mono-traversable/ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# ChangeLog for mono-traversable

## 1.0.17.0

* Added `inits`, `tails`, `initTails` to class `IsSequence` with tests and benchmarks for `initTails`.
* Improved ghc benchmark flags.
* Removed extraneous constraint `IsSequence` from `initMay`.

## 1.0.16.0

* Added MonoPointed instance for bytestring Builder
Expand Down
89 changes: 89 additions & 0 deletions mono-traversable/bench/InitTails.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE TypeFamilies #-}
module InitTails (initTailsBenchmarks) where

#if MIN_VERSION_gauge(0,2,0)
import Gauge
#else
import Gauge.Main
#endif

import Data.Sequences as Ss
import Data.MonoTraversable
import Type.Reflection (Typeable, typeRep)
import Control.DeepSeq
import Data.Foldable (foldl')
import Data.Functor ((<&>))

import Data.ByteString (StrictByteString)
import Data.ByteString.Lazy (LazyByteString)
import qualified Data.Text as TS
import qualified Data.Text.Lazy as TL
import Data.Sequence (Seq)
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Storable as VS

initTailsBenchmarks :: Benchmark
initTailsBenchmarks = bgroup "InitTails"
[ bmg @[Char]
, bmg @StrictByteString
, bmg @LazyByteString
, bmg @TS.Text
, bmg @TL.Text
, bmg @(Seq Char)
, bmg @(V.Vector Char)
, bmg @(VU.Vector Char)
, bmg @(VS.Vector Char)
]

bmg :: forall seq.
( TestLabel seq
, NFData seq
, IsSequence seq
, Num (Index seq)
, Enum (Element seq)
) => Benchmark
bmg = bgroup (testLabel @seq) $ bm <$> labelledLengths
where
bm :: (String,[Int]) -> Benchmark
bm (label,lengths) = bgroup label $
[ ("weak", weakConsume)
, ("deep", deepConsume)
] <&> \(wdLabel,consume) -> bench wdLabel
$ nf (map $ consume . initTails @seq)
$ (`Ss.replicate` (toEnum 65)) . fromIntegral <$> lengths
labelledLengths =
[ ("tiny", [0,1,2,5,10])
, ("small", [100,150,200,300])
, ("medium", [1000,1500,2000,2500])
, ("large", [10000,20000,50000])
]

class Typeable a => TestLabel a where
testLabel :: String
testLabel = show $ typeRep @a
instance TestLabel [Char]
instance TestLabel StrictByteString where testLabel = "StrictByteString"
instance TestLabel LazyByteString where testLabel = "LazyByteString"
instance TestLabel TS.Text where testLabel = "StrictText"
instance TestLabel TL.Text where testLabel = "LazyText"
instance TestLabel (Seq Char) where testLabel = "Seq"
instance TestLabel (V.Vector Char) where testLabel = "Vector"
instance TestLabel (VU.Vector Char) where testLabel = "UnboxedVector"
instance TestLabel (VS.Vector Char) where testLabel = "StorableVector"


-- *Consume used to keep memory usage lower
deepConsume :: NFData seq => [(seq,seq)] -> ()
deepConsume = foldl' (\() (is,ts) -> deepseq is $ deepseq ts ()) ()

weakConsume :: [(seq,seq)] -> ()
weakConsume = foldl' (\() (_,_) -> ()) ()

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{-# LANGUAGE CPP #-}
module Sorting (sortingBenchmarks) where

#if MIN_VERSION_gauge(0,2,0)
import Gauge
Expand All @@ -12,17 +13,20 @@ import qualified Data.List
import qualified System.Random.MWC as MWC
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as U
import System.IO.Unsafe (unsafePerformIO)

sortingBenchmarks :: Benchmark
sortingBenchmarks
= bgroup "Sorting"
$ unsafePerformIO
$ mapM mkGroup [10, 100, 1000, 10000]

asVector :: V.Vector a -> V.Vector a
asVector = id

asUVector :: U.Vector a -> U.Vector a
asUVector = id

main :: IO ()
main = do
mapM mkGroup [10, 100, 1000, 10000] >>= defaultMain

mkGroup :: Int -> IO Benchmark
mkGroup size = do
gen <- MWC.create
Expand Down
17 changes: 17 additions & 0 deletions mono-traversable/bench/main.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{-# LANGUAGE CPP #-}

#if MIN_VERSION_gauge(0,2,0)
import Gauge
#else
import Gauge.Main
#endif

import Sorting (sortingBenchmarks)
import InitTails (initTailsBenchmarks)


main :: IO ()
main = defaultMain
[ sortingBenchmarks
, initTailsBenchmarks
]
18 changes: 13 additions & 5 deletions mono-traversable/mono-traversable.cabal
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
cabal-version: 1.12

-- This file has been generated from package.yaml by hpack version 0.34.7.
-- This file has been generated from package.yaml by hpack version 0.36.0.
--
-- see: https://github.com/sol/hpack

name: mono-traversable
version: 1.0.16.0
version: 1.0.17.0
synopsis: Type classes for mapping, folding, and traversing monomorphic containers
description: Please see the README at <https://www.stackage.org/package/mono-traversable>
category: Data
Expand Down Expand Up @@ -72,18 +72,26 @@ test-suite test
, vector
default-language: Haskell2010

benchmark sorting
benchmark all
type: exitcode-stdio-1.0
main-is: sorting.hs
main-is: main.hs
other-modules:
InitTails
Sorting
Paths_mono_traversable
hs-source-dirs:
bench
ghc-options: -Wall -O2
ghc-options: -Wall -O2 -with-rtsopts=-A32m
build-depends:
base
, bytestring
, containers
, deepseq
, gauge
, mono-traversable
, mwc-random
, text
, vector
default-language: Haskell2010
if impl(ghc >= 8.6)
ghc-options: -fproc-alignment=64
14 changes: 11 additions & 3 deletions mono-traversable/package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: mono-traversable
version: 1.0.16.0
version: 1.0.17.0
synopsis: Type classes for mapping, folding, and traversing monomorphic containers
description: Please see the README at <https://www.stackage.org/package/mono-traversable>
category: Data
Expand Down Expand Up @@ -45,15 +45,23 @@ tests:
- unordered-containers
- foldl
benchmarks:
sorting:
main: sorting.hs
all:
main: main.hs
source-dirs: bench
ghc-options:
- -Wall
- -O2
- -with-rtsopts=-A32m
when:
- condition: impl(ghc >= 8.6)
ghc-options: -fproc-alignment=64
dependencies:
- base
- gauge
- mono-traversable
- text
- containers
- bytestring
- vector
- mwc-random
- deepseq
62 changes: 61 additions & 1 deletion mono-traversable/src/Data/Sequences.hs
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ class (Monoid seq, MonoTraversable seq, SemiSequence seq, MonoPointed seq) => Is
-- an empty monomorphic container.
--
-- @since 1.0.0
initMay :: IsSequence seq => seq -> Maybe seq
initMay :: seq -> Maybe seq
initMay seq
| onull seq = Nothing
| otherwise = Just (initEx seq)
Expand Down Expand Up @@ -472,6 +472,47 @@ class (Monoid seq, MonoTraversable seq, SemiSequence seq, MonoPointed seq) => Is
splitWhen :: (Element seq -> Bool) -> seq -> [seq]
splitWhen = defaultSplitWhen

-- | Returns all the final segments of 'seq' with the longest first.
--
-- @
-- > tails [1,2]
-- [[1,2],[2],[]]
-- > tails []
-- [[]]
-- @
--
-- @since 1.0.17.0
tails :: seq -> [seq]
tails x = x : maybe mempty tails (tailMay x)

-- | Return all the initial segments of 'seq' with the shortest first.
--
-- @
-- > inits [1,2]
-- [[],[1],[1,2]]
-- > inits []
-- [[]]
-- @
--
-- @since 1.0.17.0
inits :: seq -> [seq]
inits seq = is seq [seq]
where
is = maybe id (\x -> is x . (x :)) . initMay

-- | Return all the pairs of inital and final segments of 'seq'.
--
-- @
-- > initTails [1,2]
-- [([],[1,2]),([1],[2]),([1,2],[])]
-- > initTails []
-- [([],[])]
-- @
--
-- @since 1.0.17.0
initTails :: seq -> [(seq,seq)]
initTails seq = List.zip (inits seq) (tails seq)

{-# INLINE fromList #-}
{-# INLINE break #-}
{-# INLINE span #-}
Expand Down Expand Up @@ -502,6 +543,9 @@ class (Monoid seq, MonoTraversable seq, SemiSequence seq, MonoPointed seq) => Is
{-# INLINE indexEx #-}
{-# INLINE unsafeIndex #-}
{-# INLINE splitWhen #-}
{-# INLINE tails #-}
{-# INLINE inits #-}
{-# INLINE initTails #-}

-- | Use "Data.List"'s implementation of 'Data.List.find'.
defaultFind :: MonoFoldable seq => (Element seq -> Bool) -> seq -> Maybe (Element seq)
Expand Down Expand Up @@ -607,6 +651,13 @@ instance IsSequence [a] where
(matches, nonMatches) = partition ((== f head) . f) tail
groupAllOn _ [] = []
splitWhen = List.splitWhen
tails = List.tails
inits = List.inits
initTails = its id
where
its :: ([a] -> [a]) -> [a] -> [([a],[a])]
its f xs@(y:ys) = (f [], xs) : its (f . (y:)) ys
its f [] = [(f [], [])]
{-# INLINE fromList #-}
{-# INLINE break #-}
{-# INLINE span #-}
Expand All @@ -625,6 +676,9 @@ instance IsSequence [a] where
{-# INLINE groupBy #-}
{-# INLINE groupAllOn #-}
{-# INLINE splitWhen #-}
{-# INLINE tails #-}
{-# INLINE inits #-}
{-# INLINE initTails #-}

instance SemiSequence (NE.NonEmpty a) where
type Index (NE.NonEmpty a) = Int
Expand Down Expand Up @@ -961,6 +1015,12 @@ instance IsSequence (Seq.Seq a) where
{-# INLINE indexEx #-}
{-# INLINE unsafeIndex #-}

initTails = its . (,) mempty
where
its x@(is, y Seq.:<| ts) = x : its (is Seq.:|> y, ts)
its x@(_, Seq.Empty) = [x]
{-# INLINE initTails #-}

instance SemiSequence (V.Vector a) where
type Index (V.Vector a) = Int
reverse = V.reverse
Expand Down
Loading
Loading