Chapter 8. Dot-Symbol Overloading

In many programming languages, the so-called dot-notation is commonly associated with selecting a field from a given tuple-value, record-value or object-value. In ATS, field selection can be done through either pattern matching or the use of dot-notation. For example, the following code constructs a flat tuple and also a boxed one, and then uses dot-notation to select their components:

// val tup_flat = @("a", "b") val tup_boxed = $tup("a", "b") // val-"a" = tup_flat.0 and "b" = tup_flat.1 val-"a" = tup_boxed.0 and "b" = tup_boxed.1 //

There is support in ATS for overloading a specified dot-symbol with multiple function names so that dot-notation can be employed to call these functions, resulting in code that reads like field selection from tuples or records. This style of calling functions can, sometimes, make the code written in ATS more easily accessible, and it is especially so when ATS interacts with languages that support object-oriented programming.

As an example of dot-notation in overloading, let us introduce a non-linear abstract type point for points in a 2-dimensional space and also declare some associated functions:

// abstype point = ptr // boxed // extern fun point_make (x: double, y: double): point // extern fun point_get_x (p: point): double and point_get_y (p: point): double // extern fun point_set_x (p: point, x: double): void and point_set_y (p: point, x: double): void //

For getting the x-coordinate and y-coordinate of a given point, the functions point_get_x and point_get_y can be called, respectively. For setting the x-coordinate and y-coordinate of a given point, the functions point_set_x and point_set_y can be called, respectively. By introducing two dot-symbols .x and .y and then overloading them with certain function names as follows:

symintr .x .y overload .x with point_get_x overload .x with point_set_x overload .y with point_get_y overload .y with point_set_y

we can use dot-notation to call the corresponding get-functions and set-functions as is shown in the following code:

val p0 = point_make (1.0, ~1.0) val x0 = p0.x() // point_get_x (p0) and y0 = p0.y() // point_get_y (p0) val () = p0.x := y0 // point_set_x (p0, y0) and () = p0.y := x0 // point_set_y (p0, x0)

Note that writing p0.x for p0.x() is currently not supported. The dot-notation in any assigement is only allowed to refer to a function that returns the void-value. In the above example, both point_set_x and point_set_y return the void-value.

Let us introduce a linear abstract type counter for counter objects and a few functions associated with it:

// absvtype counter = ptr // extern fun counter_make (): counter extern fun counter_free (counter): void // extern fun counter_get (cntr: !counter): int extern fun counter_incby (cntr: !counter, n: int): void //

As can be expected, the functions counter_make and counter_free are for creating and destroying a counter object, respectively. The function counter_get returns the current count stored in a give counter, and the function counter_incby increase that count by a given integer value.

Let us introduce the following overloading declarations:

// overload .get with counter_get overload .incby with counter_incby //

As is expected, one can now call counter_get as follows:

val n0 = c0.get() // = counter_get(c0)

Similarly, one can call counter_incby as follows to increase the count stored in c0 by 1:

val () = c0.incby(1) // = counter_incby(c0, 1)

If we revisit the previous example involving (non-linear) points, then we can see that the following code also typechecks:

val p0 = point_make (1.0, ~1.0) val x0 = p0.x() // point_get_x (p0) and y0 = p0.y() // point_get_y (p0) val () = p0.x(y0) // point_set_x (p0, y0) and () = p0.y(x0) // point_set_y (p0, x0)

I may use the phrase functional dot-notation to refer to this form of dot-notation so as to differentiate it from the general form of dot-notation.

Please find on-line the entirety of the code presented in this chapter plus some testing code.