Getting Started¶
As a Python user, you are accustomed to running and having Python readily available on almost every machine you use. Chapel is equivalently portable (and more so). However, since Chapel is an emerging technology, it is not quite part of the standard software stack that comes bundled with your operating system. You therefore need to go ahead and download and install Chapel on your system.
If you are using a popular Linux-based operating system you will most likely be successful by running these commands:
# Download and unpack
cd /tmp
curl -L -O http://sourceforge.net/projects/chapel/files/chapel/1.9.0/chapel-1.9.0.tar.gz
tar xzf chapel-1.9.0.tar.gz
mv /tmp/chapel-1.9.0 ~/chapel
# Build Chapel
cd ~/chapel
make
# Setup your environment, add this command to ~/.bashrc for permanent installation.
source ~/chapel/util/setchplenv.bash
After doing the above you should be able to:
# Compile an example program
chpl -o hello ~/examples/hello.chpl
# Run it
./hello
Running “./hello” should output:
Hello, world!
If you are running MacOSX, Windows, or for some other the reason the above commands does not work for you then consult the official quick start instructions.
Compiling¶
What is that!? A binary! Ohh my…
Chapel is currently a compiled language. However, it lets you write and compile very simple programs. There is no annoying boiler-plate needed to get going.
Python | Chapel | |
---|---|---|
print "Hello, World!"
|
writeln("Hello, World!");
|
And if you like to structure your code, Chapel has neat means for doing so.
Python | Chapel | |
---|---|---|
def main():
print "Hello, World!"
if __name__ == "__main__":
main()
|
module Hello {
proc main() {
writeln("Hello, World!");
}
}
|
All examples in this tutorial / reference guide are compilable. Which means that you can take any snippet and put it into a file like exploring.chpl and compile it:
chpl -o exploring exploring.chpl
Which will create a binary named exploring to execute whatever you have written in exploring.chpl.
Language Basics¶
This section provides an informal language reference. It takes you through the base language features of Python and provides an example of how an equivalent program would be expressed in Chapel.
Variables and Types¶
In Python, variables are implicitly declared and their type determined when they are assigned to. In Chapel, variable declaration is explicit, but the type of the variable can be inferred from its use in a manner equivalent to that of Python.
Python | Chapel | |
---|---|---|
answer = 42
distance = 123.45
computer = "Earth"
|
var answer = 42;
var distance = 123.45;
var computer = "Earth";
|
Types in Python are dynamic, meaning that a variable can change type during its lifetime. The type of a variable in Chapel is static and inferred at compile-time, which means that a type is assigned and cannot be changed at runtime.
Comments¶
Python | Chapel | |
---|---|---|
# Single-line comment
"""
Multi-line comments
"""
|
// Single-line comment
/*
Multi-line comment
*/
|
Literals¶
These work in much the same way that you are used to. A brief overview is provided below.
Python | Chapel | |
---|---|---|
bl = True # Booleans
bl = False
ud = 42 # Unsigned digits
sd = -42 # Signed digits
hd = 0x2A # Hex-Digits
hd = 0X2A
bd = 0b101010 # Binary-Digits
bd = 0B101010
r = 42.0 # Reals
s = '42' # Strings
s = "42"
# Complex / imaginary
z = 1 + 2.0j
# Complex accessors
z.real # For the real part
z.imag # For for imaginary part
|
var bl = true; // Booleans
bl = false;
var ud = 42; // Unsigned digits
var sd = -42; // Signed digits
var hd = 0x2A; // Hex-Digits
hd = 0X2A;
var bd = 0b101010; // Binary-Digits
bd = 0B101010;
var r = 42.0; // Reals
var s = '42'; // Strings
s = "42";
// Complex / imaginary
var z = 1 + 2.0i; // Common
z = (1.0, 2.0):complex; // Alternative syntax
// Complex accessors
z.re; // For the real part
z.im; // For the imaginary part
|
Console input / output¶
You can write to the console (standard output) using write
and writeln
:
Python | Chapel | |
---|---|---|
print "Hello, you." # With a newline
print "Hello, you.", # Without a newline
|
writeln("Hello, you."); // With a newline
write("Hello, you."); // Without a newline
|
You can read input from the console (standard input) using read
and readln
:
Python | Chapel | |
---|---|---|
first_answer = raw_input(
"The Answer to the ultimate question is?\n"
)
print "That is", int(first_answer) == 42
second_answer = raw_input(
"What is the largest biological computer?\n"
)
print "That is", str(second_answer) == "Earth"
|
writeln("The Answer to the Ultimate Question is?");
var first_answer = read(int);
writeln("That is ", first_answer == 42);
writeln("What is the largest biological computer?");
var second_answer = read(string);
writeln("That is ", second_answer == "Earth");
|
Note
Notice that the interface for reading input is quite different, though equally simple. In Python you need to explicitly cast the input, whereas in Chapel the type of the input is provided to the read/readln
functions directly.
Conditionals and Blocks¶
Python is famous for using an indentation guided block-structure, thereby arguably improving readability and increasing consistency of code-style. Chapel uses curly-brackets to denote the start and end of a block.
Python | Chapel | |
---|---|---|
#
light = raw_input("Which color is the traffic light?\n")
if light == "green":
print "You can cross the street now."
if light == "green":
print "You can cross the street now."
else:
print "Wait for the green light."
if light == "green":
print "You can cross the street now."
elif light == "yellow":
print "CAUTION!"
if light == "green":
print "You can cross the street now."
elif light == "yellow":
print "CAUTION!"
else:
print "Do not cross!"
|
writeln("Which color is the traffic light?");
var light = read(string);
if light == "green" {
writeln("You can cross the street now.");
}
if light == "green" {
writeln("You can cross the street now.");
} else {
writeln("Wait for the green light.");
}
if light == "green" {
writeln("You can cross the street now.");
} else if light == "yellow" {
writeln("CAUTION!");
}
if light == "green" {
writeln("You can cross the street now.");
} else if light == "yellow" {
writeln("CAUTION!");
} else {
writeln("Do not cross!");
}
|
Switch / Case¶
Python does not support switch-statements
and instead relies on chaining if-elif-else
statements.
Chapel, on the other hand, does have switch-statements
, specifically select-when-otherwise
statements:
Python | Chapel | |
---|---|---|
#
light = raw_input("Which color is the traffic light?\n")
if light=="green":
print "You can cross the street now."
elif light=="yellow":
print "CAUTION!"
elif light=="red":
print "Do not cross!"
else:
print "WARNING! Traffic-light is broken!"
|
writeln("Which color is the traffic light?");
var light = read(string);
select(light) {
when "green" {
writeln("You can cross the street now.");
}
when "yellow" {
writeln("CAUTION!");
}
when "red" {
writeln("Do not cross!");
}
otherwise {
writeln("WARNING! Traffic-light is broken!");
}
}
|
Note
Notice that in both Python and Chapel these forms of switch-statements
do not fall through, meaning that one and only one case will be executed. Coming from Python, this might not surpise you; however, if you have ever written a switch-statement
in other languages then this may be slightly surprising.
Ranges¶
In Python range
is a list-constructor often used for driving for-loops or list comprehensions. For lowered memory consumption, Python provides the generator equivalent of range
namely xrange
.
In Chapel a range is a language construct which behaves and is used in much the same way as lists are used in Python. Where you would think about lists and slicing operations in Python, think of ranges in Chapel.
Python | Chapel | |
---|---|---|
r1 = xrange(1, 10) # yields 1, 2, 3, 4, 5, 6, 7, 8, 9
r2 = xrange(10, 1) # yields nothing
|
var ns = 1..9; // yields 1, 2, 3, 4, 5, 6, 7, 8, 9
ns = 9..1; // yields nothing
|
Note
Difference in bounds!
- In Python,
range
return values in the interval[start, stop[
. - In Chapel a range-expression yields values the interval
[start, stop]
.
For both languages the above is a shorthand of the wider form: start, stop, step
.
Python | Chapel | |
---|---|---|
# Values in ascending order
r1 = xrange(1, 10, 1) # yields 1, 2, 3, 4, 5, 6, 7, 8, 9
r2 = xrange(1, 10, 2) # yields 1, 3, 5, 7, 9
# Values in descending order
r3 = xrange(9, 0, -1) # yields 9, 8, 7, 6, 5, 4, 3, 2, 1
r4 = xrange(9, 0, -2) # yields 9, 7, 5, 3, 1
|
// Values in ascending order
var ns = 1..9 by 1; // yields 1, 2, 3, 4, 5, 6, 7, 8, 9
ns = 1..9 by 2; // yields 1, 3, 5, 7, 9
// Values in descending order
ns = 1..9 by -1; // yields 9, 8, 7, 6, 5, 4, 3, 2, 1
ns = 1..9 by -2; // yields 9, 7, 5, 3, 1
|
…
Python | Chapel | |
---|---|---|
# No equivalent in Python
|
// Infinite ranges
var one_to_inf = 1..; // yields from one to infinity: 1, 2, 3, 4, 5, ...
var inf_to_one = ..1; // yields from infinity to one: ..., -5, -4, -3 , -2, -1, 0, 1
var inf_to_inf = .. ; // yields from infinity to infinity: ... , ...
|
…
Python | Chapel | |
---|---|---|
# yields 10 values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
ns = xrange(10)
|
// yields 10 values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
var ns = 0.. # 10;
|
Loops¶
Python | Chapel | |
---|---|---|
# Using generators
for i in xrange(1, 10):
print i
|
// Using ranges
for i in 1..10 {
writeln(i);
}
|
Python | Chapel | |
---|---|---|
for i, v in enumerate(['running', 'with', 'scissors']):
print i, v
|
for (i, v) in zip(1.. , ["running", "with", "scissors"]) {
writeln(i, ' ', v);
}
|
Python | Chapel | |
---|---|---|
i = 0
while i<10: # while loop
i += 1
print i
i = 0 # do-while look-a-like loop
cond = True
while cond:
i += 1
print i
cond = i<10
|
var i = 0; // while loop
while i<10 {
i += 1;
writeln(i);
}
i = 0; // do-while loop
do {
i += 1;
writeln(i);
} while(i<10);
|
Functions and Types¶
Python | Chapel | |
---|---|---|
def abs(x):
if x < 0:
return -x
else:
return x
|
proc abs(x) {
if (x < 0) then
return -x;
else
return x;
}
|
Variable arguments? Argument unpacking? Return values? Return type declaration?
Lists, Arrays, Tuples, and Dicts¶
In Python, lists are an essential built-in datastructure. You might be frightened to learn that lists are not particularly useful in Chapel. However, fear not. Many of the uses of lists in Python are handled by ranges, such as driving loops. So if that is your primary concern, then take another look at the description of ranges above.
If you need the ability to have elements of different types in a container such as:
stuff = ['a string', 42, ['another', 'list', 'with', 'strings']]
Then take a look at tuples in the following section.
If you use lists for processing various forms of data of the same type, then what you need are Chapel arrays. Yes, that is correct, Chapel actually has arrays as first-class citizens in the language. Chapel is, to a great extent, all about arrays.
Tuples¶
Tuples work in ways quite familiar to a Python programmer. Tuples are among other things useful for packing and unpacking return-values from functions and having sequences of varying types.
Python | Chapel | |
---|---|---|
coord = ('47.606165', '-122.332233'); # Assignment
print "coord =", coord
## Tuple item access
# - Indexing
print "Latitude =", coord[0], \
", Longitude =", coord[1]
(latitude, longitude) = coord; # - Unpacking
print "Latitude =", latitude, \
", Longitude =", longitude
|
var coord = (47.606165, -122.332233); // Assignment
writeln("coord = ", coord);
/// Tuple item access
// - Indexing
writeln(
"Latitude = ", coord(1),
", Longitude = ", coord(2)
);
var (latitude, longitude) = coord; // - Unpacking
writeln(
"Latitude = ", latitude,
", Longitude = ", longitude
);
|
Note
Indexing scheme of tuples.
- In Python, tuple-indexing is 0-based.
- In Chapel, tuple-indexing is 1-based.
Note
Mutability of tuples.
- In Python, tuples are immutable.
- In Chapel, tuples are mutable.
Arrays¶
This section only scratches the surface of Arrays in Chapel. The use of arrays and concepts related to them are described in greater detail in the section on data parallelism.
Since Python does not support arrays within the language, a comparison to the widespread and popular array-library NumPy is used as a reference instead. The first example below illustrates the creation and iteration over a 10x10
array containing 64-bit floating point numbers.
Python | Chapel | |
---|---|---|
import numpy as np
A = np.zeros((10, 10), dtype=np.float64)
for a in np.nditer(A): # Element iteration
print a
for i in xrange(0, 10): # Index iteration
for j in xrange(0, 10):
print "(%d,%d) = %f" % (i, j, A[i,j])
|
// No need to import, arrays are built-in
var A: [0..9, 0..9] real;
for a in A { // Element iteration
writeln(a);
}
// Index iteration
for (i, j) in A.domain {
writeln("(",i,",",j,") = ",A[i,j]);
}
|
Note
Domains
an unfamiliar concept!
The array syntax and semantics should be easy to follow. The interesting thing to notice is the use of .domain
when doing indexed iteration. A domain
is a powerful concept and you will be very pleased with it once you get to know it. However, it does require an introduction.
A domain
defines a set of indexes. When iterating over the domain associated with an array, as in the example above, you effectively iterate over all the indexes of all elements in the array. You might be accustomed to 0-based
indexing from Python when using lists and tuples. With Chapel you can define whether you want your arrays to be 0-based
or 1-based
.
In the example above, the array is 0-based
since the indexes are defined by the range 0..9
. If you would prefer 1-based
arrays you would define it using the range 1..10
instead.
This is quite a powerful feature. When using arrays as abstractions for matrices, you might find it useful to use 1-based
indexing and in other situations a different indexing scheme. With Chapel you can define the index-set and scheme that is most convenient for the domain you are working within.
Initialization
Python | Chapel | |
---|---|---|
import numpy as np
A = np.arange(1, 11, dtype=np.float64)
print A
|
// No need to import, arrays are built-in
var A: [1..10] real = 1..10;
writeln(A);
|
Whole-array operations.
Python | Chapel | |
---|---|---|
import numpy as np
B = np.random.random((10,10))
C = np.random.random((10,10))
A = B + 2.0 * C
for a in np.nditer(A):
print a
|
use Random;
config const mySeed = SeedGenerator.currentTime; // Allow caller to set seed
var A, B, C: [1..10, 1..10] real;
fillRandom(B, mySeed); // Fill with random values
fillRandom(C, mySeed);
A = B + 2.0 * C; // Whole-array operations
for a in A { // Print the result
writeln(a);
}
|
Reductions and scans
Python | Chapel | |
---|---|---|
import numpy as np
A = np.arange(1, 11, dtype=np.float64)
print np.sum( A ) # Reduction
print np.cumsum( A ) # Scan
|
// No need to import, arrays are built-in
var A: [1..10] real = 1..10;
writeln( +reduce(A) ); // Reduction
writeln( +scan(A) ); // Scan
|
Function promotion
Python | Chapel | |
---|---|---|
import numpy as np
def unary(element):
return element*3
def binary(e1, e2):
return (e1+e2)*3
A = np.arange(1, 11, dtype=np.float64)
B = np.arange(1, 11, dtype=np.float64)
print np.sqrt( A ) # Rely on NumPy ufuncs
print map(unary, A) # Or mapping functions
print map(binary, A, B) # Or mapping functions
|
// No need to import, arrays are built-in
proc unary(element) { // User-defined functions
return element*3;
}
proc binary(e1, e2) {
return (e1+e2)*3;
}
var A, B: [1..10] real = 1..10;
writeln( sqrt(A) ); // Promotion of built-in
writeln( unary(A) ); // Promotion of userdef unary
writeln( binary(A, B) ); // Promotion of userdef binary
|
Dictionaries (Associative Arrays)¶
Dict-comprehension?
Classes and Objects¶
In Python, everything is an object and all objects have a textual representation defined by the object.str(), etc. is there equivalent functionality in Chapel?
Python | Chapel | |
---|---|---|
class Stoplight:
def __init__(self, color):
self.color = color
sl = Stoplight("Green")
print sl.color
|
class Stoplight {
var color: string;
proc Stoplight(color: string) {
this.color = color;
}
}
var sl = new Stoplight("Green");
writeln(sl.color);
|
Organizing Code¶
Python names modules implicitly via the filename convention. Chapel allows you to use the filename, but also allows you to define it explicitly through the “module” directive. You can also define and use submodules, or modules defined within the scope of another module.
Python | Chapel | |
---|---|---|
def main():
pass
if __name__ == "__main__":
main()
|
module Hello {
proc main() {
}
}
|
Python | Chapel | |
---|---|---|
from random import *
# Other means of importing
import random
assert random.Random
from random import Random
|
use Random;
// There are no equivalent means of
// of importing where the namespaces
// are maintained.
|