Notes on Writing Large Programs in Fluxus
When writing large programs in fluxus, I've found that there are some aspects of the language (the PLT scheme dialect to be more precise) which are essential in managing code and keeping things tidy. See the PLT documentation on structs and classes for more detail, but I'll give some fluxus related examples to work with.
For example, at first we can get along quite nicely with using lists alone to store data. Let's use as an example a program with a robot we wish to control:
(define myrobot (list (vector 0 0 0) (vector 1 0 0) (build-cube)))
We use a list to store the robot's position, velocity and a primitive associated with it. We could then use:
(list-ref myrobot 0) ; returns the position of the robot (list-ref myrobot 1) ; returns the velocity of the robot (list-ref myrobot 2) ; returns the root primitive of the robot
To get at the values of the robot and use them later. Seems pretty handy, but this has problems when we scale up the problem, say we want to make a world to keep our robots in:
; build a world with three robots (define world (list (list (vector 0 0 0) (vector 1 0 0) (build-cube)) (list (vector 1 0 0) (vector 1 0 0) (build-cube)) (list (vector 2 0 0) (vector 1 0 0) (build-cube)))
And then say we want to access the 2nd robot's root primitive:
(list-ref (list-ref world 1) 2)
It all starts to get a little confusing, as we are indexing by number.
Structs are simple containers that allow you to name data. This is a much saner way of dealing with containers of data than using lists alone.
Let's start again with the robots example:
(define-struct robot (pos vel root))
Where “pos” is the current position of the robot, “vel” it's velocity and “root” is the primitive for the robot. The (define-struct) automatically generates the following functions we can immediately use:
(define myrobot (make-robot (vector 0 0 0) (vector 0 0 0) (build-cube)) ; returns a new robot (robot-pos myrobot) ; returns the position of the robot (robot-vel myrobot) ; returns the velocity of the robot (robot-root myrobot) ; returns the root primitive for the robot
This makes for very readable code, as all the data has meaning, no numbers to decipher. It also means we can insert new data and not have to rewrite a lot of code, which is very important.
The world can now become:
(define-struct world (robots))
And be used like this:
; build a world with three robots (define myworld (make-world (list (make-robot (vector 0 0 0) (vector 1 0 0) (build-cube)) (make-robot (vector 1 0 0) (vector 1 0 0) (build-cube)) (make-robot (vector 2 0 0) (vector 1 0 0) (build-cube))))) ; get the 2nd robot's root primitive: (robot-root (list-ref (world-robots myworld) 1))
So far we have only used get's not set's. This is partly because setting state is seen as slightly distasteful in Scheme, so you have to use the following syntax to enable it in a struct:
(define-struct robot ((pos #:mutable) (vel #:mutable) root))
(define-struct) also generates the following functions:
(set-robot-pos! robot (vector 1 0 0)) (set-robot-vel! robot (vector 0 0.1 0))
So you can change the values in the program. For a full example, see the file dancing-robots.scm in the examples directory.