Skip to content

Instantly share code, notes, and snippets.

@dharmatech
Created April 13, 2010 15:54
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save dharmatech/364754 to your computer and use it in GitHub Desktop.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment