Monoidal array builder which should have much better performance than pure concatenation because it mutates the underling array.
Please be aware that JS
engines are generaly optimized for push
(snoc
) operation - O(1) vs unshift
(cons
) operation - O(n). We have here characteristic which is opposite to the Data.List
.
Use it for relatively small arrays (length < 10000) otherwise you can get Nothing
from build
... or just a crash (call stack overflow) in the case of unsafeBuild
.
Let me start with imports. This README is a part of the test suite so we need them.
module Test.README where
import Prelude
-- We are working with constant sized arrays
-- here so `unsafeBuild` is actually pretty safe ;-)
import Data.Array ((..))
import Data.Array.Builder (cons, consArray, snoc, snocArray, unsafeBuild, (:>), (+>), (<:), (<+), build)
import Data.Foldable (foldMap)
import Data.Maybe (Maybe(..))
import Data.Monoid as Monoid
import Effect (Effect)
import Test.Assert (assert)
<>
is right associative so it works in practice as follows:
suite ∷ Effect Unit
suite = do
assert $ unsafeBuild (cons 8 <> cons 9 <> cons 10 <> mempty) == [8, 9, 10]
and
assert $ unsafeBuild (snoc 8 <> snoc 9 <> snoc 10 <> mempty) == [10, 9, 8]
I'm not a huge fun of custom operators but in the case of List
or Array
constructors I feel that
they are pretty useful. So we have right (:>
, +>
- cons
, consArray
) and left (<:
, <+
- snoc
,snocArray
)
associative operators provided by the lib which "should" behave as you would expect when mixed:
assert $ unsafeBuild (-3 :> [-2, -1] +> 0 :> mempty <: 1 <+ [2, 3] <: 4) == -3..4
this API works nicely with tools like Monoid.guard
, foldMap
etc. because we have a "pretty" performant Monoid
here:
let
b =
snoc 5
-- | It could be `foldMap snoc [3, 4]` as well
-- | but `snocArray` is a single `push`.
<> Monoid.guard true (snocArray [3, 4])
<> snoc 2
<> foldMap snoc (Just 1)
<> Monoid.guard false (cons 0)
assert (unsafeBuild b == (1..5))
The above example uses consistently more performant snoc*
operations - mixing cons*
, snoc*
and <>
can be tricky sometimes
because we have to think about the order of the actual operations...
So I prefer my fancy operators. Everyones loves their own lovely, clean and consistent operators and...
not like the others ugly, unkown and unreadable operators ;-)
"And now for something completely different..." Let's blow up the stack:
assert $ build (foldMap snoc (1..20000)) == Nothing
Nice!
$ npm test