on master help sign in
Expose doctest setup for immutable array modules
commit303143a471bbe63ec233d7070d9e4400510609f0 authorHarendra Kumar <harendra@composewell.com> dateSat 11 Mar 2023, 22:45 (4 years ago) parent36e3e8b
6 files changed+133−110
core/src/DocTestDataArray.hs+14 core/src/Streamly/Data/Array.hs+82−81 core/src/Streamly/Internal/Data/Array.hs+33−21 core/src/Streamly/Internal/Data/Array/Type.hs+2−7 core/src/Streamly/Internal/Data/Stream/StreamD/Generate.hs+1 core/streamly-core.cabal+1−1
core/src/DocTestDataArray.hsnew file+14
@@ -0,0 +1,14 @@
1+{- $setup
2+>>> :m
3+>>> :set -XFlexibleContexts
4+>>> import Data.Function ((&))
5+>>> import Data.Functor.Identity (Identity(..))
6+>>> import System.IO.Unsafe (unsafePerformIO)
7+
8+>>> import Streamly.Data.Array (Array)
9+>>> import Streamly.Data.Stream (Stream)
10+
11+>>> import qualified Streamly.Data.Array as Array
12+>>> import qualified Streamly.Data.Fold as Fold
13+>>> import qualified Streamly.Data.Stream as Stream
14+-}
core/src/Streamly/Data/Array.hs+82−81
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Data.Array
34-- Copyright : (c) 2019 Composewell Technologies
@@ -7,84 +8,26 @@
78-- Stability : released
89-- Portability : GHC
910--
10−-- This module provides APIs to create and use unboxed immutable arrays. Once
11−-- created, their contents cannot be modified. Only types that are unboxable
12−-- via the 'Unbox' type class can be stored in these arrays. Note that the
13−-- array memory grows automatically when creating a new array, therefore, an
14−-- array can be created from a variable length stream.
15−--
16−-- See "Streamly.Internal.Data.Array" for more, unreleased APIs. See
17−-- "Streamly.Data.Array.Generic" for arrays that work for boxed types i.e. not
18−-- requiring the 'Unbox' constraint. See "Streamly.Data.MutArray" for arrays
19−-- that can be mutated in-place.
20−--
21−-- = Folding Arrays
22−--
23−-- Convert array to stream, and fold the stream:
24−--
25−-- >>> fold f arr = Stream.unfold Array.reader arr & Stream.fold f
26−-- >>> fold Fold.sum (Array.fromList [1,2,3::Int])
27−-- 6
28−--
29−-- = Transforming Arrays
11+-- Unboxed immutable arrays with streaming interfaces.
3012--
31−-- Convert array to stream, transform, and fold back to array:
13+-- Please refer to "Streamly.Internal.Data.Array" for functions that have not
14+-- yet been released.
3215--
33−-- >>> amap f arr = Stream.unfold Array.reader arr & fmap f & Stream.fold Array.write
34−-- >>> amap (+1) (Array.fromList [1,2,3::Int])
35−-- fromList [2,3,4]
36−--
37−-- = Pinned and Unpinned Arrays
38−--
39−-- The array type can use both pinned and unpinned memory under the hood.
40−-- Currently the array creation APIs create arrays in pinned memory but it will
41−-- change to unpinned in future releases. The change should not affect users
42−-- functionally unless they are directly accessing the internal memory of the
43−-- array via internal APIs. As of now unpinned arrays can be created using
44−-- unreleased APIs.
45−--
46−-- Unpinned arrays have the advantage of allowing automatic defragmentation of
47−-- the memory by GC. Whereas pinned arrays have the advantage of not requiring
48−-- a copy by GC. Normally you would want to use unpinned arrays. However, in
49−-- some cases, for example, for long lived large data storage, and for
50−-- interfacing with the operating system or foreign (non-Haskell) consumers you
51−-- may want to use pinned arrays.
52−--
53−-- = Creating Arrays from Non-IO Streams
54−--
55−-- Array creation folds require 'MonadIO' because they need to sequence effects
56−-- in IO streams. To operate on streams in pure Monads like 'Identity' you can
57−-- morph it to IO monad as follows:
58−--
59−-- The 'MonadIO' based folds can be morphed to 'Identity' stream folds:
60−--
61−-- >>> purely = Fold.morphInner (Identity . unsafePerformIO)
62−-- >>> Stream.fold (purely Array.write) $ Stream.fromList [1,2,3::Int]
63−-- Identity fromList [1,2,3]
64−--
65−-- Since it is a pure stream we can use 'unsafePerformIO' to extract the result
66−-- of fold from IO.
67−--
68−-- Alternatively, 'Identity' streams can be generalized to IO streams:
69−--
70−-- >>> pure = Stream.fromList [1,2,3] :: Stream Identity Int
71−-- >>> generally = Stream.morphInner (return . runIdentity)
72−-- >>> Stream.fold Array.write (generally pure :: Stream IO Int)
73−-- fromList [1,2,3]
74−--
75−-- = Programming Tips
76−--
77−-- This module is designed to be imported qualified:
78−--
79−-- >>> import qualified Streamly.Data.Array as Array
80−--
81−-- For experimental APIs see "Streamly.Internal.Data.Array".
16+-- For arrays that work on boxed types, not requiring the 'Unbox' constraint,
17+-- please refer to "Streamly.Data.Array.Generic". For arrays that can be
18+-- mutated in-place, please see "Streamly.Data.MutArray".
8219
8320module Streamly.Data.Array
8421 (
85− -- * Imports for Examples
22+ -- * Setup
23+ -- | To execute the code examples provided in this module in ghci, please
24+ -- run the following commands first.
25+ --
8626 -- $setup
8727
28+ -- * Overview
29+ -- $overview
30+
8831 -- * The Array Type
8932 A.Array
9033
@@ -140,17 +83,75 @@ import Streamly.Internal.Data.Array as A hiding (read, readRev)
14083import Streamly.Internal.Data.Unboxed (Unbox (..))
14184import Prelude hiding (read)
14285
143−-- $setup
144−-- >>> :m
145−-- >>> :set -XFlexibleContexts
146−-- >>> import Data.Function ((&))
147−-- >>> import Data.Functor.Identity (Identity(..))
148−-- >>> import Streamly.Data.Array (Array)
149−-- >>> import Streamly.Data.Stream (Stream)
150−-- >>> import System.IO.Unsafe (unsafePerformIO)
86+#include "DocTestDataArray.hs"
87+
88+-- $overview
89+--
90+-- This module provides APIs to create and use unboxed immutable arrays. Once
91+-- created, their contents cannot be modified. Only types that are unboxable
92+-- via the 'Unbox' type class can be stored in these arrays. Note that the
93+-- array memory grows automatically when creating a new array, therefore, an
94+-- array can be created from a variable length stream.
95+--
96+-- == Folding Arrays
97+--
98+-- Convert array to stream, and fold the stream:
99+--
100+-- >>> fold f arr = Stream.unfold Array.reader arr & Stream.fold f
101+-- >>> fold Fold.sum (Array.fromList [1,2,3::Int])
102+-- 6
103+--
104+-- == Transforming Arrays
105+--
106+-- Convert array to stream, transform, and fold back to array:
107+--
108+-- >>> amap f arr = Stream.unfold Array.reader arr & fmap f & Stream.fold Array.write
109+-- >>> amap (+1) (Array.fromList [1,2,3::Int])
110+-- fromList [2,3,4]
111+--
112+-- == Pinned and Unpinned Arrays
113+--
114+-- The array type can use both pinned and unpinned memory under the hood.
115+-- Currently the array creation APIs create arrays in pinned memory but it will
116+-- change to unpinned in future releases. The change should not affect users
117+-- functionally unless they are directly accessing the internal memory of the
118+-- array via internal APIs. As of now unpinned arrays can be created using
119+-- unreleased APIs.
120+--
121+-- Unpinned arrays have the advantage of allowing automatic defragmentation of
122+-- the memory by GC. Whereas pinned arrays have the advantage of not requiring
123+-- a copy by GC. Normally you would want to use unpinned arrays. However, in
124+-- some cases, for example, for long lived large data storage, and for
125+-- interfacing with the operating system or foreign (non-Haskell) consumers you
126+-- may want to use pinned arrays.
127+--
128+-- == Creating Arrays from Non-IO Streams
129+--
130+-- Array creation folds require 'MonadIO' because they need to sequence effects
131+-- in IO streams. To operate on streams in pure Monads like 'Identity' you can
132+-- morph it to IO monad as follows:
133+--
134+-- The 'MonadIO' based folds can be morphed to 'Identity' stream folds:
135+--
136+-- >>> purely = Fold.morphInner (Identity . unsafePerformIO)
137+-- >>> Stream.fold (purely Array.write) $ Stream.fromList [1,2,3::Int]
138+-- Identity fromList [1,2,3]
139+--
140+-- Since it is a pure stream we can use 'unsafePerformIO' to extract the result
141+-- of fold from IO.
142+--
143+-- Alternatively, 'Identity' streams can be generalized to IO streams:
144+--
145+-- >>> pure = Stream.fromList [1,2,3] :: Stream Identity Int
146+-- >>> generally = Stream.morphInner (return . runIdentity)
147+-- >>> Stream.fold Array.write (generally pure :: Stream IO Int)
148+-- fromList [1,2,3]
149+--
150+-- == Programming Tips
151+--
152+-- This module is designed to be imported qualified:
153+--
151154-- >>> import qualified Streamly.Data.Array as Array
152−-- >>> import qualified Streamly.Data.Fold as Fold
153−-- >>> import qualified Streamly.Data.Stream as Stream
154155
155156-- | Same as 'reader'
156157--
core/src/Streamly/Internal/Data/Array.hs+33−21
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Internal.Data.Array
34-- Copyright : (c) 2019 Composewell Technologies
@@ -7,29 +8,15 @@
78-- Stability : experimental
89-- Portability : GHC
910--
10−-- To summarize:
11−--
12−-- * Arrays are finite and fixed in size
13−-- * provide /O(1)/ access to elements
14−-- * store only data and not functions
15−-- * provide efficient IO interfacing
16−--
17−-- 'Foldable' instance is not provided because the implementation would be much
18−-- less efficient compared to folding via streams. 'Semigroup' and 'Monoid'
19−-- instances should be used with care; concatenating arrays using binary
20−-- operations can be highly inefficient. Instead, use
21−-- 'Streamly.Internal.Data.Stream.Chunked.toArray' to concatenate N
22−-- arrays at once.
23−--
24−-- Each array is one pointer visible to the GC. Too many small arrays (e.g.
25−-- single byte) are only as good as holding those elements in a Haskell list.
26−-- However, small arrays can be compacted into large ones to reduce the
27−-- overhead. To hold 32GB memory in 32k sized buffers we need 1 million arrays
28−-- if we use one array for each chunk. This is still significant to add
29−-- pressure to GC.
30−
3111module Streamly.Internal.Data.Array
3212 (
13+ -- * Setup
14+ -- $setup
15+
16+ -- * Design Notes
17+ -- $design
18+
19+ -- * The Array Type
3320 Array
3421
3522 -- * Construction
@@ -155,6 +142,31 @@ import qualified Streamly.Internal.Data.Stream.StreamD as D
155142import qualified Streamly.Internal.Data.Stream.StreamD as Stream
156143import qualified Streamly.Internal.Data.Unfold as Unfold
157144
145+#include "DocTestDataArray.hs"
146+
147+-- $design
148+--
149+-- To summarize:
150+--
151+-- * Arrays are finite and fixed in size
152+-- * provide /O(1)/ access to elements
153+-- * store only data and not functions
154+-- * provide efficient IO interfacing
155+--
156+-- 'Foldable' instance is not provided because the implementation would be much
157+-- less efficient compared to folding via streams. 'Semigroup' and 'Monoid'
158+-- instances should be used with care; concatenating arrays using binary
159+-- operations can be highly inefficient. Instead, use
160+-- 'Streamly.Internal.Data.Stream.Chunked.toArray' to concatenate N
161+-- arrays at once.
162+--
163+-- Each array is one pointer visible to the GC. Too many small arrays (e.g.
164+-- single byte) are only as good as holding those elements in a Haskell list.
165+-- However, small arrays can be compacted into large ones to reduce the
166+-- overhead. To hold 32GB memory in 32k sized buffers we need 1 million arrays
167+-- if we use one array for each chunk. This is still significant to add
168+-- pressure to GC.
169+
158170-------------------------------------------------------------------------------
159171-- Construction
160172-------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Array/Type.hs+2−7
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Internal.Data.Array.Type
34-- Copyright : (c) 2020 Composewell Technologies
@@ -107,13 +108,7 @@ import qualified Text.ParserCombinators.ReadPrec as ReadPrec
107108
108109import Streamly.Internal.System.IO (unsafeInlineIO, defaultChunkSize)
109110
110−--
111−-- $setup
112−-- >>> :m
113−-- >>> :set -XMagicHash
114−-- >>> import Prelude hiding (length, foldr, read, unlines, splitAt)
115−-- >>> import Streamly.Data.Stream as Stream
116−-- >>> import Streamly.Internal.Data.Array as Array
111+#include "DocTestDataArray.hs"
117112
118113-------------------------------------------------------------------------------
119114-- Array Data Type
core/src/Streamly/Internal/Data/Stream/StreamD/Generate.hs+1
@@ -1195,6 +1195,7 @@ fromPtrN n = take n . fromPtr
11951195-- | Read bytes from an 'Addr#' until a 0 byte is encountered, the 0 byte is
11961196-- not included in the stream.
11971197--
1198+-- >>> :set -XMagicHash
11981199-- >>> fromByteStr# addr = Stream.takeWhile (/= 0) $ Stream.fromPtr $ Ptr addr
11991200--
12001201-- /Unsafe:/ The caller is responsible for safe addressing.
core/streamly-core.cabal+1−1
@@ -38,13 +38,13 @@ extra-source-files:
3838 configure.ac
3939
4040 -- doctest include files
41+ src/DocTestDataArray.hs
4142 src/DocTestDataFold.hs
4243
4344 -- This is duplicated
4445 src/Streamly/Internal/Data/Array/ArrayMacros.h
4546 src/assert.hs
4647 src/inline.hs
47− src/DocTestDataArray.hs
4848
4949 src/Streamly/Internal/Data/Time/Clock/config-clock.h
5050 src/config.h.in