[net.micro.mac] Review of the Rascal Development System

dubois@uwmacc.UUCP (Paul DuBois) (05/27/86)

[Note:  this review appeared in a recent number of Mad Mac Review (a
locally-produced (Madison, WI) Macintosh journal) in slightly edited
form.  The version below is closer to the original version:  the
editing that was done to prepare the document for publication resulted
in statements containing factual mistakes!  Also, if enough people prod
me, I will post a comparison of Rascal and LightspeedC.  I have
recently gained some (limited) experience with the latter, which has
given me some valuable perspective.]

Review of the Rascal Development System
Paul DuBois

Introduction

The Rascal development system is a programming environment supporting a
Pascal-like language.  Both the environment and the language were
designed specifically for the Macintosh.  Some of the programs written
with it that you might know of are Billiard Parlour, IconMaker, and the
Grep-Wc and ZoomIdle DA's.  Let me say right off that I'm very
enthusiastic about Rascal - this isn't an unbiased review, by any
means.

Rascal was written at Reed College in Oregon, mainly to allow
development of fast, real-time laboratory software, but it stands on
its own as a general purpose language as well.  The people who wrote it
actually use it for their own work, hence are quite interested (from
other than a commercial perspective) in its being a usable product.
They actively solicit suggestions, and support is excellent, over both
phone and Usenet.

Rascal comprises an editor, compiler, linker, and execution facility,
as well as several utilities that can be run within the Rascal
environment.  This means you can write your source code, compile, link
and test it, turn it into a standalone application, build its icon and
enable the icon in the desktop - all without leaving Rascal.

