ponyc Documentation

Pony is an open-source, actor-model, capabilities-secure, high performance programming language

RAW Doc

1. Project Overview & Quickstart (ponylang/ponyc)

File: README.md

Pony

Pony is an open-source, object-oriented, actor-model, capabilities-secure, high-performance programming language.

Status

Pony is still pre-1.0 and as such, semi-regularly introduces breaking changes. These changes are usually fairly easy to adapt to. Applications written in Pony are currently used in production environments.

Supported platforms

OS \ CPU amd64 arm64 arm32 riscv64
Linux โœ… Released โœ… Released ๐Ÿ”ง Best-effort ๐Ÿงช Tested
macOS โœ… Released โœ… Released โ€” โ€”
Windows โœ… Released โœ… Released โ€” โ€”
FreeBSD ๐Ÿงช Tested โœ— Unsupported โ€” โ€”
OpenBSD ๐Ÿงช Tested โœ— Unsupported โ€” โ€”
DragonFly BSD ๐Ÿงช Tested โ€” โ€” โ€”
  • โœ… Released โ€” official prebuilt binaries; also tested in CI
  • ๐Ÿงช Tested โ€” tested in CI; build from source (no prebuilt binary)
  • ๐Ÿ”ง Best-effort โ€” not in CI; built from source and tested periodically on real hardware
  • โœ— Unsupported โ€” no support
  • โ€” Not applicable โ€” combination doesn't exist

On Windows, the minimum supported version is Windows 11 / Windows Server 2022
(build 20348). Pony's networking uses an OS readiness API introduced in that
build; earlier versions, including Windows 10, are unsupported and a Pony binary
will not run on them.

On Linux, the minimum supported version is kernel 5.3. Pony's process
support detects a child's exit with pidfd_open, a system call added in that
release; on an older kernel, starting a process returns an error. Earlier
kernels are unsupported.

More Information

Resources

Contributing

We welcome contributions to Pony. Please read through CONTRIBUTING.md for details on how to get started.

License

Pony is distributed under the terms of the 2-Clause BSD License. See LICENSE for details.


File: benchmark/README.md

Benchmarks for the Pony runtime.

Under this directory are benchmarks for the pony runtime and for libponyc
that use the Google Benchmark Library.

The benchmarks build as part of a normal ponyc build. BUILD.md has the
platform setup and the full steps; the build itself is:

bash
cmake --preset release
cmake --build --preset release

That produces one executable per subdirectory here, next to ponyc in
build/release:

  • libponyrt.benchmarks
  • libponyc.benchmarks

Run either one directly. --help lists the Google Benchmark options,
among them --benchmark_filter to select cases by name and
--benchmark_repetitions to repeat them.

See libponyrt/mem/README.md for what the runtime's memory benchmarks measure.

The memory-profile/ directory holds a different kind of benchmark: standalone Pony programs that exercise the arena allocator's --ponymemoryprofile dial, so the dial's per-rung values can be measured and re-derived. They are ordinary Pony programs, not Google Benchmark suites -- build them with the compiler you are testing and run them directly. See memory-profile/README.md.

cmake --install does not install the benchmark binaries.

lib/CMakeLists.txt sets the Google Benchmark version in
PONYC_GBENCHMARK_URL. Google Benchmark documents its options and its
API at https://github.com/google/benchmark.


File: examples/yield/README.md

yield

A program showing a pattern in Pony to transform for/while loops (which could run infinitely) into tail-recursive behavior calls -- the advantage of this beyond avoiding a non-yielding actor is that garbage collection occurs between behaviors.

How to Compile

With a minimal Pony installation, in the same directory as this README file run ponyc. You should see content building the necessary packages, which ends with:

console
...
Generating
 Reachability
 Selector painting
 Data prototypes
 Data types
 Function prototypes
 Functions
 Descriptors
Optimising
Writing ./yield.o
Linking ./yield

How to Run

Once yield has been compiled, in the same directory as this README file run ./yield. You should see a message showing the "infinite" actor has started, followed by the result of the kill command.

console
$ ./yield
Beep boop!
Ugah!

This program has multiple "sub-programs" in it which you can see with --help.

console
$ ./yield --help
usage: yield [<options>] [<args> ...]

Demonstrate use of the yield behaviour when writing tail recursive
behaviours in pony.

By Default, the actor will run quiet and interruptibly.

Options:
   -b, --bench=0         Run an instrumented behaviour to guesstimate overhead of non/interruptive.
   -d, --debug=false     Run in debug mode with verbose output.
   -h, --help=false
   -l, --lonely=false    Run a non-interruptible behaviour with logic that runs forever.
   -p, --punk=false      Run a punctuated stream demonstration.

Try running each of these program, but beware --lonely will run forever so you will need to externally kill it.

Program Modifications

Modify the program to make each of the above options into (leaf) subcommands by making the existing "yield" a parent command. debug should be the only subcommand which takes an argument.

console
$ ./yield bench --perf=10
...
console
$ ./yield debug
...
console
$ ./yield lonely
...
console
$ ./yield punk
...

File: examples/under_pressure/README.md

under_pressure

A program showing how to utilize the Pony backpressure system.

How to Compile

With a minimal Pony installation, in the same directory as this README file run ponyc. You should see content building the necessary packages, which ends with:

console
...
Generating
 Reachability
 Selector painting
 Data prototypes
 Data types
 Function prototypes
 Functions
 Descriptors
Optimising
Writing ./under_pressure.o
Linking ./under_pressure

How to Run

Once under_pressure has been compiled, in the same directory as this README file run ./under_pressure. You should see a message reporting a connection failure.

console
$ ./under_pressure
connect_failed

This failure is because there is no receiver for the data under_pressue is receiving. You can start one with netcat.

console
# Data receiver
nc -l 127.0.0.1 7669 >> /dev/null

Now run under_pressure (your output w)

console
$ ./under_pressure
Querying and setting socket options:
        getsockopt so_error = 0
        getsockopt get_tcp_nodelay = 0
        getsockopt set_tcp_nodelay(true) return value = 13
        getsockopt get_tcp_nodelay = 0
        getsockopt rcvbuf = 131072
        getsockopt sndbuf = 2626560
        setsockopt rcvbuf 5000 return was 0
        setsockopt sndbuf 5000 return was 0

If you then stop the netcat process you will see under_pressure respond that is it experiencing backpressure. Restart netcat and under_pressure will release backpressure. If you look at a system monitor while under backpressure you will notice under_pressure has very low usage -- no actors are running!

Program Modifications

Modify the program to use more than one TCPConnection -- all actors will respond to backpressure on its own.


File: examples/timers/README.md

timers

A program showing use of Timer to track time via behaviors.

How to Compile

With a minimal Pony installation, in the same directory as this README file run ponyc. You should see content building the necessary packages, which ends with:

console
...
Generating
 Reachability
 Selector painting
 Data prototypes
 Data types
 Function prototypes
 Functions
 Descriptors
Optimising
Writing ./timers.o
Linking ./timers

How to Run

Once timers has been compiled, in the same directory as this README file run ./timers. You should see a message about one timer being canceled early, the other will continue running.

console
$ ./timers
timer cancelled
timer: 1
timer: 2
...
timer: 8
timer: 9
timer: 10
timer cancelled

Program Modifications

Modify this program to make the count correspond to seconds passed. Hint: count corresponds to the number of times a given Timer has been called.


File: examples/systemtap/README.md

SystemTap in PonyC

SystemTap is the Linux alternative to DTrace. Although DTrace is available for
Linux the licensing and philosophical differences have caused Linux developers
to create an alternative tracing framework that is compatible with the GPLv2,
thus SystemTap was created. SystemTap and DTrace operate in a similar fashion
but since then the frameworks have been developed independently. Complete
SystemTap documentation can be found
here

Requirements

  • Linux Kernel with UPROBES or UTRACE.

    If your Linux kernel is version 3.5 or higher, it already includes UPROBES. To
    verify that the current kernel supports UPROBES natively, run the following
    command:

    bash
    # grep CONFIG_UPROBES /boot/config-`uname -r`

    If the kernel supports the probes this will be the output:

    bash
    CONFIG_UPROBES=y

    If the kernel's version is prior to 3.5, SystemTap automatically builds the
    UPROBES module. However, you also need the UTRACE kernel extensions required
    by the SystemTap user-space probing to track various user-space events. This
    can be verified with this command:

    bash
    # grep CONFIG_UTRACE /boot/config-`uname -r

    If the < 3.5 kernel supports user tracing this will be the output:

    bash
    CONFIG_UTRACE=y

    Note: UTRACE does not exist on kernel versions > 3.5 and has been replaced by
    UPROBES

  • SystemTap > 2.6

    You need the dtrace commandline tool to generate header and object files
    that are needed in the compilation and linking of PonyC compiler. This is
    required on Linux systems. At the time of writing this the version of
    SystemTap that these probes have been implemented and tested with is 2.6, on
    Debian 8 Stable. Later versions should work. Earlier versions might not work
    and must be tested independently. In debian based systems, SystemTap can be
    installed with apt-get

    bash
    $sudo apt-get install systemtap systemtap-runtime

