Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 哈斯克尔的祖父悖论_Haskell - Fatal编程技术网

Haskell 哈斯克尔的祖父悖论

Haskell 哈斯克尔的祖父悖论,haskell,Haskell,我正在尝试为我用Haskell编写的编译器编写重命名程序 重命名器扫描AST以查找符号def,并将其输入符号表,然后使用符号,通过查找符号表来解析符号def 在这种语言中,可以在defs之前或之后使用,因此似乎需要一个2遍策略;一次用于查找所有def并构建符号表,另一次用于解析所有用法 然而,由于Haskell很懒(和我一样),我想我可以在实际构建之前,先打个结,然后将最终的符号表传递给重命名器。这很好,只要我保证实际建造它。在命令式编程语言中,这就像及时发送消息一样。这在Haskell中确实有

我正在尝试为我用Haskell编写的编译器编写重命名程序

重命名器扫描AST以查找符号def,并将其输入符号表,然后使用符号,通过查找符号表来解析符号def

在这种语言中,可以在defs之前或之后使用,因此似乎需要一个2遍策略;一次用于查找所有def并构建符号表,另一次用于解析所有用法

然而,由于Haskell很懒(和我一样),我想我可以在实际构建之前,先打个结,然后将最终的符号表传递给重命名器。这很好,只要我保证实际建造它。在命令式编程语言中,这就像及时发送消息一样。这在Haskell中确实有效,但必须注意不要引入时间悖论

下面是一个简单的例子:

module Main where

import Control.Monad.Error
import Control.Monad.RWS
import Data.Maybe ( catMaybes )
import qualified Data.Map as Map
import Data.Map ( Map )

type Symtab = Map String Int

type RenameM = ErrorT String (RWS Symtab String Symtab)

data Cmd = Def String Int
         | Use String

renameM :: [Cmd] -> RenameM [(String, Int)]
renameM = liftM catMaybes . mapM rename1M

rename1M :: Cmd -> RenameM (Maybe (String, Int))
rename1M (Def name value) = do
  modify $ \symtab -> Map.insert name value symtab
  return Nothing
rename1M (Use name) = return . liftM ((,) name) . Map.lookup name =<< ask
--rename1M (Use name) =
--  maybe (return Nothing) (return . Just . (,) name) . Map.lookup name =<< ask
--rename1M (Use name) =
--  maybe (throwError $ "Cannot locate " ++ name) (return . Just . (,) name) . Map.lookup name =<< ask

rename :: [Cmd] -> IO ()
rename cmds = do
  let (result, symtab, log) = runRWS (runErrorT $ renameM cmds) symtab Map.empty
  print result

main :: IO ()
main = do
  rename [ Use "foo"
         , Def "bar" 2
         , Use "bar"
         , Def "foo" 1
         ]
运行符号表存储在
RWS
MonadState
中,最终符号表存储在
MonadReader

在上面的示例中,我有3个版本的
rename1M
用于
Use
s(2个被注释掉)。在第一种形式中,它运行良好

如果注释掉第一个
重命名1M Use
,并取消注释第二个,则程序不会终止。然而,在精神上,它与第一种形式没有什么不同。不同之处在于它有两个
return
s,而不是一个,因此从
Map.lookup
返回的
可能是
。必须计算lookup
以查看采取哪条路径

第三种形式是我真正想要的。如果找不到符号,我想抛出一个错误。但这个版本也没有终止。在这里,时间悖论是显而易见的;关于符号是否在表中的决定可能会影响它是否在表中


所以,我的问题是,有没有一种优雅的方式来做第三个版本所做的事情(抛出错误),而不陷入悖论?在不允许查找更改路径的情况下,在
MonadWriter
上发送错误?两次通过?

我没有一个经过深思熟虑的答案,只有一个快速的想法。您在AST上的单次传递将获取所有Def并生成一个(映射符号u),我想知道同一个AST传递是否可以获得所有使用并生成一个(集合符号)以及延迟查找


之后,您可以非常安全地将地图键中的符号与集合中的符号进行比较。如果集合中有任何不在地图中的内容,则可以报告所有这些符号都是错误。如果集合中没有任何定义符号,则可以警告未使用的符号。

发生错误时,您真的必须中断执行吗?另一种方法是记录错误。打结后,可以检查错误列表是否为空。我过去也采取过这种方法

-- I've wrapped a writer in a writer transformer.  You'll probably want to implement it differently to avoid ambiguity
-- related to writer methods.
type RenameM = WriterT [RenameError] (RWS Symtab String Symtab)

rename1M (Use name) = do
  symtab_entry <- asks (Map.lookup name)
  -- Write a list of zero or more errors.  Evaluation of the list is not forced until all processing is done.
  tell $ if isJust symtab_entry then [] else missingSymbol name
  return $ Just (name, fromMaybe (error "lookup failed") symtab_entry)

rename cmds = do
  let ((result, errors), symtab, log) = runRWS (runWriterT $ renameM cmds) symtab Map.empty
  -- After tying the knot, check for errors
  if null errors then print result else print errors
——我将一个writer包装在writer transformer中。您可能希望以不同的方式实现它,以避免歧义
--与写作方法有关。
键入RenameM=WriterT[RenameError](RWS Symtab String Symtab)
重命名1M(使用名称)=do

symtab_入口非常漂亮!这也解决了我在名称解析失败时如何重写AST的另一个问题。您只需将Def替换为
(错误“查找失败”)
-- I've wrapped a writer in a writer transformer.  You'll probably want to implement it differently to avoid ambiguity
-- related to writer methods.
type RenameM = WriterT [RenameError] (RWS Symtab String Symtab)

rename1M (Use name) = do
  symtab_entry <- asks (Map.lookup name)
  -- Write a list of zero or more errors.  Evaluation of the list is not forced until all processing is done.
  tell $ if isJust symtab_entry then [] else missingSymbol name
  return $ Just (name, fromMaybe (error "lookup failed") symtab_entry)

rename cmds = do
  let ((result, errors), symtab, log) = runRWS (runWriterT $ renameM cmds) symtab Map.empty
  -- After tying the knot, check for errors
  if null errors then print result else print errors