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.