Using SystemTap scripts for PonyC

You can find various example SystemTap scripts in example/systemtap/. To run
these scripts, a sample command would be of the form:

bash
# stap [script path] -c [binary path]

NB: stap must be run as root (or sudo) since it compiles the SystemTap script
into a loadable kernel module and loads it into the kernel when the stap script
is running.

You need to use a PonyC compiler compiled with the DTrace flag to use SystemTap.
To compile a SystemTap enabled compiler, add the use=dtrace flag into the make
command:

make config=release use=dtrace.

Writing scripts for SystemTap in PonyC

SystemTap and DTrace use the same syntax for creating providers and thus we
direct to the DTrace documentation for
more information on the syntax and possibilities.

The PonyC provider for SystemTap consists of DTrace probes implemented in the
Runtime. A binary compiled with DTrace enabled will allow the user access to the
information of the probes, which work with SystemTap scripts. You can find a
list of all probes and their arguments in
src/common/dtrace_probes.d. The following
is a toy example of a SystemTap script:

dtrace
global gc_passes

probe process.mark("gc-start")
{
  gc_passes <<< $arg1;
}

probe end
{
  printf("Total number of GC passes: %d\n", @count(gc_passes));
}

This simple example will hook into the executable that the -c parameter provides
and searches for the probe named "gc-start". Once execution of the executable is
started, the script increases a counter every time the "gc-start" probe is
accessed and at the end of the run the results of the counter are printed

In SystemTap you can use the DTrace syntax of "gc-start" but you may also call
them like they are in the
src/common/dtrace_probes.d file,
"gc__start".

All available probes in an executable can be listed like this:

bash
# stap -L 'process("<name_and_path_of_executable>").mark("*")'

This is useful to confirm that probes are ready to be hooked in.

Extending the SystemTap implementation in Pony

You can extend the SytemTap implementation by adding more probes or extra
information to existing probes. All probes are defined in
src/common/dtrace_probes.d. Usually their
names of their module and the event that triggers them. To install Probes in C
use the macros defined in src/common/dtrace.h. To fire a probe in the C code
use the macro DTRACEx; where x is the number of arguments of the probe.
There is also a macro DTRACE_ENABLED; its use allows for code to be only
executed when a probe is enabled.

For adding and extending probes please refer to the DTrace
README file, since this process works
exactly the same for both systems.


File: examples/spreader/README.md

spreader

A program producing a binary tree of actors at a given depth, which then counts/reports the number of actors in the tree.

How to Compile

With a minimal Pony installation, in the same directory as this README file run ponyc. You should see content building the necessary packages, which ends with:

console
...
Generating
 Reachability
 Selector painting
 Data prototypes
 Data types
 Function prototypes
 Functions
 Descriptors
Optimising
Writing ./spreader.o
Linking ./spreader

How to Run

Once spreader has been compiled, in the same directory as this README file run ./spreader. You should see a message reporting the number of actors in the tree.

console
$ ./spreader
1023 actors

This program can take a numeric arguments stating the depth of the tree to build (the default is 10).

console
$ ./spreader 5
31 actors

Program Modifications

Modify this program to print its total depth.

console
$ ./spreader 5
31 actors at depth 5

Additionally, add a second argument for the branching factor.

console
$ ./spreader 5 3
242 actors at depth 5 with branch factor of 3

File: examples/runtime_info/README.md

runtime_info

A program showing use of runtime stats.

How to Compile

You will need a compiler with runtime stats enabled to get meaningful output. To compile a runtime stats enabled compiler, you can use the following command: make config=release use=runtimestats_messages.

In the same directory as this README file run ponyc that has runtime stats enabled. You should see content building the necessary packages, which ends with:

console
...
Generating
 Reachability
 Selector painting
 Data prototypes
 Data types
 Function prototypes
 Functions
 Descriptors
