Skip to content

Commit 75c5365

Browse files
meooow25andrewthad
authored andcommitted
Make fromListN functions good consumers
...in terms of list fusion.
1 parent 8260d8f commit 75c5365

File tree

4 files changed

+37
-20
lines changed

4 files changed

+37
-20
lines changed

Data/Primitive/Array.hs

+15-5
Original file line numberDiff line numberDiff line change
@@ -586,18 +586,28 @@ mapArray' f a =
586586

587587
-- | Create an array from a list of a known length. If the length
588588
-- of the list does not match the given length, this throws an exception.
589+
590+
-- Note [fromListN]
591+
-- ~~~~~~~~~~~~~~~~
592+
-- We want arrayFromListN to be a "good consumer" in list fusion, so we define
593+
-- the function using foldr and inline it to help fire fusion rules.
594+
-- If fusion occurs with a "good producer", it may reduce to a fold on some
595+
-- structure. In certain cases (such as for Data.Set) GHC is not be able to
596+
-- optimize the index to an unboxed Int# (see GHC #24628), so we explicitly use
597+
-- an Int# here.
589598
arrayFromListN :: Int -> [a] -> Array a
599+
{-# INLINE arrayFromListN #-}
590600
arrayFromListN n l =
591601
createArray n (die "fromListN" "uninitialized element") $ \sma ->
592-
let go !ix [] = if ix == n
602+
let z ix# = if I# ix# == n
593603
then return ()
594604
else die "fromListN" "list length less than specified size"
595-
go !ix (x : xs) = if ix < n
605+
f x k = GHC.Exts.oneShot $ \ix# -> if I# ix# < n
596606
then do
597-
writeArray sma ix x
598-
go (ix+1) xs
607+
writeArray sma (I# ix#) x
608+
k (ix# +# 1#)
599609
else die "fromListN" "list length greater than specified size"
600-
in go 0 l
610+
in foldr f z l 0#
601611

602612
-- | Create an array from a list.
603613
arrayFromList :: [a] -> Array a

Data/Primitive/ByteArray.hs

+8-5
Original file line numberDiff line numberDiff line change
@@ -378,17 +378,20 @@ byteArrayFromList xs = byteArrayFromListN (length xs) xs
378378

379379
-- | Create a 'ByteArray' from a list of a known length. If the length
380380
-- of the list does not match the given length, this throws an exception.
381+
382+
-- See Note [fromListN] in Data.Primitive.Array
381383
byteArrayFromListN :: forall a. Prim a => Int -> [a] -> ByteArray
384+
{-# INLINE byteArrayFromListN #-}
382385
byteArrayFromListN n ys = createByteArray (n * sizeOfType @a) $ \marr ->
383-
let go !ix [] = if ix == n
386+
let z ix# = if I# ix# == n
384387
then return ()
385388
else die "byteArrayFromListN" "list length less than specified size"
386-
go !ix (x : xs) = if ix < n
389+
f x k = GHC.Exts.oneShot $ \ix# -> if I# ix# < n
387390
then do
388-
writeByteArray marr ix x
389-
go (ix + 1) xs
391+
writeByteArray marr (I# ix#) x
392+
k (ix# +# 1#)
390393
else die "byteArrayFromListN" "list length greater than specified size"
391-
in go 0 ys
394+
in foldr f z ys 0#
392395

393396
unI# :: Int -> Int#
394397
unI# (I# n#) = n#

Data/Primitive/PrimArray.hs

+8-5
Original file line numberDiff line numberDiff line change
@@ -234,17 +234,20 @@ primArrayFromList vs = primArrayFromListN (L.length vs) vs
234234

235235
-- | Create a 'PrimArray' from a list of a known length. If the length
236236
-- of the list does not match the given length, this throws an exception.
237+
238+
-- See Note [fromListN] in Data.Primitive.Array
237239
primArrayFromListN :: forall a. Prim a => Int -> [a] -> PrimArray a
240+
{-# INLINE primArrayFromListN #-}
238241
primArrayFromListN len vs = createPrimArray len $ \arr ->
239-
let go [] !ix = if ix == len
242+
let z ix# = if I# ix# == len
240243
then return ()
241244
else die "fromListN" "list length less than specified size"
242-
go (a : as) !ix = if ix < len
245+
f a k = GHC.Exts.oneShot $ \ix# -> if I# ix# < len
243246
then do
244-
writePrimArray arr ix a
245-
go as (ix + 1)
247+
writePrimArray arr (I# ix#) a
248+
k (ix# +# 1#)
246249
else die "fromListN" "list length greater than specified size"
247-
in go vs 0
250+
in foldr f z vs 0#
248251

249252
-- | Convert a 'PrimArray' to a list.
250253
{-# INLINE primArrayToList #-}

Data/Primitive/SmallArray.hs

+6-5
Original file line numberDiff line numberDiff line change
@@ -924,18 +924,19 @@ instance (Typeable s, Typeable a) => Data (SmallMutableArray s a) where
924924
-- | Create a 'SmallArray' from a list of a known length. If the length
925925
-- of the list does not match the given length, this throws an exception.
926926
smallArrayFromListN :: Int -> [a] -> SmallArray a
927+
{-# INLINE smallArrayFromListN #-}
927928
smallArrayFromListN n l =
928929
createSmallArray n
929930
(die "smallArrayFromListN" "uninitialized element") $ \sma ->
930-
let go !ix [] = if ix == n
931+
let z ix# = if I# ix# == n
931932
then return ()
932933
else die "smallArrayFromListN" "list length less than specified size"
933-
go !ix (x : xs) = if ix < n
934+
f x k = GHC.Exts.oneShot $ \ix# -> if I# ix# < n
934935
then do
935-
writeSmallArray sma ix x
936-
go (ix + 1) xs
936+
writeSmallArray sma (I# ix#) x
937+
k (ix# +# 1#)
937938
else die "smallArrayFromListN" "list length greater than specified size"
938-
in go 0 l
939+
in foldr f z l 0#
939940

940941
-- | Create a 'SmallArray' from a list.
941942
smallArrayFromList :: [a] -> SmallArray a

0 commit comments

Comments
 (0)