public

  • Download Gist
define-record-type-note-2010-04-13.md
Markdown

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?

Answer: Yes:

(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.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.