ATS has record types which are like tuples but each item is referenced via a name. They closely approximate C structs and in the generated C code are represented in this way. The following uses a record to hold x and y values representing a point on a 2D plane:

fun print_point (p: @{x= int, y= int}): void = printf("%d@%d

", @(p.x, p.y)) implement main () = { val p1 = @{x=10, y=20} val () = print_point (p1) }

A literal record object in this example is created using the @{ ... } syntax. Dereferencing record fields is done using ' . '. The generated C code shows that the representation for the record is a C struct:

typedef struct { ats_int_type atslab_x ; ats_int_type atslab_y ; } anairiats_rec_0 ;

The @ syntax used for the record literal and type marks the record as a 'flat' record. Flat records have value semantics and variables of this type have a size in bytes equivalent to the size of the underlying C structure. This is shown by the generated C code for the main function above:

ATSlocal (anairiats_rec_0, tmp4) ; __ats_lab_mainats: tmp4.atslab_x = 10 ; tmp4.atslab_y = 20 ; /* tmp3 = */ print_point_0 (tmp4) ;

Here the variable tmp4 is the p1 in our ATS code. It is an instance of the C struct representing the record and is created on the stack. It is initialized and passed to the print_point function by value:

ats_void_type print_point_0 (anairiats_rec_0 arg0) { ATSlocal (ats_int_type, tmp1) ; ATSlocal (ats_int_type, tmp2) ; ... }

Records can also be defined using a '{...} syntax (note the ' instead of @ ) for boxed records which are heap allocated and the memory managed by the garbage collector. Boxed records have pointer semantics and have a size in bytes equivalent to the size of a pointer:

implement main () = { val x = sizeof<@{x=int}> val y = sizeof<'{x=int}> val a = int_of_size x val b = int_of_size y val () = printf ("%d %d

", @(a, b)) }

This outputs "4 8" showing the flat type as having the size of an int and the boxed type having the size of a pointer. Most usage of records in ATS I've done is using flat records as I tend to avoid the use of the garbage collector.

To pass a reference to a flat record so it can be modified by a function you need to mark the function argument as 'by reference' using & :

typedef point = @{x= int, y= int} fun print_point (p: point): void = printf("%d@%d

", @(p.x, p.y)) fun add1 (p: &point): void = { val () = p.x := p.x + 1 val () = p.y := p.y + 1 } implement main () = { var p1 = @{x=10, y=20} val () = add1 (p1) val () = print_point (p1) }

In this example I use typedef to create a type alias for the record so I can refer to the type as point . The add1 function takes a point by reference (as indicated by the & prefix). This works like C++ reference arguments. The function effectively takes a pointer to an instance of the struct and can modify the instance passed to the function. For this to work the point passed to the function must be an lvalue . That is, it must be mutable. This is done in ATS by making it a var vs a val .

Note that it's a type error to create an uninitialized point object and pass it to add1 . For example, the following gives a type check error:

implement main () = { var p1: point? val () = add1 (p1) val () = print_point (p1) }

Types that are unintialized have a ? suffix added to it. The type of p1 , due to it being unintialized, is point? . Since add1 takes a point this fails type checking. Initializing it allows it to pass:

implement main () = { var p1: point val () = p1.x := 5 val () = p1.y := 10 val () = add1 (p1) val () = print_point (p1) }

As well as passing by reference to functions you can pass a pointer and deal with the pointer management directly. This requires using the proof system and I hope to go through dealing with pointers and their associated proofs in a later post:

fun add1 {l:agz} (pf: !point @ l | p: ptr l): void = { val () = p->x := p->x + 1 val () = p->y := p->y + 1 } implement main () = { var p1 = @{x=10, y=20} val () = add1 (view@ p1 | &p1) val () = print_point (p1) }

When interfacing with C API's you often have to deal with C structures. In ATS you can declare a type as being defined as a struct in C with a matching ATS record definition so that ATS can be used to access the struct. The following example shows a struct declared in C, a function that uses it in C, and how this is wrapped in ATS:

%{^ typedef struct Point { int x; int y; } Point; void print_point (Point* p) { printf("%d@%d

", p->x, p->y); } %} typedef point = $extype_struct "Point" of {x= int, y= int} extern fun print_point (p: &point): void = "mac#print_point" implement main () = { var p1: point val () = p1.x := 10; val () = p1.y := 20; val () = print_point (p1) }

The $extype_struct keyword creates a type that is represented by a C struct with the given name. By using the of {x= int, y= int} suffix we define the record layout as seen via ATS. This will stop ATS from creating its own structure to map the type and instead uses the C structure. The generated C code for the main function looks like:

ATSlocal (Point, tmp1) ; __ats_lab_mainats: /* Point tmp1 ; */ ats_select_mac(tmp1, x) = 10 ; ats_select_mac(tmp1, y) = 20 ; print_point ((&tmp1)) ;