如果你想对源代码级的功能,如做到这一点:
myFoo x y = x + y
你非常不走运了,除非你想要去的编译器黑客左右。但是,您可以定义您自己的支持此功能的概念,并带有一些适当的注释。我们称这个概念为UserAction a
,其中a
是该操作的返回类型。为了组成UserAction
中的计算,它应该是Monad
。没有想到过非常辛苦,我的第一感觉就是使用这个堆栈单子变压器:
type UserAction = WriterT [LogEntry] (ReaderT FuncIdentifier IO)
的WriterT [LogEntry]
组件说,一个UserAction
,运行时产生的LogEntry
S [1]的序列,其中含有你想要写入数据库的信息;是这样的:
data LogEntry = Call FuncIdentifier FuncIdentifier
没关系推迟存储随机种子,任务标识符等现在 - 这可以通过将信息添加到LogEntry
被纳入这个设计。
ReaderT FuncIdentifier
组件说,UserAction
取决于FuncIdentifier
;即调用它的函数的标识符。
FuncIdentifier
可以通过为
type FuncIdentifier = String
简单的东西来实现,或者你使用的东西与更多的结构,如果你喜欢。
IO
组件说,UserAction
s可以做任意的输入和输出到文件,控制台,产卵线程,整个很多。如果您的操作不需要此操作,请不要使用它(使用Identity
代替)。但是既然你提到了产生随机数,我想你并没有纯粹的计算[2]。
那么你会标注你想要这样的功能来记录日志,每个动作:
userAction :: FuncIdentifier -> UserAction a -> UserAction a
这会像这样使用:
randRange :: (Integer, Integer) -> UserAction Integer
randRange (low,hi) = userAction "randRange" $ do
-- implementation
userAction
将记录呼叫,并设置记录他们的呼叫;例如是这样的:
userAction func action = do
caller <- ask
-- record the current call
tell [Call caller func]
-- Call the body of this action, passing the current identifier as its caller.
local (const func) action
从顶层,运行所需的行动和完成任务后,收集了所有的LogEntry
S和它们写入数据库。
如果您需要在代码执行时实时写入呼叫,则需要不同的UserAction
monad;但你仍然可以呈现相同的界面。
该方法使用了一些中间Haskell概念,如monad变换器。我建议去IRC irc.freenode.net
#haskell
频道询问关于填写本实施草图细节的指导。他们是一群善良的人,会很乐意帮助你学习:-)。
[1]在实践中,您不会想要使用[LogEntry]
而是使用DList LogEntry
来获得性能。但改变很简单,我建议你用[LogEntry]
,直到你对Haskell更舒适,然后切换到DList
。
[2]随机数字的生成可以纯粹完成,但它需要进一步的大脑重新连接,这个草图已经有很多,所以我建议只是把它当作一个IO
效果来达到目的。
你是什么意思的“功能图”? GHC Haskell在运行时系统中将函数表示为图形(运行时是所谓的图形缩减机器),但内部不能被用户访问。将函数存储在数据库中会非常复杂 - 通常这是“持久”语言的领域,例如Napier 88或Tycoon-2。在Napier 88的持久性商店(Persistent Haskell)上实施了Haskell,但这是一个早已完成的研究项目。 –
对不起,有点不清楚。我仍然与Haskell摇摆不定。我会用我认为可能是我需要的策略来重述这个问题。我现在打字。 –
用户与什么样的API进行交互? – Henrik