【问题标题】:Undo records in database撤消数据库中的记录
【发布时间】:2013-01-08 03:14:51
【问题描述】:

我正在创建一个系统,该系统需要存储用户在数据库中运行的所有函数和参数。没有记录被删除,但我需要能够重新创建最小的函数序列和参数集以进行确定性重新生成。

用户交互非常少,他们不是编程 - 输入交互在 C++ 中处理,通过 FFI 作为数据累积到列表中,并回调以处理当前数据缓冲区。该函数触发一系列关于如何连接数据库中数据集的处理图以及它们被输入到的函数的决策。该图是非循环的。该图最初是运行的,并且值是为用户可视化的。图表的后面部分将被重新组合以生成新的图表。

这些图的 Haskell 内部结构是通过分析数据库中的数据和组合中的简单随机选择创建的。我希望能够只存储随机生成器的种子、它适用的模块和参数 id。

我认为这可能最好将 EDSL 的功能存储在数据库中,其中仅存储高级交互但完全确定性。

我对存储值不感兴趣,而是对动作的函数图感兴趣。

每张表对应不同的功能。每条记录都有一个日期和一个任务 ID,以将特定操作的所有功能组合在一起。参数引用表 ID 和记录 ID。如果组合函数在内部执行诸如生成随机数之类的操作,则应自动存储该数字的种子。

我正在使用没有 GHCI 和 Persistent SQlite 的 GHC 阶段 1。

我还是 Haskell 的新手,正在寻找适合以功能方式解决此问题的方法和软件包。

【问题讨论】:

  • “函数图”是什么意思? GHC Haskell 将运行时系统中的函数表示为图形(运行时是所谓的图形缩减机器),但用户无法访问内部。在数据库中存储函数会非常复杂——通常这是“持久”语言的领域,例如 Napier 88 或 Tycoon-2。已经在 Napier 88 的持久化存储(Persistent Haskell)上实现了 Haskell,但这是一个早已完成的研究项目。
  • 抱歉,有点不清楚。我仍然对 Haskell 感到不安。我将用我认为可能是我需要的策略来重新表述这个问题。我现在正在输入。
  • 用户与什么样的 API 交互?

标签: haskell functional-programming


【解决方案1】:

如果您想对源代码级函数执行此操作,例如:

myFoo x y = x + y

你很不走运,除非你想在编译器中四处乱窜。但是,您可以使用一些合适的注释定义自己的支持此功能的函数概念。让我们将此概念称为UserAction a,其中a 是操作的返回类型。为了在UserAction 中组合计算,它应该是Monad。不用想太多,我的第一印象是使用这堆 monad 转换器:

type UserAction = WriterT [LogEntry] (ReaderT FuncIdentifier IO)

WriterT [LogEntry] 组件表示UserAction 在运行时会产生一系列LogEntrys [1],其中包含您要写入数据库的信息;类似:

data LogEntry = Call FuncIdentifier FuncIdentifier

现在可以推迟存储随机种子、任务标识符等 - 可以通过将信息添加到 LogEntry 来将其合并到此设计中。

ReaderT FuncIdentifier 组件表示UserAction 依赖于FuncIdentifier;即,调用它的函数的标识符。

FuncIdentifier 可以通过简单的方式实现

type FuncIdentifier = String

如果你愿意,你也可以使用结构更丰富的东西。

IO 组件表示UserActions 可以对文件、控制台、生成线程等进行任意输入和输出。如果您的操作不需要它,请不要使用它(改用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

从顶层运行所需的操作,完成后,收集所有LogEntrys 并将它们写入数据库。

如果您需要在代码执行时实时编写调用,则需要一个不同的UserAction monad;但您仍然可以呈现相同的界面。

这种方法使用了一些中间的 Haskell 概念,例如 monad 转换器。我建议通过 IRC 到 irc.freenode.net #haskell 频道寻求有关填写此实施草图细节的指导。他们是一群善良的人,很乐意帮助你学习:-)。

[1] 实际上,您不想使用[LogEntry] 而是使用DList LogEntry 来提高性能。但是更改很容易,我建议您使用 [LogEntry],直到您对 Haskell 更加熟悉,然后切换到 DList

[2] 随机数生成可以纯粹完成,但它需要进一步的大脑重新布线,这个草图已经有很多了,所以我建议将其视为IO 效果以继续进行。

【讨论】:

  • 您的问题不断变化!这种方法不太适合确定性再生——主要是因为UserAction 类型中的IO。我认为我们需要更多地了解你打算使用这个框架来帮助确定性再生——如果你不使用IO,Haskell 是纯粹的和确定性的,所以你可能不需要做任何事情,只需要记录计算的输入。
  • 我已经更新了这个问题。 EDSL 可能不是正确的方法,在这种情况下,我将删除该方面 - 有点摸不着头脑。
猜你喜欢
  • 1970-01-01
  • 2011-03-18
  • 2010-10-01
  • 2010-09-30
  • 1970-01-01
  • 1970-01-01
  • 2014-02-04
  • 1970-01-01
  • 2014-04-14
相关资源
最近更新 更多