在功能解释器中序列化正在运行的程序

时间:2013-10-08 01:04:15

标签: serialization functional-programming interpreter monads continuations

我正在编写一个使用Cont Monad的变体功能实现的解释器。受Smalltalk使用图像捕获正在运行的程序的启发,我正在研究如何序列化执行的托管程序,并需要帮助确定如何在高级别完成此任务。

问题陈述

使用Cont monad,我可以在我的解释器中捕获正在运行的程序的当前延续。存储当前延续允许通过调用continuation来恢复解释器执行。我想序列化此延续,以便正在运行的程序的状态可以保存到磁盘或由另一个解释器实例加载。但是,我的语言(我的目标和使用Javascript)不支持以这种方式序列化函数。

我感兴趣的是一种方法可以用来在给定的执行点建立延续,给定一些元数据而不再运行整个程序直到它到达那一点。优选地,将对解释器本身的实现进行最小的改变。

考虑方法

可能有效的一种方法是将所有控制流逻辑推入程序状态。例如,我目前使用宿主语言的递归来表示循环行为的C样式:

var forLoop = function(init, test, update, body) {
    var iter = function() {
        // When test is true, evaluate the loop body and start iter again
        // otherwise, evaluate an empty statement and finish
        return branch(test,    
            next(             
                next(body, update),
                iter()),
             empty);
    };

    return next(
        init,
        iter());
};

这是一个很好的解决方案,但是如果我通过for循环中途暂停程序,我不知道如何序列化已经构建的延续。

我知道我可以使用跳转来序列化转换后的程序,并且可以通过跳转操作构造for循环。我的解释器的第一遍将生成代码块并将其保存在程序状态中。这些块将捕获托管语言中的某些操作,并可能执行其他块。预处理程序看起来像这样:

Label         Actions (Block of code, there is no sequencing)
-----------------------------------
start:        init, GOTO loop

loop:         IF test GOTO loop_body ELSE GOTO end

loop_body:    body, GOTO update

update:       update, GOTO loop

end:          ...

这使得每个代码块都独立,只依赖于存储在程序状态中的值。

要序列化,我会保存当前标签名称和输入时的状态。反序列化将预处理输入代码以再次构建标签,然后在具有给定状态的给定标签处继续。但是现在我必须在实现我的解释器时考虑这些块。即使使用构图来隐藏其中一些似乎也很难看。

问题

有没有好的现有方法来解决这个问题?我是否正在考虑以完全错误的方式序列化程序?对于像这样的结构,这甚至可能吗?

1 个答案:

答案 0 :(得分:0)

经过更多的研究,我对如何做到这一点有一些想法。但是,我不确定在这一点上添加序列化是我想要做的事情,因为它会影响其余的实现。

我对这种方法不满意,并且非常希望听到任何其他选择。

问题

正如我所指出的,将程序转换为语句列表使序列化更容易。整个程序可以转换成汇编语言,但我想避免这种情况。

保持表达式的概念,我最初没有考虑的是函数调用可以在深层嵌套的表达式中发生。以此计划为例:

function id(x) { return x; }
10 + id(id(5)) * id(3);

序列化程序应该能够在任何语句中序列化程序,但该语句可能会在表达式中进行评估。

状态中的主机功能

程序状态无法轻松序列化的原因是它包含继续的主机函数。必须将这些延续转换为数据结构,这些数据结构可以被序列化并独立地重建为原始延续所表示的动作。功能化通常用于在一阶语言中表达高阶语言,但我相信它也可以启用序列化。

如果不彻底重写解释器,并非所有延续都可以轻松地进行功能化。由于我们只对特定点的序列化感兴趣,因此在这些点进行序列化需要对整个连续堆栈进行功能化。因此,所有语句和表达式必须是功能化的,但在大多数情况下内部逻辑可以保持不变,因为我们不希望通过内部操作中途允许序列化。

但是,据我所知,由于绑定语句,defunctionalization对Cont Monad不起作用。缺乏良好的抽象使其难以使用。

关于解决方案的想法

目标是创建一个仅由简单数据结构组成的对象,可用于重建整个程序状态。

首先,为了最大限度地减少所需的工作量,我会重写语句级别解释器,以便使用更容易序列化的状态机。然后,我会对表达式进行去功能化。函数调用会将剩余表达式的defunctionlized continuation推送到状态机的内部堆栈上。

使程序状态成为可序列化对象

看看语句是如何工作的,我不相信Cont Monad是将语句链接在一起的最佳方法(我确实认为它在表达层和内部操作中都能很好地工作)。状态机似乎更自然的方法,这也更容易序列化。

将编写在语句之间切换的机器。该州的所有其他结构也将可以序列化。内置函数必须使用可序列化的句柄来识别它们,以便状态中没有函数。

处理表达式

表达式将被重写以传递defunctionalized continuation而不是host function continuation。当在表达式中遇到函数调用时,它会捕获defunctionalized当前的continuation并将其推送到语句机器的内部堆栈(这只会发生在托管函数上,而不是内置函数),从而创建一个可以恢复计算的恢复点。 p>

当函数返回时,defunctionalized continuation传递结果。

关注

Javascript还允许在几乎任何表达式(getter,setter,类型转换,高阶内置函数)中评估托管函数,如果我们允许在这些函数内部进行序列化,这可能会使事情变得复杂。

去功能化似乎需要直接使用continuation,并且会使整个解释器变得不那么灵活。