The Chapel community is happy to announce the release of Chapel 2.4! In this article, we’ll summarize some of its main highlights, including:
-
Brand-new syntax for defining multidimensional array values
-
Significant improvements in Chapel-Python interoperability
-
Support for building Chapel programs with CMake
-
Significant advances in Dyno’s ability to resolve Chapel features
Other highlights of Chapel 2.4 that are not covered in this article include:
-
Parallel iterator and zippering support over the
set
andmap
types -
The ability to query the number of co-locales running on a node
-
New custom settings, location-based rules, and documentation for the
chplcheck
linter
For a much more complete list of changes in Chapel 2.4, see the CHANGES.md file. And huge thanks to all the Chapel community members who contributed to version 2.4!
Multidimensional Array Literals
Since Chapel’s inception, the language has supported both
multidimensional rectangular arrays and arrays of arrays—arrays
whose elements also happen to be arrays. For example, the following
declarations each create $n^2$ array elements, where the first is a
2D array of real floating point values (real
), while the second is
a 1D array whose elements are each a 1D array of real
values:
|
|
There are a few differences between these two types in Chapel, as well as potential motivations for using either over the other.
(Expand this section for background on some of the key differences…)
Focusing on the simple declarations above, some important distinctions between the two array types are:
-
Arr2D
will allocate all of its elements using a single block of consecutive memory. In contrast,ArrOfArr
allocates each of its inner array’sn
elements in a block of consecutive memory, but with no guarantee as to where each inner array will be placed in memory relative to the others. -
The elements of
Arr2D
can be accessed using either a pair of integer indices or a 2-tuple of integers. However,ArrOfArr
must be indexed using an integer index to select one of its inner arrays, after which a second integer index can be used to access one of itsreal
values. This can be seen in the following lines, which write and read elements of each array:
|
|
Note that multidimensional arrays and tuple-based indexing can be particularly valuable when writing rank-independent code, since they don’t require a number of commas or brackets proportional to the array’s rank.
Arr2D
can also be sliced across both of its dimensions to create a pseudo-array that refers to the elements in question. In contrast,ArrOfArr
doesn’t support directly referring to an arbitrary subset of its elements—just to some or all of the elements in one of its inner arrays:
|
|
Despite Chapel’s longstanding support for multidimensional array types and computations, up until this week’s release, the language has only supported a syntax for specifying 1D array values, or literals. Thus, a user could write:
|
|
or:
|
|
where these two declarations rely on Chapel’s type inference to infer that the variables are arrays, based on the initializing array literal.
However, there has not been an equivalent way to declare a type-inferred multidimensional array, nor to create such a value to pass as an argument or compute with directly. This deficiency in the language has long been noted by users, who have requested a solution.
Happily, Chapel 2.4 adds this requested feature. Even better, the approach taken reflects the result of intensive user discussions, including live community feedback sessions to reach consensus. The resulting approach is to use semicolons as a kind of “heavyweight comma” to indicate the end of a dimension. Thus, a 2D 3$\times$3 array can be written:
|
|
Similarly, a 3D, 2$\times$2$\times$2 array can be written:
|
|
Note that the whitespace and linefeeds used in these examples are optional, but used here to improve readability.
We’re very pleased by the addition of this feature, and thoroughly appreciate all the input we’ve received from the Chapel community on this topic over the years, and particularly as we completed the feature over the past few months.
Python Interoperability
Chapel 2.4 contains significant improvements to the Python interoperability support that we introduced in version 2.3. Let’s look at some of the improvements in terms of ergonomics and data types:
Ergonomic Improvements
As a motivating example, consider the following snippet of Python code:
|
|
Previously, calling compute_sum()
on a Chapel array required a lot
of explicit handling of types; it also resulted in lst
being
copied twice, unnecessarily. Calling such a function also required
using multiple files, as the Python code needed to be in its own
module for Chapel to import. Here’s an example of how this looked
in Chapel 2.3:
|
|
In this week’s release, this computation can now be expressed much more succinctly and with less overhead. Rather than importing a piece of Python code from a standalone module, we can now [note: Chapel 2.4 can also directly pull in Python bytecode as a module or a pickle object as a value. ]
|
|
This gives us a lib
object like before, but without the need for a
separate file. And now, when we acquire a handle to compute_sum()
,
it reads better:
|
|
The next lines are the most important part, as we can now call
compute_sum()
using a direct reference to the Chapel array (rather
than the default copying behavior) and get the result back:
|
|
We can actually make one more productivity improvement here. Since all
we do with res
is print it out, we don’t really need to specify
the return type of compute_sum()
at all:
|
|
In this version, res2
is a generic Python value that Chapel’s
Python
module knows how to print, so we can just pass it to
writeln()
directly. While this is a small change, across larger
programs such brevity can add up.
All of the improvements above come together to form a much more ergonomic and powerful interface to the Python ecosystem.
Data Type Improvements
Chapel 2.4 also expands the Chapel types that the Python
module
understands, specifically adding the new Chapel types PyList
,
PySet
, PyDict
, and [note: Python
provides a low-level array
type that the Chapel
PyArray
type represents. This type can also refer to other
array-like things in Python, such as NumPy arrays. ].
Values of these types are handles to Python objects that provide
specialization over an abstract Value
type.
For example, the following code creates a Python set, and then adds
new elements to it. The make_set()
function it uses is a simple
Python function that takes an arbitrary number of arguments,
returning a set containing those arguments.
|
|
Using make_set()
, we can create a set containing arbitrary elements:
|
|
We could use a generic return type and have an opaque handle to the
set, but since we know it’s a set, we use the PySet
type to
get a more specialized handle. This lets us invoke set-specific
operations using normal Chapel method calls:
|
|
For those familiar with Chapel’s sets, the above may look a little
suspect. A Chapel set
can only store a single element type, but
here, we are adding strings to a set that was originally created to
store integers. By using the Python set, we are taking advantage of
Python’s loose typing and can store any type we’d like (provided the
Python
module knows how to convert the data to Python). This
doesn’t result in the same performance and parallelism as native Chapel
sets support, but it provides a new level of flexibility to Chapel
programmers using Python code.
The combination of exposing these new Python types and the
aforementioned ergonomic improvements make the Python
module far
more powerful and easy to use than it was in the previous Chapel 2.3
release. We’re very interested in hearing feedback from users on
their experiences with the module.
Chapel Support for CMake
Chapel 2.4 also contains new support for building Chapel applications with CMake. Previously, users could only use CMake to build Chapel by defining custom commands. We now provide CMake module files to build applications in a more idiomatic way.
Currently, this is enabled by adding the necessary files to your
project. As an example, the following CMakeLists.txt
file builds
a simple Chapel application from two module files, A.chpl
and
B.chpl
:
find_package(chpl REQUIRED HINTS .)
project(myProgram LANGUAGES CHPL)
add_executable(myProgram A.chpl B.chpl)
This results in a project that can be built and executed using the normal CMake workflow:
$ mkdir build && cd build
$ cmake ..
$ make
$ ./myProgram
See the documentation for this new support for more information.
Dyno Support for Chapel Features
As you may have seen in previous release announcements, Dyno is the name of a project whose aim is to modernize and improve the Chapel compiler. Dyno improves error messages, allows incremental type resolution, and enables the development of language tooling. Among the major wins for this ongoing effort is the Chapel Language Server (CLS), which was previously featured on this blog in the post about editor integration. The team has been hard at work implementing many features of Chapel’s type system in Dyno, which—among other things—will enable tools like CLS to provide more accurate and helpful information to users.
This release has seen a significant number of language features come online within Dyno. One major area of focus has been Dyno’s support for arrays and domains. These types are incredibly powerful and implemented using Chapel. As a result, they draw on a lot of the language’s features. The following picture shows an editor in which CLS is displaying the type information computed by Dyno (in grey) for the Chapel program that follows, which relies heavily on inferred types.
release-2.4-arrays.chpl
|
|
In addition to arrays and domains, Dyno is now capable of verifying
interface
constraints.
The manage
statement,
similar to Python’s with
, and built on top of interfaces, is also
now supported. The following program demonstrates resolving a
manage
statement, as well as the error issued when the
contextManager
interface constraint is not met.
release-2.4-interfaces.chpl
|
|
Another group of features that is fun to demonstrate are those that
have to do with the compiler itself. In 2.4, Dyno is able to handle
compilerError()
and
compilerWarning()
,
as well as routines for retrieving the current line number and
filename. The following program demonstrates the use of these:
release-2.4-compiler.chpl
|
|
All of the code presented in this section began resolving in Dyno for the first time in Chapel 2.4, and this doesn’t even remotely cover [note: Over 27 PRs have been merged into the Dyno library since the December release. ] since version 2.3. With each release, Dyno draws nearer to feature parity with the production compiler.
For More Information
If you have questions about Chapel 2.4 or its new features, please reach out on Chapel’s Discord channel, Discourse group, or one of our other community forums. As always, we’re interested in feedback on how we can make the Chapel language, libraries, implementation, and tools more useful to you.