【问题标题】:How to use the Reader Monad when traversing an Abstract Syntax Tree遍历抽象语法树时如何使用 Reader Monad
【发布时间】:2013-03-06 03:24:24
【问题描述】:

我在 Haskell 中有一个由 Parsec 制成的抽象语法树。我希望能够在遍历它的同时查询它的结构,以便将其转换为中间代码。例如,我需要知道我的 AST 的任何给定函数需要多少参数才能进行这种翻译。我目前正在做的是将 AST 传递给每个函数,这样我就可以在需要进行查找时调用它,并且我在另一个文件中有辅助函数来为我进行查找。这污染了我的类型签名。尤其是当我开始添加更多的东西时,比如累加器。

与其将 AST 传递给我听说过的每个函数,这对于 Reader Monad(对于不改变的状态,AST)和 State Monad(对于确实改变的状态,累加器)。

如何从 IO monad (gulp) 中取出 ast 并在 Reader Monad 中使用它来进行全局查找?

main = do
  putStrLn "Please enter the name of your jack file (i.e. Main)"
  fileName <- getLine
  file <- readFile (fileName++".jack")
  let ast = parseString file
  writeFile (fileName++".xml") (toClass ast) --I need to query this globally
  putStrLn $  "Completed Parsing, " ++ fileName ++ ".vm created..."

type VM = String

toClass :: Jack -> VM
toClass c = case c of
   (Class ident decs) ->
     toDecs decs 

toDecs ::[Declaration] -> VM -- I don't want to add the ast in every function arg...
toDecs [] = ""
toDecs (x:xs) = case x of
  (SubDec keyword typ subname params subbody) ->
    case keyword of
      "constructor" -> --use the above ast to query the # of local variables here...
    toSubBody subbody ++
    toDecs xs
  otherwise -> []

Reader Monad 进度更新: 我把上面的例子变成了这样的:(见下文)。但是现在我想知道由于所有这些字符串输出的积累,我是否也应该使用作家 Monad?如果是这样,我应该如何组合这两者? ReaderT 应该封装作家吗?或相反亦然?我应该创建一个只接受 Reader 和 Writer 而不尝试将它们组合为 Monad Transformer 的类型吗?

main = do
  putStrLn "Please enter the name of your jack file (i.e. Main)"
  fileName <- getLine
  file <- readFile (fileName++".jack")
  writeFile (fileName++".xml")  (runReader toClass $ parseString file)
  putStrLn $  "Completed Parsing, " ++ fileName ++ ".xml created..."

toClass = do   
  env <- ask
  case env of Class ident decs -> return $ toDecs decs env

toDecs [] = return ""
toDecs ((SubDec keyword typ subname params subbody):xs) = do
  env <- ask
  res <- (case keyword of
            "method" -> do return "push this 0\n"
            "constructor" -> do return "pop pointer 0\nMemory.alloc 1\n"
            otherwise -> do return "")
  return $ res ++ toSubBody subbody env ++ toDecs xs env
toDecs (_:xs) = do
  decs <- ask
  return $ toDecs xs decs

toSubBody (SubBodyStatement states) = do
    return $ toStatement states
toSubBody (SubBody _ states) = do
    return $ toStatement states

http://hpaste.org/83595 --用于声明

【问题讨论】:

  • 你找到了一个非常合适的机会来使用 reader monad。确实,您不想一直绕过 AST。但是,在您的示例代码中,我看不到任何(并且我期望有多个)函数采用 AST。也许您可以粘贴包含较少域详细信息的代码?
  • Tarrasch,一个例子是“构造函数”案例。它的结果需要是:"push pointer" ++ (show $ getFieldCount subname ast) ++ "\n" 我已经在上面的代码中列出了。
  • toDecs 中绑定res,但从不使用它。我不确定那是错误还是错字。另外,toDecs 的类型是什么?在前 2 种情况下,它需要 1 个输入,但在第 3 种情况下,它需要 2 个输入。如果您提供可编译的代码片段(即带有 ClassSubDec、@ 987654330@等...)
  • 克里斯,你是对的。我忘了将 res 添加到返回表达式。我已经添加了我所有声明的 hpaste。 hpaste.org/83595

标签: haskell monads abstract-syntax-tree


【解决方案1】:

如果不了解更多关于 JackDeclaration 类型的信息,很难理解如何将其转换为 Reader monad。如果想法是在 ast :: Jack 对象在范围内时对某物执行“映射”或“折叠”,您可以编写

f :: [Declaration] -> Reader Jack [Something]
f decls = mapM go decls where 
  go :: Declaration -> Reader Jack Something
  go (SubDec keyword typ subname params subbody) =
    case keyword of
      "constructor" -> do
        ast <- ask
        return (doSomething subbody ast)

然后在 ast 的上下文中以 runReader (f decls) ast 执行它。

【讨论】:

  • 感谢您的代码,它对您很有帮助。 Jack 声明是一个大的 AST。基本上很多嵌套列表。 [声明] 只是该 AST 的一个级别。另一个问题。我将如何在“构造函数”-> do 部分中使用 Writer monad。我想通过 AST 并保留一个 [[String]] ,它会在我向下遍历 AST 时累积。
  • 以防万一。 data Jack = Class Identifier [Declaration] deriving (Show);数据声明 = ClassVarDec 关键字类型 [VarName] | SubDec Keyword Type SubName [Params] SubBody deriving (Show)
  • 好吧,如果VarNameParams 包含另一个Jack,你会做什么会有很大的不同。无论哪种情况,您都需要研究 monad 转换器并了解如何构建 WriterT [[String]] (Reader Jack) monad,以便您可以同时使用 asktell。您要查看的软件包是transformersmtl,它们都是相当标准的。 transformers 有点冗长,mtl 利用类型类来减少冗长。
猜你喜欢
  • 2011-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-17
相关资源
最近更新 更多