For $129, you get four disks:  (i) system, (ii) libraries and
utilities, (iii) example programs, (iv) library source.  You also get
documentation that is clearly not an afterthought.  It is well-written
and visually attractive (LaserWriter-produced).  Included are a helpful
primer, a system manual, a language manual, and a binder containing
library source (so you don't have to print out the 53 (!!) libraries).
Both manuals are indexed, as is the library binder.

Rascal supports the ToolBox, the Operating System (file I/O, sound,
speech, serial drivers, printing, AppleTalk), and SANE floating point.
The MacInTalk and AppleTalk drivers are not included.  The RAM serial
driver (SERD resource) is buried in a utility file but can be clipped
out with ResEdit.  There is no debugger, assembler or resource
creator.  A promotional blurb I have seen implies that multi-tasking is
currently implemented.  This is not (yet) true; a premonitory
indication of its impending arrival is that you can edit your source
while your program is running.

Rascal is copyrighted but not copy-protected, thus can be used with ram
disks or hard disks.  The whole system is 146K, small enough to be put
in a ram disk with a system file and a few libraries, with some space
left over (unlike TML Pascal, for instance).  I run Rascal in a ram
disk on a 512K Mac, with my current application in one drive and more
libraries and some utilities in the other.  Rascal is supposed to run
marginally on a 128K Macintosh, and intelligently under HFS.  The
latter claim is corroborated by recent discussion on Usenet.

Language Specifics

Rascal is most similar to Pascal, but has some C-like features as
well.  It is a block-structured procedural language. Nested procedure
blocks are not allowed.

Identifiers may be up to 255 characters long (case-insensitive).  This
is of course ridiculously long, but less ridiculous than having the
compiler teutonically enforce an 8- or 10-character limit.  Defined
constants may be hex, decimal, or character constants.  Numeric
constants may be explicitly byte, word or long word sized.  Defined
string constants are not allowed, but may be placed freely in
expressions.  C-type escapes ('\n', '\t', etc.) are allowed in strings
and character constants.

Variable types may be scalars, arrays, unions (like C unions), records,
or combinations thereof.  Variant records are supported.  Enumerations
and sets are not, but are easily simulated (the language manual says
how).

Register local variables are allowed with some restrictions (e.g., must
be scalars, can't use address operator).  Static local variables are
allowed within routines, but are nearly worthless - there's no
initializer syntax!

Statement types include assignment, if-then-else, loop, case (default
case specifier is supported), and block statement.  Rascal propagates
the Pascal atrocity of considering the semicolon a statement separator
rather than a terminator, but more than makes up for it by providing a
return statement.

Typecasting is allowed, even on the left hand side of assignments.
Conditional expressions are evaluated left-to-right, but there is no
short-circuiting, so nested if's are still necessary to a greater
extent than in equivalent C programs.  Assignment of non-scalars (e.g.,
records) is allowed. In fact, assignment between any two objects of
equal size is allowed.  C assignment operators +=, -=, <<=, etc., are
supported.

The loop construct (there's only one) is ugly and too complex.  The
manual says it is sufficient to model other loop types (for, repeat,
while).  That's true enough, but it's still wretched.  I have seen the
loop structure described in promotional literature as "novel." This
strikes me as the sort of linguistic subterfuge by which ghastly
concoctions are served in school lunches under names such as "Richard's
Surprise."

Timer operations are provided, at either 1/60th of a second or
millisecond resolution (I don't know how they manage the latter).  This
capability fits Rascal superbly for laboratory software performing
tasks such as timed automatic data collection.  (Hence the name:
Rascal = Real-time Pascal-like language.)

Floating point operations are handled entirely by procedure calls.
Yuck.  On the other hand, its FP runs faster than a number of the C
compilers on the Byte benchmarks.

You can write interrupt procedures and filter functions.  Procedures
and functions may be defined in any order.  Forward declarations must
be provided for functions that are used before being defined, so that
the compiler may ascertain the result type.  You don't get type
checking on procedure calls that precede the definitions.  Type
checking is rudimentary in any case, involving number and size of
arguments only.  If you define a procedure

	Proc MyProc (arg: LongInt); ...

you can pass it any four-byte argument with impunity; the compiler
won't complain. Even for number or size mismatches, the compiler
generates warnings only. (It may be configured to abort on warnings.)

The INLINE statement allows word-size values to be inserted directly
into your code.  (The ToolBox trap routines are implemented this way.)
Push and Pop procedures are available for moving values to and from the
stack.  These constructs can be used to write routines that call their
own filter functions by setting up the stack directly.  (This is how
installation of wordbreak and autoscroll functions for TextEdit records
is implemented by the library routines SetWordBreak and SetClikLoop.)

For stack-based routines, Rascal calling conventions differ somewhat
from those of Lisa Pascal.  The latter passes non-scalar variables by
address unless the type is four bytes or less, in which case it puts
the contents of the variable on the stack.  In this, the designers of
the Macintosh opted for space-efficiency over consistency.  I think
they made the wrong choice, but it's a moot point now.  The designers
of Rascal opted for consistency over efficiency:  non-scalar variables
are always passed by address.  Thus records, arrays, unions and floats
(implemented as Byte[10] variables) are always Var parameters.

Since Rascal differs from Lisa Pascal in the case of four-byte
non-scalar arguments, you have to be careful to distinguish between the
use of ToolBox routines and Rascal routines taking Point arguments.
(The only non-scalar four-byte Inside Macintosh type is the Point.)  In
Lisa Pascal, to pass a point to a ToolBox routine "TBRoutine," you say

	TBRoutine (thePoint);

All the Inside Macintosh examples use point arguments this way,
assuming that the contents of the record go on the stack.  In Rascal,
the address goes on the stack, so you must say

	RasRoutine (thePoint.vh);

You get used to this, but the difference will get you if you're not
aware of it (the issue is addressed in the manual).  This complaint
aside, it's still true that treatment of the stack in Rascal is more
similar to Lisa Pascal than in many (all?) of the C compilers.  In
addition, the RegCall procedure makes calling register-based ROM
routines simplicity itself.

Development System

The package is fully integrated.  Everything described below can be run
within the development system - the "Transfer" concept so ubiquitous
elsewhere is entirely superfluous to Rascal.

The editor is a fairly standard, sizable, vertically scrollable window
supporting the usual Cut/Copy/Paste operations, with the usual TERec
32K size limit.  Undo doesn't work (it's only in the menu for DA's).
Find/replace is supported (not, alas, with grep-style patterns).
Programmer-oriented editing functions include auto-indent (can be
toggled on/off), back tabbing, tab-width setting, aligning blocks of
lines along left margin, shifting blocks of lines left or right by one
column (very useful, but would be more so if shifts were by tab-widths
rather than single columns).  All in all, a nearly vanilla editor, but
virtually bug-free, with one exception:  if you try to open a file
that's bigger then 32K, it hangs.  The size isn't checked before
attempting the read.  (There is actually some justification for this,
which I won't go into.)

The compiler generates optimized native 68000 code.  I am not sure how
well it optimizes (the manual says 15% - 40%), but in general my
programs shrunk significantly when I compiled them with the current
release rather than the pre-release.  Optimization can be turned off.
The compiler has no "syntax-check only" mode.

Segmentation is not yet supported, so there is a 32K code + 32K global
data limit.  Rascal might therefore be considered unsuitable for large
projects.  In practice, I don't consider this a serious matter, since
nothing I've written has even come close to the limit yet (yes, I have
written non-trivial programs).  The developers do have a segmentation
scheme that will be beta-tested shortly.

During a compile, lots of information is displayed, such as the name of
the current routine, how much code is being generated, etc.  When the
compiler finds an error, you can either continue (to find other
errors), abort, or go back to the editor.  The editor is integrated
with the compiler such that the caret is placed at the point of error.
If you were not editing the file being compiled, the editor opens it
and reads it in first.  (Notice that this implies not only that Rascal
has a notion of "the current file," but also that you can override it.
This is true of linking and executing as well.)

Separate compilation is supported.  (The Rascal libraries were created
this way, in fact.)  There is no syntactic difference in structure
between a so-called "main program" and any other type of program module
- a big plus, to my mind.  Compiled modules may be combined into larger
modules, facilitating incremental library development.

Transmission of information between modules is accomplished either by
pulling the information out of other modules via a Uses statement
(similiar to that of Lisa Pascal), or by external declarations.  The
latter method works only for variables, procedures and functions, the
former with constants and types as well.  The compiler assumes in
either case (with one exception) that you will supply code for the
missing routines at link time.  The exception is that the compiler
immediately resolves references to ToolBox trap word routines in Uses
libraries, so they need not be linked in later.

In special circumstances, use of trap libraries can trigger a compiler
bug (which Reed is aware of, and working on).  There are two levels of
Uses.  Partial-uses pulls in function and procedure header information
only.  Full-uses brings in constant, type and variable information as
well.  When a full-uses module is created which itself does a Uses of a
trap library, the trap information in one of the tables generated by
the compiler may be incorrect.  Using the full-uses module in another
module causes at least two problems that I know about:  (i) the
compiler "forgets" about some of the trap routines; (ii) the compiler
doesn't forget but compiles in bad code.  This is much rarer, as far as
I can tell.  In any case, there is a simple workaround.  The only
reason you really need full-uses modules is to provide access to global
constants, types and variables.  If you don't put functions or
procedures in such a module, it doesn't need any trap libraries, so the
problem is solved.

The linker is smart (tosses dead code), but can be configured dumb (to
combine libraries, for example).  A Link directive placed in your
program causes the linker to automatically look through the set of
named modules to resolve references.  If some of the modules are not
found (e.g., they're on an ejected disk) or references remain
unresolved, the linker doesn't give up, but instead allows selection
(via standard GetFile dialog) of more modules to search.

The execution facility allows you to run your programs within Rascal.
A supervisor acts as a wrapper around your program, insulating you from
details you don't want to think about.  This makes Rascal ideal as an
introduction to Macintosh programming (at least for those who already
know a language), since you can write a running program with just a few
lines of code.  (There are many examples of such in the primer, which
isn't all fluff like so many Macintosh manuals.)  This is extremely
convenient for debugging, and encourages writing small program
fragments to test ideas.  The supervisor enables the interrupt switch,
allowing recovery from a surprising number of crashes and infinite
loops.  If you're not running in a ram disk, crashes don't destroy
unsaved work, since the edit file is automatically backed up in a
rescue file.

The supervisor handles many of the setup details (such as the Five Big
Inits), provides a default execution window (which is dragged, sized,
activated and deactivated automatically), and hides many of the details
of event handling.  For example, to process mouse clicks in the default
window, you declare a procedure _MOUSE and the supervisor automatically
routes clicks there.  Similarly, to handle keystrokes, you declare a
procedure _KEY.  If you want to ignore the mouse or the keyboard, you
leave out the appropriate procedures.  Very simple.

_MOUSE and _KEY are two members of the set of standard entry
procedures.  The others are _INIT, _HALT, _UPDATE, _MENU, _MAIN, and
_EVENT.  Setup and termination code goes into _INIT and _HALT.  _UPDATE
gets update events in the default execution window (so you know when to
redraw).  _MENU receives selections from the menu bar.  All entry
procedures are optional, even _MAIN.

The supervisor begins execution by calling _INIT, then goes into a
loop.  On each iteration it processes pending events by calling all
event-handling procedures which are defined and for which there are
appropriate events, and then calls _MAIN.  When the program requests a
halt, the supervisor calls _HALT and terminates.  This is a very well
defined and easy-to-use framework within which to develop an
application.

If the standard event-handling procedures are insufficient for your
task, you use the _EVENT entry procedure to override them.  An event
mask statement indicates the event types you want _EVENT to receive.
(Conceptually similar to signal() in C.)  You can handle such an event
if it looks interesting, or tell the supervisor to handle it as usual.
_EVENT provides a convenient way to incrementally take over the details
of event processing, handling only those circumstances for which the
normal mechanisms are insufficient.  For instance, _MOUSE cannot
recognize shift-clicks (e.g., to do text editing), since the supervisor
passes it only the mouse position, not the state of the shift key.  To
recognize shift-clicks, you could catch mouse events with _EVENT, test
the shift key, and call your own procedure

	MyMouse (thePoint, theShiftKey);

I often use the standard procedures to construct a prototype
application, then move the detailed stuff into _EVENT as development
progresses.  This framework is an invaluable aid to maintenance of
conceptual clarity.

Utilities

When your program is debugged and ready to go, you use a utility
(MakeAppl) that is itself run from within Rascal to create a standalone
application.  Your program is poured into an application wrapper that
is essentially the same as the execution supervisor.  If you are doing
all the event handling yourself, you can use a smaller wrapper to avoid
the disk space overhead associated with the default wrapper.  Another
utility (IconMaker) allows you to design your own application icons.
It enables them in the desktop as well, a task often avoided.  To make
a desk accessory, you use the DeskMaker utility.

Other utilities include a HFS search-path editor and a MDS-to-Rascal
library converter.

Two important points should be emphasized here.  First, utility
programs such as MakeAppl, IconMaker and DeskMaker are not actually
part of Rascal itself, but since they perform development tasks, and
may be executed from within the Rascal environment, they in effect
implement extensibility of the development system.  Second, since the
provided utilities are no different than programs you write yourself
(in fact, they are Rascal programs), you can tune your environment by
writing your own utilities, or by changing the existing ones (source is
provided for all of them).  For example, while DeskMaker is a wonderful
program, one shortcoming is that it has no facility for recognizing
owned resources and transferring them from your object into the DA
file.  You can do this with ResEdit, but it's a simple matter to write
a program that copies resources from one file to another.  This solves
the problem for the most part, and most of the necessary code is
already in MakeAppl, just waiting to be cannabilized.

I think that this concept of extensibility could have been taken even
further, e.g., by putting printing and system configuration into
separate utilities.

Example programs

A vast array of example programs is provided, all with source.  The
value of these cannot be overestimated.  Even if you know nothing about
programming the Macintosh, a great deal can be learned by studying the
examples.  For instance, you could find out how to use such things as
the sound and speech drivers.  A caveat:  one of the speech example
programs (speak.src) contains an unpleasant, and to my mind
significant, blemish, in that when one selects the "Dirty" menu option,
it speaks a semi-pornographic phrase.  The academic environment within
which Rascal was created apparently caused the writers to lose
perspective of the fact that some of their customers may use their
product in a family setting.  In such cases, the buyer may find it
necessary to hack speak.src into a more satisfactory condition.

Other examples include editor prototypes which serve as models for
learning virtually anything you want to know about TextEdit, a fast
fourier transform, a terminal emulator (SquirmTerm) of sufficient
functionality that I have felt no need to buy a "real" one (yes, it
works with vi), a file printer (works with LaserWriter), demonstrations
of 3-D and offscreen graphics, a pseudo-Unix DA.  There is a host of
others.

Rascal may be ordered from:

	Metaresearch, Inc.
	1100 SE Woodward St.
	Portland, OR  97202

	(503) 232-1712
-- 
Paul DuBois     UUCP: {allegra,ihnp4,seismo}!uwvax!uwmacc!dubois    |
                ARPA: dubois@easter                               --+--
                                                                    |
Doth the hawk fly by thy wisdom, and stretch her wings              |
toward the south?                        Job 39:26

clarke@utcsri.UUCP (Jim Clarke) (05/29/86)

(Sorry if this breaks some newsreaders; the original was so long I saved
it and went directly into vi to prepare this reply.)

In a message whose number I have dropped, dubois@uwmacc (Paul DuBois) writes:
>                                             ....  The people who wrote it
>   actually use it for their own work, hence are quite interested (from
>   other than a commercial perspective) in its being a usable product.
>   They actively solicit suggestions, and support is excellent, over both
>   phone and Usenet.

He's right.  I got an "instant" (turnaround roughly same as mail time there and
back) response to a very stupid (answer in the manual) question recently.

I pretty much agree with the review.  Rascal is nice; it mostly doesn't have
the worst features of either Pascal or C, and I haven't had any language-
related problems learning to use it, even though I've never used Pascal -- the
source of most of Rascal -- and I don't get along too well with C -- the source
of the rest.

The only exception to Rascal's niceness, really, is that loop construct (as the
review said).  If you like C's three-entry "for" loop, you'll love Rascal's.
It has *four* items in the list following the keyword "for"!  Luckily it also
has C's "break" statement, so you can say

	for(,,,) {
	  ...
	  if blah then break;
	  ... };

just like in Modula-2 or (plug) Turing.  There aren't any radically new
language features other than "for", so you shouldn't have any trouble learning
the language itself.  And the system is fun and easy to use.
-- 

Jim Clarke -- Dept. of Computer Science, Univ. of Toronto, Canada M5S 1A4
              (416) 978-4058
{allegra,cornell,decvax,ihnp4,linus,utzoo}!utcsri!clarke