Проблема рекурсии при написании версии Lens.para с поддержкой "Pretext"

Я пытался построить замену Lens.para это обеспечивает линзовые контексты для функции para, когда она выполняет свою работу. Однако, я, кажется, где-то допустил ошибку в рекурсии.

Согласно моему пониманию, Lens.para является функцией параморфизма значений в рекурсивном алгебраическом типе данных То есть он использует plated и принимает функцию, которая анализирует список опций, которые будут использоваться для обхода "самоподобного синтаксического пространства" фрагмента данных, в то же время делая свой контекст данных обхода доступным для функции во время ее работы. Его тип Lens.Plated a => (a -> [r] -> r) -> a -> r, где [r] список значений контекста данных, и a является типом каждого значения, которое указывает, как "изучить" последующие уровни.

Чрезвычайно простой пример типа игрушек, который я использую для проверки концепции, выглядит следующим образом:

data EExp a = ELit a | EAdd (EExp a) (EExp a) deriving (Show, Eq)

Итак, вот мой код, включая как существующую рабочую версию showOptions и моя новая версия этого, showOptions' который использует мой обычай Lens.para который называется paraApp, Разница в том, что этот проходит Pretext вместе с данными, как это делает свою работу, чтобы позже я мог настроить свой код, чтобы использовать это Pretext настроить исходную структуру данных, если это необходимо.