Optimising
Writing ./runtime_info.o
Linking ./runtime_info

How to Run

Once runtime_info has been compiled, in the same directory as this README file run ./runtime_info. You should see a large amount of output of actor and scheduler stats ending with something similar to the following:

console
$ ./runtime_info
<SNIPPED>
  memory allocated influght messages: 96
  number of inflight messages: 3

2. Official Technical Reference & Guides (ponylang/ponylang.github.io)

File: README.md

ponylang-website

Source for the Pony website.

Interested in contributing?

Please see our contributing guidelines

Interacting with the community

Please make sure to read our code of conduct.


File: docs/index.md


hide:

  • navigation
  • toc

Pony

An open-source, object-oriented, actor-model, capabilities-secure, high-performance programming language.

Get Started{ .md-button .md-button--primary }
Try It in Your Browser{ .md-button target=_blank }
Install{ .md-button target=_blank }

Pony is...

Type safe

Really type safe. There's a mathematical proof and everything.

Memory safe

No dangling pointers. No buffer overruns. No null.

Data-race free

Pony doesn't have locks or atomic operations. The type system ensures at compile time that your concurrent program can never have data races.

Deadlock-free

Pony has no locks at all. They can't deadlock because they don't exist.

Exception-safe

No runtime exceptions. All exceptions have defined semantics, and they are always caught.

Native code

Pony is an ahead-of-time (AOT) compiled language. No interpreter. No virtual machine.

Compatible with C

Pony programs can natively call C libraries using the foreign function interface.

See It in Action

Three actors coordinate a party. Main tells Party to add friends. Party creates a Friend actor for each one and sends it an invite. Each Friend decides whether to attend and sends back an RSVP. When all responses are in, Party prints the guest list.

No locks. No mutexes. No synchronization primitives. The type system handles it.

pony
actor Party
  let _env: Env
  var _guests: Array[String val] = _guests.create()
  var _expected: USize = 0
  var _responded: USize = 0

  new create(env: Env) =>
    _env = env

  be add_friend(name: String val) =>
    _expected = _expected + 1
    Friend(name).invite(this)

  be rsvp(name: String val, attending: Bool) =>
    _responded = _responded + 1
    if attending then
      _guests.push(name)
    end
    if _responded == _expected then
      _env.out.print("Party guest list:")
      for guest in _guests.values() do
        _env.out.print("  " + guest)
      end
    end

actor Friend
  let _name: String val

  new create(name: String val) =>
    _name = name

  be invite(party: Party) =>
    // friends with short names are too busy
    let attending = _name.size() > 3
    party.rsvp(_name, attending)

actor Main
  new create(env: Env) =>
    let party = Party(env)

    party.add_friend("Alice")
    party.add_friend("Bob")
    party.add_friend("Carol")

Try this example in the Playground{ .md-button target=_blank }
More examples{ .md-button target=_blank }

Pony's Design Philosophy

Pony's philosophy is "get-stuff-done." Every design decision follows a clear priority order:

Correctness comes first. Incorrectness is simply not allowed. It's pointless to get stuff done if you can't guarantee the result is correct.

Performance is second. Runtime speed is more important than everything except correctness.

Simplicity is third. It's ok to make things a bit harder on the programmer to improve performance, but it's more important to make things easier on the programmer than on the language runtime.

This philosophy shapes concrete commitments: a program that compiles should never crash. All semantics are fully defined โ€” nothing is left "implementation dependent." There is no ambiguity โ€” the programmer should never have to guess what the compiler will do. There is no "trust me, I know what I'm doing" escape hatch.

The Pony Philosophy | Guiding Principles

Why Pony?

Pony makes it easy to write fast, safe, highly concurrent programs. The type system introduces reference capabilities โ€” labels you put on data that describe how it can be shared. The compiler verifies you're handling data correctly based on those labels. Share only immutable data. Exchange only isolated data. The compiler enforces both. No locks needed.

If you ask us, that's pretty damn cool.

Read the full argument

Why Not Pony?

We believe in being honest. Pony hasn't reached version 1.0. Breaking changes still happen. The pool of ready-to-use libraries is small. There's no IDE. If your project needs a large ecosystem and stable APIs right now, Pony isn't the right choice today.

But if you see the potential, we'd love for you to give it a try.

Read more

Start Learning