Types¶
Chapel is a statically typed language with a rich set of types. These include a set of predefined primitive types, enumerated types, structured types (classes, records, unions, tuples), data parallel types (ranges, domains, arrays), and synchronization types (sync, single, atomic).
The syntax of a type is as follows:
type-expression:
primitive-type
enum-type
structured-type
dataparallel-type
synchronization-type
lvalue-expression
if-expression
unary-expression
binary-expression
Many expressions are syntactically allowed as a type; however not all expressions produce a type. For example, a call to a function is syntactically allowed as the type of a variable. However it would be an error for that call to result in a value (rather than a type) in that context.
Programmers can define their own enumerated types, classes, records, unions, and type aliases using type declaration statements:
type-declaration-statement:
enum-declaration-statement
class-declaration-statement
record-declaration-statement
union-declaration-statement
type-alias-declaration-statement
These statements are defined in Sections Enumerated Types, Class Declarations, Record Declarations, Union Declarations, and Type Aliases, respectively.
Primitive Types¶
The concrete primitive types are: void
, nothing
, bool
,
int
, uint
, real
, imag
, complex
, string
and
bytes
. They are defined in this section.
In addition, there are several generic primitive types that are described in Built-in Generic Types.
The primitive types are summarized by the following syntax:
primitive-type:
`void'
`nothing'
`bool' primitive-type-parameter-part[OPT]
`int' primitive-type-parameter-part[OPT]
`uint' primitive-type-parameter-part[OPT]
`real' primitive-type-parameter-part[OPT]
`imag' primitive-type-parameter-part[OPT]
`complex' primitive-type-parameter-part[OPT]
`string'
`bytes'
`enum'
`record'
`class'
`owned'
`shared'
`unmanaged'
`borrowed'
primitive-type-parameter-part:
( integer-parameter-expression )
integer-parameter-expression:
expression
If present, the parenthesized integer-parameter-expression
must
evaluate to a compile-time constant of integer type.
See Compile-Time Constants
Open issue.
There is an expectation of future support for larger bit width primitive types depending on a platform’s native support for those types.
The Void Type¶
The void
type is used to represent the lack of a value, for example
when a function has no arguments and/or no return type. It is an error
to assign the result of a function that returns void
to a variable.
The Nothing Type¶
The nothing
type is used to indicate a variable or field that should
be removed by the compiler. The value none
is the only value of type
nothing
.
The value none
can only be assigned to a variable of type
nothing
, or to a generic variable that will take on the type
nothing
. The variable will be removed from the program and have no
representation at run-time.
Rationale.
The
nothing
type can be used to conditionally remove a variable or field from the code based on aparam
conditional expression.
The Bool Type¶
Chapel defines a logical data type designated by the symbol bool
with the two predefined values true
and false
. This default
boolean type is stored using an implementation-defined number of bits. A
particular number of bits can be specified using a parameter value
following the bool
keyword, such as bool(8)
to request an 8-bit
boolean value. Legal sizes are 8, 16, 32, and 64 bits.
Some statements require expressions of bool
type and Chapel supports
a special conversion of values to bool
type when used in this
context (Implicit Statement Bool Conversions).
Signed and Unsigned Integral Types¶
The integral types can be parameterized by the number of bits used to
represent them. Valid bit-sizes are 8, 16, 32, and 64. The default
signed integral type, int
, and the default unsigned integral type,
uint
correspond to int(64)
and uint(64)
respectively.
The integral types and their ranges are given in the following table:
Type | Minimum Value | Maximum Value |
---|---|---|
int(8) | -128 | 127 |
uint(8) | 0 | 255 |
int(16) | -32768 | 32767 |
uint(16) | 0 | 65535 |
int(32) | -2147483648 | 2147483647 |
uint(32) | 0 | 4294967295 |
int(64), int | -9223372036854775808 | 9223372036854775807 |
uint(64), uint | 0 | 18446744073709551615 |
The unary and binary operators that are pre-defined over the integral types operate with 32- and 64-bit precision. Using these operators on integral types represented with fewer bits results in an implicit conversion to the corresponding 32-bit types according to the rules defined in Implicit Conversions.
Real Types¶
Like the integral types, the real types can be parameterized by the
number of bits used to represent them. The default real type, real
,
is 64 bits. The real types that are supported are machine-dependent, but
usually include real(32)
(single precision) and real(64)
(double
precision) following the IEEE 754 standard.
Imaginary Types¶
The imaginary types can be parameterized by the number of bits used to
represent them. The default imaginary type, imag
, is 64 bits. The
imaginary types that are supported are machine-dependent, but usually
include imag(32)
and imag(64)
.
Rationale.
The imaginary type is included to avoid numeric instabilities and under-optimized code stemming from always converting real values to complex values with a zero imaginary part.
Complex Types¶
Like the integral and real types, the complex types can be parameterized
by the number of bits used to represent them. A complex number is
composed of two real numbers so the number of bits used to represent a
complex is twice the number of bits used to represent the real numbers.
The default complex type, complex
, is 128 bits; it consists of two
64-bit real numbers. The complex types that are supported are
machine-dependent, but usually include complex(64)
and
complex(128)
.
re
and im
. The type of these components is real. The standard
Math
module provides some functions on complex types. SeeExample.
Given a complex number
c
with the value3.14+2.72i
, the expressionsc.re
andc.im
refer to3.14
and2.72
respectively.
The String Type¶
Strings are a primitive type designated by the symbol string
comprised of Unicode characters in UTF-8 encoding. Their length is
unbounded.
Open issue.
There is an expectation of future support for fixed-length strings.
The Bytes Type¶
Bytes is a primitive type designated by the symbol bytes
comprised
of arbitrary bytes. Bytes are immutable in-place and their length is
unbounded.
Open issue.
There is an expectation of future support for mutable bytes.
Enumerated Types¶
Enumerated types are declared with the following syntax:
enum-declaration-statement:
`enum' identifier { enum-constant-list }
enum-constant-list:
enum-constant
enum-constant , enum-constant-list[OPT]
enum-constant:
identifier init-part[OPT]
init-part:
= expression
The enumerated type can then be referenced by its name, as summarized by the following syntax:
enum-type:
identifier
An enumerated type defines a set of named constants that can be referred to via a member access on the enumerated type. Each enumerated type is a distinct type.
If the init-part
is omitted for all of the named constants in an
enumerated type, the enumerated values are abstract and do not have
associated integer values. Any constant that has an init-part
will
be associated with that integer value. Such constants must be parameter
values of integral type. Any constant that does not have an
init-part
, yet which follows one that does, will be associated with
an integer value one greater than its predecessor. An enumerated type
whose first constant has an init-part
is called concrete, since
all constants in the enum will have an associated integer value, whether
explicit or implicit. An enumerated type that specifies an init-part
for some constants, but not the first is called semi-concrete. Numeric
conversions are automatically supported for enumerated types which are
concrete or semi-concrete
(see Explicit Enumeration Conversions).
Example (enum-statesmen.chpl).
The code
enum statesman { Aristotle, Roosevelt, Churchill, Kissinger }defines an abstract enumerated type with four constants. The function
proc quote(s: statesman) { select s { when statesman.Aristotle do writeln("All paid jobs absorb and degrade the mind."); when statesman.Roosevelt do writeln("Every reform movement has a lunatic fringe."); when statesman.Churchill do writeln("A joke is a very serious thing."); when statesman.Kissinger do { write("No one will ever win the battle of the sexes; "); writeln("there's too much fraternizing with the enemy."); } } }outputs a quote from the given statesman. Note that enumerated constants must be prefixed by the enumerated type name and a dot unless a use statement is employed (see The Use Statement and Using Modules).
It is possible to iterate over an enumerated type. The loop body will be invoked on each named constant in the enum. The following method is also available:
-
proc
enum.
size
: param int¶ Returns the number of constants in the given enumerated type.
-
proc
enum.
first
: enum¶ Returns the first constant in the enumerated type.
-
proc
enum.
last
: enum¶ Returns the last constant in the enumerated type.
Structured Types¶
The structured types are summarized by the following syntax:
structured-type:
class-type
record-type
union-type
tuple-type
Classes are discussed in Classes. Records are discussed in Records. Unions are discussed in Unions. Tuples are discussed in Tuples.
Class Types¶
A class can contain variables, constants, and methods.
Classes are defined in Classes. The class type can also contain type aliases and parameters. Such a class is generic and is defined in Generic Types.
A class type C
has several variants:
C
andC?
owned C
andowned C?
shared C
andshared C?
borrowed C
andborrowed C?
unmanaged C
andunmanaged C?
The variants with a question mark, such as owned C?
, can store
nil
(see Nilable Class Types). Variants without a
question mark cannot store nil
. The keywords owned
, shared
,
borrowed
, and unmanaged
indicate the memory management strategy
used for the class. When none is specified, as with C
or C?
, the
class is considered to have generic memory management strategy.
See Class Types.
Record Types¶
Records can contain variables, constants, and methods. Unlike class types, records are values rather than references. Records are defined in Records.
Data Parallel Types¶
The data parallel types are summarized by the following syntax:
dataparallel-type:
range-type
domain-type
mapped-domain-type
array-type
index-type
Ranges and their index types are discussed in Ranges. Domains and their index types are discussed in Domains. Arrays are discussed in Arrays.
Synchronization Types¶
The synchronization types are summarized by the following syntax:
synchronization-type:
sync-type
single-type
atomic-type
Sync and single types are discussed in Synchronization Variables. The atomic type is discussed in Atomic Variables.
Type Aliases¶
Type aliases are declared with the following syntax:
type-alias-declaration-statement:
privacy-specifier[OPT] `config'[OPT] `type' type-alias-declaration-list ;
external-type-alias-declaration-statement
type-alias-declaration-list:
type-alias-declaration
type-alias-declaration , type-alias-declaration-list
type-alias-declaration:
identifier = type-expression
identifier
A type alias is a symbol that aliases the type specified in the
type-expression
. A use of a type alias has the same meaning as using
the type specified by type-expression
directly.
Type aliases defined at the module level are public by default. The
optional privacy-specifier
keywords are provided to specify or
change this behavior. For more details on the visibility of symbols, see
Visibility Of A Module’s Symbols.
If the keyword config
precedes the keyword type
, the type alias
is called a configuration type alias. Configuration type aliases can be
set at compilation time via compilation flags or other
implementation-defined means. The type-expression
in the program is
ignored if the type-alias is alternatively set.
If the keyword extern
precedes the type
keyword, the type alias
is external. The declared type name is used by Chapel for type
resolution, but no type alias is generated by the backend. See the
chapter on interoperability
(Interoperability) for more information on
external types.
The type-expression
is optional in the definition of a class or
record. Such a type alias is called an unspecified type alias. Classes
and records that contain type aliases, specified or unspecified, are
generic (Type Aliases in Generic Types).
Example (type-alias.chpl).
The declaration
type t = int;defines a
t
as a synonym for the typeint
. Functions and methods available onint
will apply to variables declared with typet
. For example,var x: t = 1; x += 1; writeln(x);will print out
2
.