Как я могу заставить мое правило стрелять?

Я работаю над правилами слияния списков для fromListN в Data.Primitive.Array, и я немного застрял. Функция выглядит так:

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 чтобы не писать, но это... не очень красивое зрелище.

Ответ 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 и не fillArray_go n mary ее. Я до сих пор не понимаю, почему это происходит.