Native Syntax Extension Capability

This section describes the application of some native Cangjie syntax features in eDSL construction. Codes written using these syntaxes constitute not only eDSL programs complying with domain habits and having domain-specific meanings, but also legal Cangjie programs. Most of these syntaxes are described in the Efficient Programming section. Here, we focus on their functions in constructing eDSLs.

Type Extension and Attribute

Type extension allows you to add new functions to the original type without intrusive modification, especially for native types of languages and types defined by external libraries. Such extension improves the usability of types. The attribute mechanism provides getter and setter support for field access and hides data access details. However, getter and setter can be implicitly called using the syntax similar to that for directly accessing fields. With these two features, you can write some programs that can naturally express domain-specific meanings. For example, in the scenario where a book is borrowed from a library, we want to construct an expression similar to the natural language indicating that the book return time is two weeks later. The expected expression is as follows:

var bookReturnDate: DateTime = 2.weeks.later

You can use attributes to extend Int64:

extend Int64 {
    prop weeks: Int64 {
        get() {
            this * 7
        }
    }

    prop later: DateTime {
        get() {
            DateTime.now() + Duration.day * this
        }
    }
}

Named Parameters and Default Values

During eDSL construction, parameters need to be configured for some objects. The following may occur:

  • There are many parameters, of which the configuration sequence may be incorrect.
  • You do not want to set all parameters each time, and need to use the default values in most cases.

In these scenarios, you can solve the problems based on the features of named parameters and default values. For example, to set the size of a rectangular zone on a plane, you need to determine the top, bottom, left, and right positions of the rectangular zone. Generally, the top and left coordinates are 0 by default. The implementation is as follows:

class Rect {
    static func zone(top!: Int64 = 0, left!: Int64 = 0, bottom!: Int64, right!: Int64): Rect {
        //
    }
}

In this way, the rectangular zone can be configured more clearly. For example, the following call modes are allowed:

Rect.zone(bottom: 10, right: 10) // Uses the default values for **top** and **left**.
Rect.zone(top: 5, bottom: 10, right: 10) // Uses the default value for **left**.
Rect.zone(right: 10, bottom: 10) // You do not need to remember the parameter sequence.

Operator Overloading

Operator overloading enables objects of non-numeric types to implement the syntax of arithmetic operators. For example, in the library example, you can write:

DateTime.now() + Duration.day * this

In the Cangjie standard library, the add (+) operation of DateTime and the multiply (*) operation of Duration are reloaded. For example:

//DateTime
public operator func +(r: Duration): DateTime

//Duration
public operator func *(r: Int64): Duration

Trailing Lambda

The concept of trailing lambda is introduced in preceding sections, and its use is described from the perspective of constructing DSL. Here is an example of declarative UI. You can use trailing lambda to express the hierarchical relationship between components and construct an HTML-like expression paradigm.

Column {
    Image()
    Row {
        Text()
        Text()
    }
}

Column is a call to the Column function, and the braces are the lambda expression of Cangjie, which is the parameter of the Column function call provided in the trailing lambda mode. The same syntax is used for Row.

Keyword Omission

eDSL syntax noise refers to syntaxes introduced by the host language but irrelevant to actual service abstraction in the domain. The syntax noise affects the readability of the eDSL. Cangjie allows omission of the following: new during object construction, ";" at the end of a line, and return in function returns. This further simplifies the eDSL expression and reduces the syntax noise.