简介
pony 是一门让我感觉非常有趣又亲切的语言. 它采用了一直很小众的 object capability 来保证没有 data race, 实现了共享内存的 actor model. 下面我将通过一个简单的例子来介绍 pony.
Example1 Mailbox
代码来源于 https://github.com/ponylang/p...
"""
This example is a stress test of Pony messaging and mailbox usage.
All X Mailers send to a single actor, Main, attempting to overload its
mailbox. This is a degenerate condition, the more actors you have sending
to Main (and the more messages they send), the more memory will be used.
Run this and watch the process memory usage grow as actor Main gets swamped.
Also note, it finishes reasonably quickly for how many messages that single
actor ends up processing.
"""
use "collections"
actor Mailer
be ping(receiver: Main, pass: U32) =>
for i in Range[U32](0, pass) do
receiver.pong()
end
actor Main
var _env: Env
var _size: U32 = 3
var _pass: U32 = 0
var _pongs: U64 = 0
new create(env: Env) =>
_env = env
try
parse_args()?
start_messaging()
else
usage()
end
be pong() =>
_pongs = _pongs + 1
fun ref start_messaging() =>
for i in Range[U32](0, _size) do
Mailer.ping(this, _pass)
end
fun ref parse_args() ? =>
_size = _env.args(1)?.u32()?
_pass = _env.args(2)?.u32()?
fun ref usage() =>
_env.out.print(
"""
mailbox OPTIONS
N number of sending actors
M number of messages to pass from each sender to the receiver
"""
)
这段程序的主要功能如图所示, 首先 Main actor 向N个 Mailer actor 发送 ping, Mailer 收到之后会返回M条 pong. 以测试 Main actor 的 mailbox 是否会溢出.
actor Mailer
be ping(receiver: Main, pass: U32) =>
for i in Range[U32](0, pass) do
receiver.pong()
end
这里是对 Mailer 的定义, 试想一下在 erlang/elixir 中我们需要如何实现: 需要定义接口函数, 并在接收消息的进程里定义处理函数. pony 简化了这些流程, 使用关键字 be
即可直接定义这个 actor 的"行为(Behaviour)".
fun ref start_messaging() =>
for i in Range[U32](0, _size) do
Mailer.ping(this, _pass)
end
这里的ref
是一种 "reference capabilites", 表示该函数只可以由它定义处的 actor 调用.(存疑)
Example2 Counter
代码来源于 https://github.com/ponylang/p...
use "collections"
actor Counter
var _count: U32
new create() =>
_count = 0
be increment() =>
_count = _count + 1
be get_and_reset(main: Main) =>
main.display(_count)
_count = 0
actor Main
var _env: Env
new create(env: Env) =>
_env = env
var count: U32 = try env.args(1)?.u32()? else 10 end
var counter = Counter
for i in Range[U32](0, count) do
counter.increment()
end
counter.get_and_reset(this)
be display(result: U32) =>
_env.out.print(result.string())
这段程序的主要功能就是 Main actor 多次发送 increment 给 Counter actor, 每次该 Counter actor 的_count
变量都会 +1.
注意到与第一个例子不同, 这里没有出现多个Couter actor:
var counter = Counter
for i in Range[U32](0, count) do
counter.increment()
end
在pony中, 所有首字母大写的都是type, 我们可以猜想, 每当直接调用 actor 的 type 时, 将会新建一个这种类型的 actor. (存疑)
consume
使用 consume, 可以将某 object 从一个变量移动到另一个变量(重命名):
fun test(a: Wombat iso) =>
var b: Wombat iso = consume a // Allowed!
其它
在 pony 中你可以传递 lambda(匿名函数) 给接收的 actor 来执行.
pony 里没有阻塞(block) 的机制, 但可以通过计时器(timer) 来实现延时执行.