Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!watmath!clyde!burl!ulysses!allegra!princeton!caip!topaz!uwvax!uwmacc!dubois From: dubois@uwmacc.UUCP (Paul DuBois) Newsgroups: net.micro.mac Subject: Review of the Rascal Development System Message-ID: <2234@uwmacc.UUCP> Date: Tue, 27-May-86 14:15:27 EDT Article-I.D.: uwmacc.2234 Posted: Tue May 27 14:15:27 1986 Date-Received: Thu, 29-May-86 02:46:51 EDT Distribution: net Organization: UW-Madison Primate Center Lines: 387 [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