Haskell 如何使用Supply monad创建生成全局唯一名称的函数? 背景:
我正在做一个代码翻译项目,需要我生成变量名。我生成的所有名称都不应相互重复 我真的很沮丧,因为如果使用Python生成器函数,这将非常简单和优雅 我所尝试的: 我以前的做法是通过递归调用将计数器变量向下传递给我的翻译代码,并在基本上每个函数的返回值中向上传递(可能是递增的)计数器 这真是一团糟:它添加了一个额外的参数来跟踪这些函数中的每一个;更糟糕的是,它迫使我处理混乱的元组返回值,否则我会得到一个简单的一元返回值 在我与Haskell相处的短短时间里,我从未真正精通过monad,但我有一个想法,我可以在Haskell 如何使用Supply monad创建生成全局唯一名称的函数? 背景:,haskell,functional-programming,unique,monads,state-monad,Haskell,Functional Programming,Unique,Monads,State Monad,我正在做一个代码翻译项目,需要我生成变量名。我生成的所有名称都不应相互重复 我真的很沮丧,因为如果使用Python生成器函数,这将非常简单和优雅 我所尝试的: 我以前的做法是通过递归调用将计数器变量向下传递给我的翻译代码,并在基本上每个函数的返回值中向上传递(可能是递增的)计数器 这真是一团糟:它添加了一个额外的参数来跟踪这些函数中的每一个;更糟糕的是,它迫使我处理混乱的元组返回值,否则我会得到一个简单的一元返回值 在我与Haskell相处的短短时间里,我从未真正精通过monad,但我有一个想法
状态
monad上使用包装器来模拟全局计数器变量。三天来,我一直在尝试摸索单子并制作自己的单子,然后尝试修改其他人的单子以生成我所需的值,最后我决定直接使用其他人的高级单子(可能需要一些修改)
我现在的问题是:
我已经将和模块确定为一对,它们可能提供我需要的简单类型的接口。不幸的是,我不知道如何使用它们
尤其是MonadSupply
模块文档提供了这个很好的示例用例:
runSupplyVars x = runSupply x vars
where vars = [replicate k ['a'..'z'] | k <- [1..]] >>= sequence
我曾尝试将大量(小时价值)不同的内容传递给此函数,但没有成功。我还尝试将该函数传递给其他一些函数,看看它们是否会隐式地提供我需要的参数。到目前为止运气不好
问题是:
是否有人可以提供此runSupplyVars
函数的示例用例
有可能用它来做我想做的事吗?我希望有一个可以从程序中的任何地方调用的函数,在每次调用时为我提供不同的变量名或整数。要实际使用
Supply
monad,您应该使用它来构造代码,并在实际需要名称时调用Supply
函数
例如,这将产生一个新的变量名,前缀为var\uu
,只是为了说明如何从供应中获取并使用它:
newVar :: Supply [Char] [Char]
newVar = do
name <- supply
return ("var"_++name)
下面是一个更完整的示例,将所有内容放在一起。请注意使用do
表示法的不同单子-IO
用于main
函数,而Supply[Char]
用于realProgram
以及更大版本中的大部分剩余代码:
import MonadSupply
import Control.Monad.Identity
main :: IO ()
main = do
let result = runSupplyVars realProgram
print result
realProgram :: Supply [Char] Int
realProgram = do
x <- newVar
return 0
newVar :: Supply [Char] [Char]
newVar = do
name <- supply
return ("var_"++name)
runSupplyVars :: Supply [Char] a -> a
runSupplyVars x = fst (runIdentity (runSupply x vars))
where vars = [replicate k ['a'..'z'] | k <- [1..]] >>= sequence
导入MonadSupply
导入控制.Monad.Identity
main::IO()
main=do
let result=runSupplyVars realProgram
打印结果
realProgram::Supply[Char]Int
realProgram=do
x=序列
我想我最大的问题是do
符号。如果我从do
块的第一行调用runSupplyVars
,比如pb0
(根据我的编译错误消息)。我假设这意味着,无论我使用什么顶级函数来启动我的翻译(比如foo
)都需要该返回类型,然后我可以在foo
中自由调用newVar
,以及我在foo
中调用的任何函数。我可以返回一些这种类型的垃圾吗?好的,我看到了你的部分问题-作为外部世界入口点的main
函数的do
符号正在处理IO
monad,而对于你的其余代码,它将处理Supply
monad。我已经添加了一个例子。顺便说一句,如果您还想在代码的其余部分使用IO
,那么您要么需要一个“monad transformer”(SupplyT
,在wiki页面上),要么使用IORef
来传递名称supply。这又增加了一层需要理解的地方,所以最好先掌握这种风格。我想你可能误读了,这是t0而不是IO。我没有做任何IO或使用monad(除非你说我需要?),我将仔细查看你的新示例代码。顺便说一句,如果我弄明白了这一点,你已经获得了丰厚的赏金。我没有误读t0
-我猜你正在使用IO
,因为你说从“我的do
的第一行”内部调用runSupplyVars
,从中我推断它可能来自IO
monad中的main
。Supply
monad的工作方式是建立一个类型为Supply[Char]a
的大型操作,然后使用runSupplyVars
在“纯”代码中消除它。因此,无论从何处调用它,调用看起来都更像let result=runSupplyVars realProgram
,而不是do p
import Control.Monad.Identity
[...]
runSupplyVars :: Supply [Char] a -> a
runSupplyVars x = fst (runIdentity (runSupply x vars))
where vars = [replicate k ['a'..'z'] | k <- [1..]] >>= sequence
import MonadSupply
import Control.Monad.Identity
main :: IO ()
main = do
let result = runSupplyVars realProgram
print result
realProgram :: Supply [Char] Int
realProgram = do
x <- newVar
return 0
newVar :: Supply [Char] [Char]
newVar = do
name <- supply
return ("var_"++name)
runSupplyVars :: Supply [Char] a -> a
runSupplyVars x = fst (runIdentity (runSupply x vars))
where vars = [replicate k ['a'..'z'] | k <- [1..]] >>= sequence