【发布时间】:2018-09-05 07:08:19
【问题描述】:
为了生成 x86 汇编代码,我定义了一个名为 X86 的自定义类型:
data X86 a = X86 { code :: String, counter :: Integer, value :: (X86 a -> a) }
这种类型在 do-notation 中使用,如下所示。这使得编写用于生成 if 语句、for 循环等的模板变得很容易......
generateCode :: X86 ()
generateCode = do
label1 <- allocateUniqueLabel
label2 <- allocateUniqueLabel
jmp label1
label label1
jmp label2
label label2
指令定义如下:
jmp :: String -> X86 ()
jmp l = X86 { code = "jmp " ++ l ++ ";\n", counter = 0, value = const () }
label :: String -> X86 ()
label l = X86 { code = l ++ ":\n", counter = 0, value = const () }
完成的汇编文件打印如下:
printAsm :: X86 a -> String
printAsm X86{code=code} = code
main = do
putStrLn (printAsm generateCode)
我以以下方式实现了X86 monad。本质上,序列运算符按顺序连接汇编代码块并确保计数器递增。
instance Monad X86 where
x >> y = X86 { code = code x ++ code y, counter = counter x + counter y, value = value y }
x >>= f = x >> y
where y = f (value x x)
问题是标签没有正确递增,所以它们不是唯一的!以下是输出:
jmp Label1;
Label1:
jmp Label1;
Label1:
我希望输出对每个标签都有一个唯一的值:
jmp Label1;
Label1:
jmp Label2;
Label2:
为了完成这个例子,这里是allocatedUniqueLabel函数的实现:
allocateUniqueId :: X86 Integer
allocateUniqueId = X86 { code = "", counter = 1, value = counter }
allocateUniqueLabel :: X86 String
allocateUniqueLabel = do
id <- allocateUniqueId
return ("Label" ++ show id)
如何修复我的 X86 monad 以便标签是唯一的?
这是我尝试过的:
- 递增全局计数器。 => Haskell 不安全地允许 IO monad 之外的全局状态。
- 使用
Statemonad。 => 我研究了一些示例,但不明白如何将它们集成到我现有的X86monad 中。 - 跟踪单子外的计数器。 => 我宁愿计数器在“幕后”更新;否则,很多不使用标签的代码模板将需要手动传播计数器。
【问题讨论】:
-
您只是为了方便使用
Monad类还是有某种合法的实例? -
@Li-yaoXia 主要用于通过do-notation创建DSL。该实例是否合法,我不完全确定,但它一直在成功运行,直到需要唯一标签为止。
-
好吧,我不确定我是否遗漏了什么,但
X86实际上甚至不是Functor(Monad必须是)。 -
您可以尝试自己实现 State,以了解更多信息。基本上,它的
counter的类型应该是Integer -> Integer -
一个小建议:我会为
allocateUniqueLabel的结果创建一个专用的Label类型。这将使您的代码更安全一些,确保您只跳转到标签,而不是任意字符串。
标签: haskell monads state-monad do-notation