我正在研究Data.Primitive.Array
中fromListN
的列表融合规则,我有点卡住了。该函数如下所示:
fromListNArray :: Int -> [a] -> Array a
fromListNArray !n l =
createArray n fromListN_too_short $ \mi ->
let go i (x:xs)
| i < n = writeArray mi i x >> go (i+1) xs
| otherwise = fromListN_too_long
go i [] = unless (i == n) fromListN_too_short
in go 0 l
{-# NOINLINE fromListNArray #-}
fromListN_too_short
和fromListN_too_long
只是错误调用。
我的重写规则是
{-# RULES
"fromListNArray/foldr" [~1] forall n xs.
fromListNArray n xs = createArray n fromListN_too_short $ \mary ->
foldr (fillArray_go n mary) (fillArray_stop n) xs 0
"fillArrayN/list" [1] forall n mary xs i.
foldr (fillArray_go n mary) (fillArray_stop n) xs i = fillArrayN n mary xs i
#-}
定义助手
fillArrayN :: Int -> MutableArray s a -> [a] -> Int -> ST s ()
fillArrayN !n !mary xs0 !i0 = go i0 xs0
where
go i (x:xs)
| i < n = writeArray mary i x >> go (i+1) xs
| otherwise = fromListN_too_long
go i [] = unless (i == n) fromListN_too_short
{-# NOINLINE fillArrayN #-}
fillArray_go :: Int
-> MutableArray s a
-> a
-> (Int -> ST s ())
-> Int
-> ST s ()
fillArray_go !n !mary = \x r i ->
if i < n
then writeArray mary i x >> r (i + 1)
else fromListN_too_long
{-# INLINE CONLIKE [0] fillArray_go #-}
fillArray_stop :: Int -> Int -> ST s ()
fillArray_stop !n = \i -> unless (i == n) fromListN_too_short
{-# INLINE [0] fillArray_stop #-}
第一条重写规则似乎没问题。第二个回写规则是问题所在。我似乎永远无法解雇它。有人可以提出建议吗?
注意:我知道我可以直接与build
和augment
融合,以避免回写,但它......不是很好看。
答案 0 :(得分:1)
主要问题似乎是我的错误。在
"fillArrayN/list" [1] forall n mary xs i.
foldr (fillArray_go n mary) (fillArray_stop n) xs i = fillArrayN n mary xs i
foldr
是Data.Foldable.foldr
,这是一种类方法,因此不适用于规则的LHS。解决此问题会使回写规则在简单的情况下起作用。
不幸的是,当fromListNArray
与augment
融合时(通常在将其应用于附加列表时发生),该规则因其他原因无法触发。 GHC为fillArray_go n mary
创建了一个函数,并没有内联它。我仍然不明白为什么会这样。