Standard Module Style¶
This document describes general guidance for style when writing a standard module. Of course, there will be exceptions to this guidance when there is a good reason.
PascalCase and camelCase¶
Prefer PascalCase or camelCase to separating_with_underscores. The rest of this document will use PascalCase or camelCase to signify the pattern of indicating new words with an upper case letter and starting the identifier; where PascalCase starts the name with an upper case letter and camelCase starts with a lower case letter.
When including acronyms in PascalCase or camelCase, treat the acronym as if it were a regular word. There is one exception - if using PascalCase and the acronym is the entire symbol name, then let the acronym be all uppercase.
- Here are a few examples:
“rendered HTML DOM” in PascalCase is RenderedHtmlDom
“rendered HTML DOM” in camelCase is renderedHtmlDom
“HTML” in PascalCase is HTML
“HTML” in camelCase is html
- Exceptions:
The following are exceptions to the above capitalization guidelines that occur in the standard modules or packages:
numPUs
(num Processor-Units)
Our hope and intention is to update our rules over time so that they cover cases like this, obviating the need to call it out as an exception. As more exceptions accumulate, rules may be amended to cover multiple cases at once.
Modules¶
Module names should be PascalCase.
Note
Chapel does not use a separate namespace for types vs. module names. As a result, it’s generally a goal to avoid using the same name for, say, a type and a module.
Classes¶
Class type names should be PascalCase. The idea is that starting with an uppercase letter is the convention for by-reference types.
Records¶
Record type names should be camelCase. The idea is that starting with a lowercase letter is the convention for by-value types.
Enums¶
Enum type names should be camelCase. The enum values should also be camelCase.
Functions and Methods¶
Function and method names should be camelCase.
Generally speaking, it’s desirable to use methods on a value (vs functions that aren’t methods) when there is a clear type responsible for the operation.
Use parentheses-less methods only for returning properties that could be
reasonably implemented as fields. However, if such a method is named
isXYZ or hasXYZ it should use parentheses (so, use
proc isReal() { ... }
rather than proc isReal { ... }
).
Parentheses-less functions that aren’t methods should be avoided.
Many paren-ful methods take some notable action. Try to make these methods method names be a verb. In particular, a method that modifies an argument in-place should be a verb.
When possible, functions and methods that return should declare their return
type explicitly. When not possible, such functions and methods should use the
:returns:
documentation field (as described in
Argument and return/yield types).
Factory Functions and Methods¶
Factory functions or methods are procedures where the main purpose of that procedure is to create a new object or value. There are many other procedures that return a value but constructing that value is not the main purpose of the function. For example, ‘open’ and ‘spawn’ are not factory functions – the main action of these methods is to work with the OS to open a file or launch a subprocess.
In many cases factory procedures are not needed. Regular initializers
will work just fine to support the pattern of new Something(...)
.
Type conversion is best supported by creating a cast (:
) operator
overload.
One case in which factory procedures are useful is when it is awkward to distinguish between several ways of creating something based on the initializer arguments alone.
Factory procedures should have a name that begin with ‘create’.
Factory procedures can be functions or type methods. For example, we could have:
createStringWithBorrowedBuffer
or
string.createBorrowingBuffer
.
Accessors¶
Accessor methods are methods intended to return a distinct piece of information about the type on which they are defined. This piece of information could be reasonably implemented as a field. Typically, such methods are defined without parentheses, allowing them to appear similar to a field access. However, there is no requirement that the contents of the accessor be similarly simple - whether the information returned is calculated based on the internal fields of the type or whether it is actually a field is an implementation detail.
Accessor methods will avoid using “get” in their name. E.g., instead of
array.getIdxType
, the accessor is named array.idxType
.
Methods that are not accessors are still allowed to use “get” in their name.
Formals¶
Formal names should be camelCase or PascalCase. Descriptive names are recommended, within reason. Encoding the type name into a formal name is generally avoided when multiple types are supported, as doing so makes it harder to support generic functions.
Some commonly used names are:
idx
for an indexx
andy
for general pairs of formalssrc
anddst
/dest
(source and destination) for directional pairs of formalslhs
andrhs
(left hand side and right hand side) may also be used
eltType
for the type of elements stored in a collection (also used as a field name)obj
for a general object instance (as opposed to formals that can also be primitive types)
Other Identifiers¶
Variables and fields should be camelCase or PascalCase.
Handling Failure¶
In general, when code in a library encounters erroneous behavior, an appropriate
Error
should be thrown (see the spec description for error handling in general). This will enable
users of the library to potentially recover from the bad behavior without having
to restart their program.
There are some known exceptions to this recommendation:
When division by zero is encountered, libraries will halt (unless the check for division by zero has been disabled, see the flag’s man page entry).
When accesses are made outside of the known bounds of a container type, libraries will halt (unless bounds checking has been disabled, see the flag’s man page entry).
Other checks controlled by flags that are listed when running
chpl -h
.When the program runs out of memory and the function in question does not already throw other types of errors, the library will halt.