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:
cmake --preset release
cmake --build --preset releaseThat produces one executable per subdirectory here, next to ponyc inbuild/release:
libponyrt.benchmarkslibponyc.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 inPONYC_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:
...
Generating
Reachability
Selector painting
Data prototypes
Data types
Function prototypes
Functions
Descriptors
Optimising
Writing ./yield.o
Linking ./yieldHow 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.
$ ./yield
Beep boop!
Ugah!This program has multiple "sub-programs" in it which you can see with --help.
$ ./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.
$ ./yield bench --perf=10
...$ ./yield debug
...$ ./yield lonely
...$ ./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:
...
Generating
Reachability
Selector painting
Data prototypes
Data types
Function prototypes
Functions
Descriptors
Optimising
Writing ./under_pressure.o
Linking ./under_pressureHow 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.
$ ./under_pressure
connect_failedThis failure is because there is no receiver for the data under_pressue is receiving. You can start one with netcat.
# Data receiver
nc -l 127.0.0.1 7669 >> /dev/nullNow run under_pressure (your output w)
$ ./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 0If 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:
...
Generating
Reachability
Selector painting
Data prototypes
Data types
Function prototypes
Functions
Descriptors
Optimising
Writing ./timers.o
Linking ./timersHow 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.
$ ./timers
timer cancelled
timer: 1
timer: 2
...
timer: 8
timer: 9
timer: 10
timer cancelledProgram 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:
bashCONFIG_UPROBES=yIf 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 -rIf the < 3.5 kernel supports user tracing this will be the output:
bashCONFIG_UTRACE=yNote: UTRACE does not exist on kernel versions > 3.5 and has been replaced by
UPROBESSystemTap > 2.6
You need the
dtracecommandline 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-getbash$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:
# 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 insrc/common/dtrace_probes.d. The following
is a toy example of a SystemTap script:
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 thesrc/common/dtrace_probes.d file,
"gc__start".
All available probes in an executable can be listed like this:
# 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 insrc/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 DTraceREADME 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:
...
Generating
Reachability
Selector painting
Data prototypes
Data types
Function prototypes
Functions
Descriptors
Optimising
Writing ./spreader.o
Linking ./spreaderHow 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.
$ ./spreader
1023 actorsThis program can take a numeric arguments stating the depth of the tree to build (the default is 10).
$ ./spreader 5
31 actorsProgram Modifications
Modify this program to print its total depth.
$ ./spreader 5
31 actors at depth 5Additionally, add a second argument for the branching factor.
$ ./spreader 5 3
242 actors at depth 5 with branch factor of 3File: 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:
...
Generating
Reachability
Selector painting
Data prototypes
Data types
Function prototypes
Functions
Descriptors
Optimising
Writing ./runtime_info.o
Linking ./runtime_infoHow 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:
$ ./runtime_info
<SNIPPED>
memory allocated influght messages: 96
number of inflight messages: 32. 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.
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.
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.