{-# LANGUAGE RankNTypes, TemplateHaskell, ExplicitForAll, DeriveDataTypeable, StandaloneDeriving #-}

module StepThree where

import qualified Control.Lens as Lens
import qualified Data.Data as DD
import qualified Data.Data.Lens as DDL
import qualified Data.Maybe as DM
import qualified Data.List as DL
import Text.Read (readMaybe)
import StepThreeGrammar (EExp(..), pretty, run)

import Control.Comonad.Store.Class (pos, peek, ComonadStore)
import Control.Lens.Internal.Context (Pretext(..), sell)

import qualified Language.Haskell.Interpreter as I
import Language.Haskell.Interpreter (Interpreter, GhcError(..), InterpreterError(..))

instance DD.Data a => Lens.Plated (EExp a)
deriving instance DD.Data a => DD.Data (EExp a)

eg3' :: EExp Int
eg3' = EAdd (EAdd (EAdd (EAdd (EAdd (ELit 11) (ELit 9)) (ELit 3)) (ELit 1)) (ELit 5)) (ELit 0)

showOptions :: (Lens.Plated a, Show a) => (a -> String) -> a -> [String]
showOptions showFn = Lens.para $ \a xs ->
    let
      sa = showFn a
      (_,is) = DL.mapAccumL mapAccumFn (0, sa) xs
    in
      sa : concat is
  where
    mapAccumFn (n, acc) x =
      let
        i = pfxIndex (head x) acc
      in
        ( (n+i+length (head x)
          , drop (i+length (head x)) acc)
        , map (replicate (n+i) ' ' ++) x)


showOptions' :: (Lens.Plated a, Show a) => (a -> String) -> a -> [String]
showOptions' showFn = paraApp $ \(a, ctx) xs ->
    let
      sa = showFn a
      (_, is) = DL.mapAccumL mapAccumFn (0, sa) xs
    in
      sa : concat is
  where
    mapAccumFn (n, acc) x =
      let
        i = pfxIndex (head x) acc
      in
        ( (n+i+length (head x)
          , drop (i+length (head x)) acc)
        , map (replicate (n+i) ' ' ++) x)

paraApp :: Lens.Plated a => ((a, Pretext (->) a a a) -> [r] -> r) -> a -> r
paraApp f x = go id (x, makePretextFocussingOnSelfFor x)
  where
    go p a =
      let p' = Lens.plate . p
          holes = Lens.holesOf p' x
      in f a (go p' <$> (map (\c -> (pos c, c)) holes))
    makePretextFocussingOnSelfFor x = Pretext ($ x)


pfxIndex :: Eq a => [a] -> [a] -> Int
pfxIndex x y = maybe 0 id (DL.findIndex (x `DL.isPrefixOf`) (DL.tails y))

Если я пойду в GHCi и выполнить следующий код, он обеспечивает предполагаемый вывод:

*Main EditorTest StepThree Control.Lens> mapM_ putStrLn $ StepThree.showOptions show eg3'
EAdd (EAdd (EAdd (EAdd (EAdd (ELit 11) (ELit 9)) (ELit 3)) (ELit 1)) (ELit 5)) (ELit 0)
      EAdd (EAdd (EAdd (EAdd (ELit 11) (ELit 9)) (ELit 3)) (ELit 1)) (ELit 5)
            EAdd (EAdd (EAdd (ELit 11) (ELit 9)) (ELit 3)) (ELit 1)
                  EAdd (EAdd (ELit 11) (ELit 9)) (ELit 3)
                        EAdd (ELit 11) (ELit 9)
                              ELit 11
                                        ELit 9
                                                  ELit 3
                                                            ELit 1
                                                                      ELit 5
                                                                                ELit 0

Что хорошо для случая, когда я не хочу ничего делать с контекстом (скажем, обновление определенного фрагмента исходного значения)

Поэтому, когда я пытаюсь выполнить функцию замены, происходит следующее (оно должно совпадать с приведенным выше):

    *Main EditorTest StepThree Control.Lens> mapM_ putStrLn $ StepThree.showOptions' show eg3'
    EAdd (EAdd (EAdd (EAdd (EAdd (ELit 11) (ELit 9)) (ELit 3)) (ELit 1)) (ELit 5)) (ELit 0)
          EAdd (EAdd (EAdd (EAdd (ELit 11) (ELit 9)) (ELit 3)) (ELit 1)) (ELit 5)
                EAdd (EAdd (EAdd (ELit 11) (ELit 9)) (ELit 3)) (ELit 1)
                      EAdd (EAdd (ELit 11) (ELit 9)) (ELit 3)
                            EAdd (ELit 11) (ELit 9)
                                  ELit 11
                                            ELit 9
                                                      ELit 3
                                                      ELit 11
                                                             ELit 9
                                                                ELit 1
                                                                EAdd (ELit 11) (ELit 9)
                                                                      ELit 11
                                                                                ELit 9
                                                                                       ELit 3
                                                                                       ELit 11
                                                                                              ELit 9
                                                                          ELit 5
                                                                          EAdd (EAdd (ELit 11) (ELit 9)) (ELit 3)
                                                                                EAdd (ELit 11) (ELit 9)
                                                                                      ELit 11
                                                                                                ELit 9
                                                                                                          ELit 3
                                                                                                          ELit 11
                                                                                                                 ELit 9
                                                                                                                 ELit 1
                                                                                                                 EAdd (ELit 11) (ELit 9)
                                                                                                                       ELit 11
                                                                                                                                 ELit 9
                                                                                                                                        ELit 3
                                                                                                                                        ELit 11
                                                                                                                                               ELit 9
                                                                                    ELit 0
                                                                                    EAdd (EAdd (EAdd (ELit 11) (ELit 9)) (ELit 3)) (ELit 1)
                                                                                          EAdd (EAdd (ELit 11) (ELit 9)) (ELit 3)
                                                                                                EAdd (ELit 11) (ELit 9)
                                                                                                      ELit 11
                                                                                                                ELit 9
                                                                                                                          ELit 3
                                                                                                                          ELit 11
                                                                                                                                 ELit 9
                                                                                                                                    ELit 1
                                                                                                                                    EAdd (ELit 11) (ELit 9)
                                                                                                                                          ELit 11
                                                                                                                                                    ELit 9
                                                                                                                                                           ELit 3
                                                                                                                                                           ELit 11
                                                                                                                                                                  ELit 9
                                                                                                                                           ELit 5
                                                                                                                                           EAdd (EAdd (ELit 11) (ELit 9)) (ELit 3)
                                                                                                                                                 EAdd (ELit 11) (ELit 9)
                                                                                                                                                       ELit 11
                                                                                                                                                                 ELit 9
                                                                                                                                                                           ELit 3
                                                                                                                                                                           ELit 11
                                                                                                                                                                                  ELit 9
                                                                                                                                                                                  ELit 1
                                                                                                                                                                                  EAdd (ELit 11) (ELit 9)
                                                                                                                                                                                        ELit 11
                                                                                                                                                                                                  ELit 9
                                                                                                                                                                                                         ELit 3
                                                                                                                                                                                                         ELit 11
                                                                                                                                                                                                                ELit 9

Очевидно, у меня где-то неправильная рекурсия, но я не могу разобраться. Как всегда, любая помощь будет принята с благодарностью.

Если вы не знакомы с первоначальным определением Lens.para ее можно найти по адресу https://hackage.haskell.org/package/lens-4.15.2/docs/src/Control.Lens.Plated.html

1 ответ

Решение

Это взяло меня в очень интересное путешествие, в котором я все еще нахожусь. Я уверен, что ответ заключается в создании новой функции, которая объединяет функциональность Lens.paraOf plate с этим из Lens.contexts, По крайней мере, я знаю проблему сейчас и намного больше о контексте и рекурсивных схемах. Я бы посоветовал всем, кто заинтересован в написании этой функции, обратиться к ее источнику.

Итак, чтобы ответить на вопрос, ошибка в рекурсии заключается в том, что я использую fmap (<$>) отобразить каждую верхнюю линзу на каждого ребенка в нижней части структуры. Это означает, что каждое поддерево, а не только получает рекурсию в свою конкретную часть дерева, получает полную рекурсию в каждую часть дерева.

Правильная реализация будет учитывать это.

Другие вопросы по тегам