Instantly share code, notes, and snippets.

# dharmatech/define-record-type-note-2010-04-13.md Created Apr 13, 2010

What would you like to do?

Here's a struct in C:

``````struct Point
{
int x;
int y;
} ;
``````

Given a variable `p0` which is typed as `struct Point`:

``````struct Point p0;
``````

C offers concise syntax for accessing the components of the struct:

``````p0.x
p0.y
``````

R6RS Scheme has records. Let's define a comparable record type:

``````(define-record-type point
(fields x y))
``````

Given a variable `p0` which is bound to a `point`:

``````(define p0 (make-point 1 2))
``````

Scheme's approach to field access is more verbose than C's:

``````(point-x p0)
(point-y p0)
``````

Let's go back to C. Here's a struct which has components of type `struct Point`:

``````struct Spaceship
{
struct Point pos;
struct Point vel;
} ;
``````

Given a variable `ship` which is typed as `struct Spaceship`, C offers concise syntax for accessing the sub-components of the struct:

``````ship.pos.x
ship.pos.y
ship.vel.x
ship.vel.y
``````

The spaceship record type in Scheme:

``````(define-record-type spaceship
(fields pos vel))
``````

Given a variable `ship`, accessing the sub-components looks like:

``````(point-x (spaceship-pos ship))
(point-y (spaceship-pos ship))
(point-x (spaceship-vel ship))
(point-y (spaceship-vel ship))
``````

I was porting a C++ library to Scheme and this sort of verbosity was getting outta hand. I wondered, is Scheme powerful enough to overcome this challenge? After some experimentation, hackage, some help from a macro jedi, and a bunch of syntax-case macros, Scheme pulled through, enough for my needs.

The result of the hacking was an extended form of `define-record-type`. Let's do our `point` again:

``````(define-record-type++ point
(fields x y))
``````

Define a variable `p0` to a point value:

``````(define p0 (make-point 1 2))
``````

Now the magic comes. Much like in C, we declare the variable to be of "type" `point`:

``````(is-point p0)
``````

And now we have concise accessors:

``````p0.x
p0.y
``````

FAQ: Are those variables? If so, what happens when you change the value of a field? Are the variables somehow updated?

Answer: No, those are not variables. They are identifier syntax macros (see this for more details). This is syntax:

``````p0.x
``````

and it expands at compile time to this expression:

``````(point-x p0)
``````

FAQ: Supposing that a field is mutable, is there nice syntax for updating the field?

``````(p0.x! 10)
``````

That syntax expands into:

``````(point-x-set! p0 10)
``````

Any more questions? OK, let's continue.

What about getting at sub-components of the spaceship? We can declare the fields to be of a certain type:

``````(define-record-type++ spaceship
(fields (pos is-point)
(vel is-point)))
``````

Now if we have a variable holding a spaceship:

``````(define ship
(make-spaceship (make-point 1 2)
(make-point 3 4)))
``````

and it's type is properly declared:

``````(is-spaceship ship)
``````

we get nice accessors:

``````ship.pos.x
ship.pos.y
ship.vel.x
ship.vel.y
``````

Not bad for a 35 year old language eh? :-)

You can browse the implementation. The tests pass in Ikarus, Ypsilon, Chez, Larceny, and Mosh.