在上一篇文章中,我们实现了一个简单的Actor模型。如果要构建一个Actor,便只是 简单地继承Actor<T>类型并实现其Receive方法即可。在上次文章的末尾,我们使 用C#演示了该Actor模型的使用。不过现在我们将尝试一下F#。
C#使用Actor模型的缺陷
在Erlang中,每个消息都使用模式匹配来限制其“结构”或“格式”,以此表达不同 含义。C#类型系统的抽象能力远胜于Erlang,但是Erlang的“动态性”使得开发人员可以 在程序中随意发送和接收任何类型,这种“自由”为Erlang带来了灵活。我们的Actor模 型中,每个Actor对象都需要一种特定的消息格式,而这种消息格式承担了“表现Actor所 有职责”的重任,但是一个Actor的职责是可能由任何数据组合而成。例如一段最简单的 “聊天”程序,其Actor表示了一个“人”,用Erlang实现可能就会这么写:
loop() ->
receive
% 系统要求发起聊天,于是向对方打招呼
{start, Person} ->
Person ! {self(), {greeting, "你好")},
loop();
% 有人前来发起聊天,于是向对方说了点什么
{Person, {greeting, Message}} ->
Person ! {self(), {say, "..."}},
loop();
% 有人前来说话,于是拜拜
{Person, {say, Message}} ->
Person ! {self(), {bye, "..."}},
loop();
...
end.
不同的元组(tuple)配合不同的原子(atom)便表示了一条消息的“含义”,但是使 用C#您又该怎样来表现这些“命令”呢?您可能会使用:
使用object[]作为消息类型,并检查其元素。
使用object作为消息类型,并判断消息的具体类型。
使用枚举或字符串代表“命令”,配合一个参数集合。
第1种做法十分麻烦;第2种则需要“先定义,后使用”也颇为不易;而第3种做法,平 心而论,如果有一个“分发类库”的支持就会比较理想——可能比这篇文章中的F#还要理 想。老赵正在努力实现这一功能,因为C#的这个特性会影响到.NET平台下所有Actor模型 (如第一篇文章中所提到的CCR或Retlang)的使用。
而目前,我们先来看看F#是否可以略为缓解一下这方面的问题。
在F#中使用Actor模型
Erlang没有严谨的类型系统,其“消息类型”是完全动态的,因此非常灵活。那么F# 又有什么“法宝”可以解决C#中所遇到的尴尬呢?在现在这个问题上,F#有三个领先于C# 的关键:
灵活的类型系统
强大的模式匹配
自由的语法
虽然F#也是强类型的编译型语言(这点和C#一致),但是F#的类型系统较C#灵活许多 ,例如在“聊天”这个示例中,我们就可以编写如下类型作为“消息”类型:
type Message = string
type ChatMsg =
| Start of Person
| Greeting of Person * Message
| Say of Person * Message
| Bye of Person * Message