编写 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#para

这让我踏上了一段非常有趣的旅程,我仍在继续。我很确定答案在于创建一个新功能,将Lens.paraOf plate的功能与Lens.contexts的功能合并。至少我现在知道这个问题,并且更多地了解上下文和递归方案。我建议任何有兴趣编写此函数的人都应该查看这些函数的来源。

所以,为了回答这个问题,递归中的错误在于我使用 fmap(<$>)将每个透镜映射到结构的每个子镜头上。这意味着每个子树,不仅仅是将递归引入树的特定部分,而是将完全递归获取到树的每个部分。

正确的实现会考虑到这一点。