初探 pony 语言

简介

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 是否会溢出.

初探 pony 语言_第1张图片


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.

初探 pony 语言_第2张图片


注意到与第一个例子不同, 这里没有出现多个Couter actor:

    var counter = Counter

    for i in Range[U32](0, count) do
      counter.increment()
    end

在pony中, 所有首字母大写的都是type, 我们可以猜想, 每当直接调用 actor 的 type 时, 将会新建一个这种类型的 actor. (存疑)

consume

初探 pony 语言_第3张图片

使用 consume, 可以将某 object 从一个变量移动到另一个变量(重命名):

fun test(a: Wombat iso) =>
  var b: Wombat iso = consume a // Allowed!

其它

在 pony 中你可以传递 lambda(匿名函数) 给接收的 actor 来执行.

pony 里没有阻塞(block) 的机制, 但可以通过计时器(timer) 来实现延时执行.

你可能感兴趣的:(语言,并发)