`
inshua
  • 浏览: 8157 次
  • 性别: Icon_minigender_1
文章分类
社区版块
存档分类
最新评论

以流、delay 的方式看待消息

阅读更多
试将消息当做预先设想到的流的一部分。与语法分析类似,在设计 Parser 的阶段,并不清楚 TokenStream 下一个 token 会是什么,但由于预先知道 token 的类型,故可从容面对。

将程序视为由消息在驱动是一种急促的思路,与此对应的是语法分析的思路。有多少种消息类型,能处理多少种消息类型,这是预先可以预见到的。在何种状态会接收到什么消息,也是可以预见的。因此,可以将状态机转化为相当于流处理的动作机。这种做法体现在 uniguard-connection 的连绵处理器的设计中,另外,在 naggati scala 中,分包的 step 也有类似的设计。如果将 erlang 视为声明式的语言,由于它可以更换接收函数,亦可认为做到了同等的效果。

唯需要注意的是,由于一个语法分析器实际上只处理一段叙述。在采用语法分析器模式对付消息时,由于消息类型不同,相应的叙事内容也不同,因此,根据消息分类,可以衍生出若干连绵处理器。这样一来,在按这种方式设计时,在消息分派程序中,按消息类型决定应该分派到哪个连绵处理器。

不应考虑两个叙事混杂的情况。如两个叙事混杂,从设计上事先应该隔离,如任务增加任务id。


实现:

连绵处理器虽然可行,但代码过于冗长,应该有办法缩减。和语法分析器的方式对比:

function parse(tokens){
match('if')
match('(')
condition(tokens)
match(')')
}

function condition(tokens){
// more and more
}

由于不涉及异步,故无需回调,因此这种方式简单优雅。

不妨采取以下形式:

parse(match('if'), match('('), condition(tokens), match(')'))

parse 函数是一个 combine 函数,其自动为每个 match 生成 id,当消息箱中没有更多的消息时,就一层一层挂起,待收到消息后,从这个挂起的 id 继续执行,这种 combine 函数需要能嵌套,以便在内部仍可以挂起。因此,match 函数也是一个 combine 函数。 此外,还需要用于分支的 cond, if,逻辑 combine,and, or,以及支持迭代 while。这种思路形似 sicp 中的 delay 机制。

一个简单的发送短信过程如下:

sequence(
login(username, password),
cond(
match('success',
sendmessage('hello'),
cond(
match('success', log('success')),
match('failed', log('failed'))
),
logout()
),
match('failed', log('log failed'))
)
)


这个做法更适合漫长的消息交互,适合表现序列图。当然,这会比状态机耗费更多的资源。


以这种方式理解,每个设备是这样一个处理器:

sequence(
promptLogin(),
login(givenUsername, pwd),
loop(
match()
)
)


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics