Haskell 最后是无标记解析和递归一元操作

Haskell 最后是无标记解析和递归一元操作,haskell,encoding,attoparsec,Haskell,Encoding,Attoparsec,我以最终无标记样式从光盘反序列化数据结构。i、 e class SYM repl where a :: repl include :: FilePath -> repl myParser :: SYM r => Parser r 我正在解析的语言具有include指令 我使用的是attoparsec,它不是monad转换器,所以我不能简单地提供类型Loader=FilePath->IO(可能是Text) 我可以用下面的SYM实例编写一个解释器,它解析includ

我以最终无标记样式从光盘反序列化数据结构。i、 e

class SYM repl where
    a :: repl
    include :: FilePath -> repl 

myParser :: SYM r => Parser r
我正在解析的语言具有include指令

我使用的是
attoparsec
,它不是monad转换器,所以我不能简单地提供类型
Loader=FilePath->IO(可能是Text)

我可以用下面的
SYM
实例编写一个解释器,它解析include

instance SYM r => SYM (Loader -> IO (Either String r)) where
    include path loader = 
        maybe (Left "cannot load") (parseOnly myParser) <$> loader path
instance SYM r=>SYM(Loader->IO(任一字符串r)),其中
包含路径加载器=
可能(左“无法加载”)(仅解析myParser)加载程序路径
不幸的是,无法解析包含文件中的包含。当然,我可以两次解析它们来解析下一层。但如果我想对每一个可能的级别都这样做,那么这将导致无限类型

现在我预装了所有的include(在
HashMap
中)并绑定它们,这样我就可以将
FilePath->Maybe Text
传递给解析器并解析其中的include,但这显然不是最优的。(并且include不再是
SYM
的一部分。)

我的问题是,最终无标签风格如何处理这个问题


编辑:我在lpaste上发布了一个完整的示例:

事后看来,这很容易,但通常是这样

只有一个级别的解析如下所示

instance SYM r => SYM (Loader -> IO (Either String r)) where
    token t _ = return . Right $ token t
    include path loader =
        maybe (Left "cannot load") (parseOnly myParser) <$> loader path
关键的一步是,它将向新的
parseOnly
d
SYM
repl提供额外的参数加载器,从而选择正确的实例


完整的代码段位于带注释的lambda粘贴中:。输入“include include token”进行测试
myParser
函数似乎不正确。使用include实例,您将得到
解析器(加载程序->IO(字符串r))
,这就是您想要的吗?什么是
parseOnly
parseOnly
来自ATOPASSEC。是的,我认为这就是解析器的类型。最终编码中的表示应该是这样的。不过,在我更大的示例中,我有一个介于两者之间的新类型。我将举一个简单的例子,我认为您已经找到了解决这个问题的方法:将您的实例类型包装成
newtype
,然后您可以使它们无限递归。理论上,
include
s实际上可能是无限的,因此您必须处理这种可能性。或者,您可以使用免费的monad来处理每一层的分辨率,而不是创建自己的新类型。虽然我通常尽量避免使用免费的单子(而且总是可能的),但我不确定我是否理解你的意思。我会再玩一玩,看看能不能找到什么。
    include path loader =
        maybe (return $ Left "cannot load")
              (either (return ∘ Left) ($ loader) . parseOnly myParser)
              =<< loader path