Developing concurrent programs with Pony

Horse Power

© Lead Image © Aleksandr Frolov, 123RF

© Lead Image © Aleksandr Frolov, 123RF

Article from Issue 205/2017
Author(s):

Pony, an object-oriented programming language with static typecasting, trots down well-mapped paths to deliver secure, high-performance code for concurrent applications.

The still young Pony [1] programming language uses the actor model [2] and capabilities [3] to make deadlocks and data races things of the past. In this article, I take Pony for a test ride with an example application that, once it has compiled successfully, logs the consumption of paint and reports the results in a single line.

Figure 1 revisits the problems of concurrent programming in C and C++. To improve performance, the hypothetical program outsources tasks to concurrent threads that access the shared memory area. Locks manage access to prevent different threads editing data simultaneously, falsifying each other's results in the process, and generating race conditions. Nothing good results when programming errors interact and create deadlocks.

Figure 1: In C and C++, a lock is necessary to allow a thread exclusive write access.

Table 1 summarizes the problems associated with implementation across various languages. The comparison with Java is inevitable, because the language offers one of the most successful enhancements of the C and C++ method. A comparison with Rust [4] is also appropriate, because the new kid in town also tries to generate secure and fast code for concurrent applications.

Table 1

Feature Comparison

Language Feature

C/C++

Java

Rust

Pony

AoT Compilation

Yes

No*

Yes

Yes

Memory Secure

No

Yes

Yes

Yes

Type Secure

No

Yes  

Yes

Yes

Avoids Race Conditions

No

No

Yes

Yes

Avoids Deadlocks

No

No

No

Yes

Actor-Based

No

No

No

Yes

*Officially as of Java 9 [5].

Except for dynamic loading of classes [6].

Both Pony and Rust compile programs ahead of time (AoT) to create executable binary code. The method saves time because the languages identify and rule out type conflicts in advance. Java, on the other hand, usually first converts source code to non-executable bytecode, which a virtual machine (VM) executes and monitors. Pony and Java both leave memory management to a garbage collector, which Pony integrates into the run-time environment of the compiler results because it lacks a VM.

Actor Model

Pony programs are always free of data races and deadlocks through the agency of actors. As Figure 2 shows, the model does without a shared memory area and therefore does not need to use locks. Because internally the threads (aka actors) work sequentially, data races could theoretically still occur between the competing actors; however, applications mutually exchange application data through asynchronous communications. The sender deletes all pointers to variable data before sending, which also successfully prevents data races in this scenario.

Figure 2: Behaviors accept asynchronous messages and feed them to the actor's sequential operating mode.

Behaviors (asynchronous functions) are used as mailboxes for incoming messages, which Pony calls like normal methods but runs asynchronously. The actor thus continues its work sequentially. The Pony developers did not create the actor model; the programming languages Erlang, Io, D, and Scala also use it.

Installation

The current release of Pony [7] can be installed using the commands in Listing 1 on a 64-bit Debian system. The first line integrates the packet source into the /etc/apt/sources.list file. The second line updates the Debian package database, and the third line installs Pony. There is no Debian package for the i386 architecture; a manual compile is needed in this case.

Listing 1

Installation on Debian 8

echo "deb https://dl.bintray.com/pony-language/ponyc-debian pony-language main" | tee -a /etc/apt/sources.list
apt-get update
apt-get install ponyc-release

Listing 2 shows the directory tree of the sample application. If you change to the project directory and compile the program with ponyc, the result is the binary executable, as shown in Figure 3, which you can launch with the ./blue-horses command.

Listing 2

The .pony Files

|- blue-horses
  |- blue-horses
  |- main.pony
  |- painter.pony
  |- primitives.pony
  |- resource.pony
Figure 3: Pony compiles successfully (top), and blue-horses reveals paint consumption (bottom).

The main.pony file is the entry point for the Pony program (Figure 4). It uses the class from the resource.pony file to create paint stocks for the primitives (colors) from primitives.pony and presents these to the actor of the Painter type from painter.pony. The actor processes the quantity of paint and reports its consumption in the form of a report to main.pony.

Figure 4: The actor Painter processes the quantity of paint and then sends a report on consumption to main.pony.

Classes

Pony summarizes application data in classes, as the Resource class illustrates in the example in Listing 3, which stores paint types and their respective stock. The class keyword introduces the class in line 1, and line 2 declares the public field name of the user-defined Paint type, which line 6 in Listing 4 again picks up. Thanks to the let keyword, the field can only be written to once, in contrast to a field initiated as var. The class stores the paint types and the respective stock.

Listing 3

resource.pony

01 class Resource
02   let name: Paint
03   var _amount: U64 = 0
04
05   new create(name': Paint) =>
06     name = name'
07
08   fun level(): U64 =>
09     _amount
10
11   fun ref fill() =>
12     _amount = _amount + 1

Listing 4

primitives.pony

02 primitive Amber
02 primitive Blue
03 primitive Crimson
03 primitive Other
05
06 type Color is (Amber | Blue | Crimson | Other)
07
08 primitive Properties
09   fun list(): Array[Paint] =>
10     [Amber, Blue, Crimson, Other]
11
12   fun name(x: Paint): String =>
13     match x | Amber => "amber" | Blue => "blue" | Crimson => "crimson" else "another color" end

Line 3 of Listing 3 defines the private _amount field as an integer data type and sets its value to  . Private field names are prefixed by an underscore, and only the owning class instance can access them. The constructor of the Resource class introduces the new keyword in line 5. The parameter list follows in create(). The name' variable field is a pointer to an object of the Paint type. The next line then saves the name field permanently as an instance of the class.

The fun keyword introduces the method declarations for level() and fill() (lines 8 and 11). If the parameter list is followed by a colon, the type of the return value follows. For level(), it is integer type U64 from the private _amount field.

The value of the expression in the last line of a method is usually its return value, although the use of return lets developers close methods prematurely. The fill() method (line 11) increments the value of _amount by 1.

Because of the ref specification in line 11, the method requires write access for a pointer to an instance of the Resource class. These rights are granted by reference capabilities, which I talk about later; however, before I get to that, I'll look at primitives, which are similar to classes.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus

Direct Download

Read full article as PDF:

Price $2.95

News

njobs Europe
What:
Where:
Country:
Njobs Netherlands Njobs Deutschland Njobs United Kingdom Njobs Italia Njobs France Njobs Espana Njobs Poland
Njobs Austria Njobs Denmark Njobs Belgium Njobs Czech Republic Njobs Mexico Njobs India Njobs Colombia