Skip to content

Commit fdcddf8

Browse files
committed
Traverse better
Instead of traversing a list and then converting to an array, be more direct. GHC seems to be able to optimize this better, at least in common cases. Addresses #85.
1 parent a263cc2 commit fdcddf8

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

Data/Primitive/Array.hs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -444,10 +444,33 @@ instance Foldable Array where
444444
{-# INLINE product #-}
445445
#endif
446446

447+
newtype STA a = STA {_runSTA :: forall s. MutableArray# s a -> ST s (Array a)}
448+
449+
runSTA :: Int -> STA a -> Array a
450+
runSTA !sz = \ (STA m) -> runST $ newArray_ sz >>= \ ar -> m (marray# ar)
451+
{-# INLINE runSTA #-}
452+
453+
newArray_ :: Int -> ST s (MutableArray s a)
454+
newArray_ !n = newArray n badTraverseValue
455+
456+
badTraverseValue :: a
457+
badTraverseValue = die "traverse" "bad indexing"
458+
{-# NOINLINE badTraverseValue #-}
459+
447460
instance Traversable Array where
448-
traverse f a =
449-
fromListN (sizeofArray a)
450-
<$> traverse (f . indexArray a) [0 .. sizeofArray a - 1]
461+
traverse f = \ !ary ->
462+
let
463+
!len = sizeofArray ary
464+
go !i
465+
| i == len = pure $ STA $ \mary -> unsafeFreezeArray (MutableArray mary)
466+
| (# x #) <- indexArray## ary i
467+
= liftA2 (\b (STA m) -> STA $ \mary ->
468+
writeArray (MutableArray mary) i b >> m mary)
469+
(f x) (go (i + 1))
470+
in if len == 0
471+
then pure emptyArray
472+
else runSTA len <$> go 0
473+
{-# INLINE traverse #-}
451474

452475
#if MIN_VERSION_base(4,7,0)
453476
instance Exts.IsList (Array a) where

Data/Primitive/SmallArray.hs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,9 +525,35 @@ instance Foldable SmallArray where
525525
{-# INLINE product #-}
526526
#endif
527527

528+
newtype STA a = STA {_runSTA :: forall s. SmallMutableArray# s a -> ST s (SmallArray a)}
529+
530+
runSTA :: Int -> STA a -> SmallArray a
531+
runSTA !sz = \ (STA m) -> runST $ newSmallArray_ sz >>=
532+
\ (SmallMutableArray ar#) -> m ar#
533+
{-# INLINE runSTA #-}
534+
535+
newSmallArray_ :: Int -> ST s (SmallMutableArray s a)
536+
newSmallArray_ !n = newSmallArray n badTraverseValue
537+
538+
badTraverseValue :: a
539+
badTraverseValue = die "traverse" "bad indexing"
540+
{-# NOINLINE badTraverseValue #-}
541+
528542
instance Traversable SmallArray where
529-
traverse f sa = fromListN l <$> traverse (f . indexSmallArray sa) [0..l-1]
530-
where l = length sa
543+
traverse f = \ !ary ->
544+
let
545+
!len = sizeofSmallArray ary
546+
go !i
547+
| i == len
548+
= pure $ STA $ \mary -> unsafeFreezeSmallArray (SmallMutableArray mary)
549+
| (# x #) <- indexSmallArray## ary i
550+
= liftA2 (\b (STA m) -> STA $ \mary ->
551+
writeSmallArray (SmallMutableArray mary) i b >> m mary)
552+
(f x) (go (i + 1))
553+
in if len == 0
554+
then pure emptySmallArray
555+
else runSTA len <$> go 0
556+
{-# INLINE traverse #-}
531557

532558
instance Functor SmallArray where
533559
fmap f sa = createSmallArray (length sa) (die "fmap" "impossible") $ \smb ->

0 commit comments

Comments
 (0)