Coding style
This topic lists some recommended coding practices for the Gosu language. These guidelines encourage good programming practices that improve Gosu readability and encourage code that is error-free, understandable, and maintainable by other people.
Use the following recommendations to improve the clarity and readability of your Gosu code.
Omit semicolons
Omit semicolons as line terminators, because they are unnecessary in almost all cases. Gosu code looks cleaner without semicolons.
Semicolons are only needed if you need to separate multiple Gosu statements all written on a single line in a one-line statement list. This construction is generally not recommended, but is sometimes appropriate for simple statement lists declared in-line in Gosu block definitions.
Type declarations
Omit the type declaration if you declare variables with an assignment. Instead, use
as TYPE where appropriate. The type declaration is
particularly redundant if a value must be cast to a type that is already included at the end
of the Gosu statement.
The recommended type declaration style is:
var delplans = currentPage as DelinquencyPlans
Do not add the redundant type declaration:
var delplans : DelinquencyPlans = currentPage as DelinquencyPlans
The == and != operator recommendations and
warnings
The Gosu == and != operators are safe to
use even if one side evaluates to null.
Use these operators where possible instead of using the equals method on objects.
The Gosu == operator is equivalent to the object method
equals(obj1.equals(obj2)) but the == operator is
null-safe. The null-safety of the Gosu == operator is similar to the
null-safety of the Java code ObjectUtil.equals(...). In contrast, for both
the Gosu and Java languages, the object method
myobject.equals(...) is not null-safe.
Consider the following Gosu code that uses the equals method:
( planName.equals( row.Name.text ) )
Using the == operator is more readable, as shown in the following
line:
( planName == row.Name.text) )
Gosu prevents you from comparing incompatible array types for equality with the
== and != operators. For example, the following code
generates a compile-time error because arrays of numbers and strings are incompatible:
new Number[] {1,2} == new String[] {"1","2"}
If the array types are comparable, Gosu does not recursively apply implicit coercion rules
on the array’s elements. For example, the following code evaluates to false
because a Number is a subclass of Object, so Gosu compares
the individual elements of the table:
new Number[] {1,2} == new Object[] {"1","2"}
You can also compare objects by using the === and !==
operators.
Capitalization conventions
The following table lists conventions for capitalization of various Gosu language elements:
|
Language element |
Standard capitalization |
Example |
|---|---|---|
|
Gosu keywords |
Always specify Gosu keywords correctly, typically lowercase. |
|
|
Type names, including class names |
Uppercase first character |
|
|
Local variable names |
Lowercase first character |
|
|
Property names |
Uppercase first character |
|
|
Method names |
Lowercase first character |
|
|
Package names |
Lowercase all letters in packages and subpackages |
|
Because Gosu is case-sensitive, you must access existing types exactly as they are declared, including correct capitalization. Capitalization in the middle of a name is also important.
Use the Gosu editor’s code completion feature to enter the names of types and properties correctly. This ensures standard capitalization.
Some entity and typelist APIs are case insensitive if they take a
String value for the name of an entity, a property, or a typecode.
However, best practice is to pass the name exactly as declared.
Booleans
return ("A".equalsIgnoreCase(PR.cnaPaymentOption.Code))boolean data type. For
example:var isHoldIssuance = true, raiseEval = false // instead of isHoldIssuance = "Y", raiseEval = "N"
if (isHoldIssuance and raiseEval) {
PolicyRevision.raiseEvalIssue( ... )
}Class names
uses declarations to avoid specifying fully qualified class names.
For example:
uses com.cna.pc.webservices.plugin.AccountPlugin
...
var plugin: AccountPlugin = new AccountPlugin()Array usage
length property instead of
Libraries.Array.length(). For example:
var len = baLine.AllBAVehicleEUs.length // instead of libraries.Array.length( baLine.AllBAVehicleEUs )Class variable and class property recommendations
Always prefix private and protected class variables
with an underscore character (_).
Avoid public variables. Convert public variables to properties, so that the property name, which is the interface to other code, is separated from the storage and retrieval of the value.
Although Gosu supports public variables for compatibility with other languages, the
standard Gosu style is to use public properties backed by private variables rather than
public variables. Gosu syntax supports creating the local variable and the public property
on the same line by using the as keyword followed by the property name.
For example, in your Gosu classes that define class variables, use this variable declaration syntax:
private var _firstName : String as FirstName
This line declares a private variable called _firstname, which Gosu
exposes as a public property called FirstName.
Do not create a public variable, like the following line:
public var FirstName : String // Do not do this. Public variable scope is not Gosu standard style.
Casting
var searchResults = find (code in ProducerCode where ...)
for (code in searchResults) {
if ((code).getUWBranch() == this) {} // instead of if ((code as ProducerCode).getUWBranch() ...
}Use typeis inference
To improve the readability of your Gosu code, Gosu automatically downcasts after a
typeis expression if the type is a subtype of the original type. This
behavior is particularly valuable for if statements and similar Gosu
structures. In the Gosu code that the if statement bounds, you do not need
to cast by using an as TYPE expression to the subtype.
Because Gosu confirms that the object has the more specific subtype, Gosu implicitly
considers that variable’s type to be the subtype, within that block of code.
The structure of this type inference looks like the following code:
var VARIABLE_NAME : TYPE_NAME
// This comparison assumes SUBTYPE_NAME is a subtype of TYPE_NAME
if (VARIABLE_NAME typeis SUBTYPE_NAME) {
// Use the VARIABLE_NAME as SUBTYPE_NAME in this block without casting
}
For example, the following example declares a variable as an Object, but
downcasts the type to String in a block of code within an
if statement.
Because of downcasting, the following code is valid:
var x : Object = "nice"
var strlen = 0
if ( x typeis String ) {
strlen = x.length
}
This code works because the typeis inference is effective immediately and
propagates to adjacent expressions.
Note that length is a property on String, not
Object. By using typeis to downcast from
Object to String, you do not need an additional cast
around the variable x. The following code is equivalent but has an
unnecessary cast:
var x : Object = "nice"
var strlen = 0
if ( x typeis String ) {
strlen = (x as String).length // "length" is a property on String, not Object
}
Use automatic downcasting to write readable, concise Gosu code. Do not write Gosu code with unnecessary casts.
See also
Indenting
The recommended base indentation level is two spaces, as shown in this example:
class BankAccount {
private var _numberOfAccounts = 0 // object-level (instance) variables are preceded by an underscore
}
Wrapping
Ensure that wrapped lines and chained method calls have an extra indentation of four spaces
from the previous line. In the following example,
verifyPolicyInvoiceItemMatching and returnToStatements
are indented four spaces from yourAccountBalance.goToStatement(), and
new BigDecimal is indented four spaces from
verifyPolicyInvoiceItemMatching:
yourAccountBalance.goToStatement( secondStatementDate )
.verifyPolicyInvoiceItemMatching( policyPeriod, new BigDecimal( "250.00" ),
new BigDecimal( "25.00" ), "10.0%", new BigDecimal( "225.00" ) )
.returnToStatements()
Braces
Place opening curly brackets, or braces, on the same line as the function or block declaration. Place closing braces on their own line, lining up with the line the opening curly brace:
function giveMeMoney( yourAccount : Account, myAccount : Account, amount : BigDecimal ) : Boolean {
var yourAccountBalance = TabBar.goToAccount( yourAccount ).balance()
}
Comment headers
Format comment blocks as follows:
- Use an initial
/**and close with an*/. - Precede comments with an
*. - Indent
/**with two spaces and the content with three spaces, so that the content*lines up with the opening/**:
/**
* Method-level comments go here, you should be answering the questions:
* Why does this method exist?
* Is there anything special about it?
* Other comments, and so on.
*/
Arguments
When using argument lists, precede the first argument with a space and insert a space after the last argument:
someFunction( 10, "foo", "bar" )
Blocks
Short names are preferable for block parameter names. In this example, e
is preferable to event:
var names = employees.map( \ e -> e.Name )
Always favor expression bodies over statement bodies. In this example,
e.Name is favored over { return e.Name }:
var names = employees.map( \ e -> e.Name )
var names = employees.map( \ e -> { return e.Name } )
When creating block argument lists, use:
- a space preceding the first argument
- a space after the last argument.
- a space after the right arrow
- a space after the block body
sampleFunction( \ arg1, arg2, arg3 -> print( arg1 + arg2 + arg3 ) )
Loops
for
statement, apply the test without
looping:if (PR.WorkersCompLineExists and "NJ".equalsIgnoreCase(PR.BaseState)) {
return false
}return or break statement when possible to exit a
loop immediately. For
example:for (mSC in SCs) {
if (checkAvailability(mSC, ST)) { return true }
}
return falsevar bFound: Boolean = false
for (cov in covs) {
if (cov.Selected == true) {
bFound = true
break // exit loop early after found
}
}
if (!bFound) { ... }
Direct references
if (PolRev.Policy.Product.Code == "Connect") { ... } // instead of (PolRev.Policy.Product == Product( "Connect" )var allCovs = PolicyLine.AllCoverages // instead of PolicyLine.PolicyRevision.BusinessAutoLine.AllCoveragesSearching
getFirstResult() and getAtMostOneRow() methods on
find() results. For example:
var results = find (fp in FormPattern where ...)
return results.getFirstResult()getCount() method rather than iterating over all
find() results to determine the number of results returned. For
example:var rs = find(...)
if (rs.getCount() > 100) {
...
}find() results are empty, test using the
expression getFirstResult() == null rather than getCount() ==
0.find() method parameters to reduce the number of
results returned. For example:
var rs = find(ps in cnaSicNaicsXref where ... and ps.naics_code == Location.IndustryCode.Code)
var xref = rs.getFirstResult()
if (xref != null) {
print(...)
actions.exitToNext()
}HashSet or HashMap to avoid O(n^2) algorithms. For
example:
var vins = new java.util.HashSet()
for (eu in baLine.AllBAVehicleEUs) {
if (!vins.add(eu.Vin)) {
PolicyLine.rejectWithFlowStep(...)
}
}if (Location.IndustryCode.Domain == "NAICS") {
var rs = find(ps in cnaSicNaicsXref where ...)
for (e in rs) {
if (...) {
print(...)
actions.exitToNext()
}
}Eliminate
common sub-expressions. For
example:var baOwnedLiabCov = cov as BAOwnedLiabilityCov
var pubID = baOwnedLiabCov.BAOwnedLiabilityLiabTerm.PackageValue.PublicID
if (pubId == "ba:13" or pubID == "ba:14"){ ... }See also
