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"}
Warning: Be careful if comparing arrays. Gosu performs recursive comparison of individual elements for compatible array types.

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.

if

Type names, including class names

Uppercase first character

DateUtil

Claim

Local variable names

Lowercase first character

myClaim

Property names

Uppercase first character

CarColor

Method names

Lowercase first character

printReport

Package names

Lowercase all letters in packages and subpackages

com.mycompany.*

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

Use Boolean-valued expressions. For example:
return ("A".equalsIgnoreCase(PR.cnaPaymentOption.Code))
Use the boolean data type. For example:
var isHoldIssuance = true, raiseEval = false // instead of isHoldIssuance = "Y", raiseEval = "N" 
if (isHoldIssuance and raiseEval) {
  PolicyRevision.raiseEvalIssue( ... )
}

Class names

Apply 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

Use the 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

Only use casts when necessary. For example, casting can be avoided here:
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 } )
Note: The return syntax is required if the lack of return causes syntactical ambiguity. For example, return syntax is required when the return type is an array.

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

Avoid loops if possible. For example, rather than testing within a for statement, apply the test without looping:
if (PR.WorkersCompLineExists and "NJ".equalsIgnoreCase(PR.BaseState)) {  
  return false
}
Use a return or break statement when possible to exit a loop immediately. For example:
for (mSC in SCs) {
  if (checkAvailability(mSC, ST)) { return true }
}
return false
var bFound: Boolean = false
for (cov in covs) {
  if (cov.Selected == true) {
    bFound = true
    break // exit loop early after found
  }
}
if (!bFound) { ... }

Direct references

Avoid entity literals if possible. For example:
if (PolRev.Policy.Product.Code == "Connect") { ... } // instead of (PolRev.Policy.Product == Product( "Connect" )
Avoid unnecessary object graph traversals. For example:
var allCovs = PolicyLine.AllCoverages // instead of PolicyLine.PolicyRevision.BusinessAutoLine.AllCoverages

Searching

Use getFirstResult() and getAtMostOneRow() methods on find() results. For example:
var results = find (fp in FormPattern where ...)
return results.getFirstResult()
Use the 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) {
  ...
}
Note: To determine whether find() results are empty, test using the expression getFirstResult() == null rather than getCount() == 0.
Add restrictions to 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()
}
Use a 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(...)
  }
}
Evaluate constant expressions first to avoid recomputation. For example:
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