我注意到邮箱类型是封装的,并且只能通过使用MailboxProcessor来使用。
这意味着为了拥有一个可以向其发送消息的代理,我必须拥有一个单一类型的邮箱(或者以一种奇特的方式使用现有的MailboxProcessor )。
我是否应该理解,一个工作流拥有多个邮箱本身就会导致糟糕的设计?Ccr显然为您提供了这种级别的自由。
编辑:正如Daniel指出的,如果一个人想发送多种类型的消息,我们优雅地解决了这个问题--这不是我以前没有做过的。
但问题是,这样做不是有代码的味道吗?随着时间的推移,添加发送给代理的更多类型的消息不会导致您承担太多责任吗?我有时认为,始终将代理使用的消息类型封装在接口后面是很重要的,这样就永远不会暴露这些信息。
发布于 2012-06-21 01:55:01
我想我可能找到我要找的东西了。我已经听过Rich Hickey的演讲至少5次了,我相信他的方法解决了我所关心的许多设计问题。显然,这可以通过F#邮箱或CAS引用来实现。
我非常推荐它,如果能听到一些反馈,我会很高兴的。
发布于 2011-12-13 07:37:26
我认为使用MailboxProcessor和CCR的F#代理实现了不同的编程模型,但我相信两者都是同样强大的,尽管肯定有一些问题可以用其中一个更好地解决,所以如果有另一个围绕邮箱构建的F#库就更好了。基于CCR的编程模型可能在基于连接演算的各种语言中描述得更清楚,比如COmega (这是一个旧的MSR项目)。
例如,您可以比较使用COmega和F#代理的一处缓冲区的实现:
public class OnePlaceBuffer {
private async empty();
private async contains(string s);
public OnePlaceBuffer() { empty(); }
public void Put(string s) & empty() {
contains(s);
}
public string Get() & contains(string s) {
empty();
return s;
}
}在此示例中,异步方法的行为类似于邮箱(因此有四个:empty、contains、Put和Get),而主体的行为类似于当邮箱组合包含值时(即,当您放入空缓冲区或从满缓冲区获取时)触发的处理程序。在F#中,您可以使用MailboxProcessor并编写:
type Message<'T> =
| Put of 'T * AsyncReplyChannel<unit>
| Get of AsyncReplyChannel<'T>
MailboxProcessor.Start(fun agent ->
let rec empty = agent.Scan(function
| Put(v, repl) -> repl.Reply(); Some(full(v))
| _ -> None)
and full v = agent.Scan(function
| Get repl -> repl.Reply(v); Some(empty)
| _ -> None)
empty )这两个实现表达了相同的思想,但方式略有不同。在F#中,empty和full是两个函数,它们代表代理的不同状态,发送到代理的消息代表代理状态的不同方面(待完成的工作)。在COmega实现中,程序的所有状态都由邮箱捕获。
我猜,将代理的状态与需要处理的即时消息分开可能会更容易考虑F# MailboxProcessor,但这只是一个没有理由的即时想法……
最后,在一个在F#中使用MailboxProcessor的实际应用程序中,您很可能会使用更多的它们,并且它们会以某种方式联系在一起。例如,实现流水线是使用多个MailboxProcessor实例(当然,这些实例都有一些简单的运行异步工作流)的应用程序的一个很好的例子。有关示例,请参阅this article。
发布于 2011-12-13 06:30:06
通常,消息类型是有区别的联合,允许在单个邮箱中处理各种类型的消息。这在你的情况下不起作用吗?
https://stackoverflow.com/questions/8481655
复制相似问题