Hello,

Let's define a simple point type:

(define-record-type pt
  (fields (mutable x)
          (mutable y)))

In C++, we'd be able to define a method on the class and reference the
instance variables without qualification. Let's do that in Scheme:

(define (pt::norm p)

  (import-pt p)

  (sqrt (+ (sq x)
           (sq y))))

You can see I'm referencing 'x' and 'y' without special accessors or
qualifiers. This magic is due to the 'import-pt' macro. We'll see that
later.

Also in C++, you'd be able to declare a variable to be of type pt and
have convenient access to it's components and methods. Let's do that
too:

(define (pt/n p n)

  (is-pt p)

  (make-pt (/ p.x n)
           (/ p.y n)))

Let's try it out:

> (pt/n (make-pt 3 4) 5)
#[pt 3/5 4/5]

In the definition of 'pt/n' I'm accessing the 'x' component via 'p.x'
and the 'y' component via 'p.y'. The magic in this case is due to
'is-pt'; we'll see it later. By the way, the 'x' component can be set
like so:

    (p.x! 4)

In a method on 'pt' setting 'x' would simply be:

    (x! 4)

Earlier I said that 'pt::norm' is a method on the 'pt' record type. In 
C++ we'd be able to reference that method with no qualification from the
definition of another method on 'pt'. Let's do that:

(define (pt::normalize p)

  (import-pt p)

  (pt/n p (norm)))

So 'normalize' is a method on 'pt' and the body is referencing the
'norm' method without qualification.

Here's the 'import-pt' macro:

  (define-syntax import-pt

    (lambda (stx)

      (syntax-case stx ()

        ((import-pt p)

         (with-syntax ( (x (gen-id #'p "x"))
                        (y (gen-id #'p "y"))

                        (x! (gen-id #'p "x!"))
                        (y! (gen-id #'p "y!"))

                        (neg       (gen-id #'p "neg"))
                        (norm      (gen-id #'p "norm"))
                        (normalize (gen-id #'p "normalize")) )

           #'(begin

               (define-syntax x
                 (identifier-syntax
                  (pt-x p)))

               (define-syntax y
                 (identifier-syntax
                  (pt-y p)))

               (define-syntax x!
                 (syntax-rules ()
                   ((x! val)
                    (pt-x-set! p val))))

               (define-syntax y!
                 (syntax-rules ()
                   ((y! val)
                    (pt-y-set! p val))))

               (define-syntax neg
                 (syntax-rules ()
                   ((neg)
                    (pt::neg p))))

               (define-syntax norm
                 (syntax-rules ()
                   ((norm)
                    (pt::norm p))))

               (define-syntax normalize
                 (syntax-rules ()
                   ((normalize)
                    (pt::normalize p))))))))))

And the 'is-pt' macro:

  (define-syntax is-pt

    (lambda (stx)

      (syntax-case stx ()

        ((is-pt p)

         (with-syntax ( (p.x (gen-id #'p #'p ".x"))
                        (p.y (gen-id #'p #'p ".y"))

                        (p.x! (gen-id #'p #'p ".x!"))
                        (p.y! (gen-id #'p #'p ".y!"))

                        (p.neg       (gen-id #'p #'p ".neg"))
                        (p.norm      (gen-id #'p #'p ".norm"))
                        (p.normalize (gen-id #'p #'p ".normalize")) )

           #'(begin

               (define-syntax p.x
                 (identifier-syntax
                  (pt-x p)))

               (define-syntax p.y
                 (identifier-syntax
                  (pt-y p)))

               (define-syntax p.x!
                 (syntax-rules ()
                   ((p.x! val)
                    (pt-x-set! p val))))

               (define-syntax p.y!
                 (syntax-rules ()
                   ((p.y! val)
                    (pt-y-set! p val))))

               (define-syntax p.neg
                 (syntax-rules ()
                   ((p.neg)
                    (pt::neg p))))

               (define-syntax p.norm
                 (syntax-rules ()
                   ((p.norm)
                    (pt::norm p))))

               (define-syntax p.normalize
                 (syntax-rules ()
                   ((p.normalize)
                    (pt::normalize p))))))))))

A library '(pt-class)' containing the examples above is at:

    http://gist.github.com/243752

Of course, having to manually define macros like 'import-pt' and 'is-pt'
for each record type you'd like to use in this manner is not so
convenient. Having them auto-generated from a record definition and it's
corresponding methods would be very cool.

Ed

Reply via email to