{"id":314,"date":"2023-02-04T00:43:31","date_gmt":"2023-02-04T08:43:31","guid":{"rendered":"http:\/\/eschatologist.net\/blog\/?p=314"},"modified":"2023-02-05T21:46:49","modified_gmt":"2023-02-06T05:46:49","slug":"lisa-source-code-understanding-clascal","status":"publish","type":"post","link":"https:\/\/eschatologist.net\/blog\/?p=314","title":{"rendered":"Lisa Source Code: Understanding Clascal"},"content":{"rendered":"<p>On January 19, Apple and the Computer History Museum released the <a href=\"https:\/\/info.computerhistory.org\/apple-lisa-code\">source code to the Lisa Office System 7\/7 version 3.1<\/a>, including both the complete Office System application suite and the Lisa operating system. (The main components not released were the Workshop environment and its tooling, including the Edit application and the Pascal, COBOL, BASIC, and C compilers and the assembler.) Curious people have started to dig into what\u2019s needed to understand and build it, and I thought I\u2019d share some of what I\u2019ve learned over the past few decades as a Lisa owner and enthusiast.<\/p>\n<p>While Lisa appears to have an underlying procedural API similar to that of the Macintosh Toolbox, the Office System applications were primarily written in the <em>Clascal<\/em> language\u2014an object-oriented dialect of Pascal designed by Apple with Niklaus Wirth\u2014using the <em>Lisa Application ToolKit<\/em> so they could share as much code as possible between all of them. This framework is the forerunner of most modern frameworks, including MacApp and the NeXT frameworks, which in turn were huge influences on the Java and .NET frameworks.<\/p>\n<p>One of the interesting things about Clascal is that it doesn\u2019t add much to the Pascal dialect Apple was using at the time: Pascal was originally designed by Wirth to be a teaching language and several constructs useful for systems programming were left out, but soon added back by people who saw Pascal as a nice, straightforward, compact language with simple semantics that\u2019s straightforward to compile. While in the 1990s there was a bitter war fought between the Pascal and C communities for microcomputer development, practically speaking the popular Pascal dialects and C are almost entirely isomorphic; there\u2019s almost nothing in C that\u2019s not similarly simple to express in Pascal, and <em>vice versa<\/em>.<\/p>\n<p>So beyond standard Pascal, Apple Pascal had a concept of \u201cunits\u201d for promoting code modularity: Instead of having to cram an entire program in one file, you could break it up into composable units that specify their \u201cinterface\u201d separately from their \u201cimplementation.\u201d Sound familiar?<\/p>\n<p>When creating a unit under this model, both the interface and the implementation can go in a single file, but in separate sections. So let\u2019s say you want to create a unit that makes some simple types available along with procedures and functions to operate on them. (In code examples, I\u2019m putting keywords in uppercase since Pascal was historically case-insensitive and it helps to make clear the distinction between language constructs and developer code.)<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\nUNIT Geometry;\n\nINTERFACE\n\n  TYPE\n    Point  = RECORD\n               h, v: INTEGER;\n             END;\n\n  VAR\n    ZeroPoint: Point;\n\n  PROCEDURE InitGeometry;\n  PROCEDURE SetPoint(var p: Point; h, v: INTEGER);\n  FUNCTION EqualPoints(a, b: Point): BOOLEAN;\n\nIMPLEMENTATION\n\n  PROCEDURE InitGeometry\n  BEGIN\n    SetPoint(ZeroPoint, 0, 0);\n  END;\n\n  PROCEDURE SetPoint\n  BEGIN\n    p.h = h;\n    p.v = v;\n  END;\n\n  FUNCTION EqualPoints\n  BEGIN\n    IF a.h = b.h AND a.v = b.v THEN BEGIN\n      EqualPoints := TRUE;\n    ELSE BEGIN\n      EqualPoints := FALSE;\n    END\n  END;\n\nEND.\n<\/pre>\n<p>Reading through this code, what\u2019s the first thing you notice? While <code>InitGeometry<\/code> would <em>typically<\/em> be written without parentheses, as is normal for a zero-argument procedure or function in Pascal, functions and procedures that do take arguments and return values are <em>also<\/em> written without parameter lists <em>but only in the <code>IMPLEMENTATION<\/code> section<\/em>.<\/p>\n<p>This is why, in a lot of the Lisa codebase, they would actually be written like this:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\n  FUNCTION EqualPoints{(a, b: Point): BOOLEAN}\n  BEGIN\n    IF a.h = b.h AND a.v = b.v THEN BEGIN\n      EqualPoints := TRUE;\n    ELSE BEGIN\n      EqualPoints := FALSE;\n    END\n  END;\n<\/pre>\n<p>This is because, despite being \u201cwordy,\u201d Pascal also typically tries to minimize repetition and risk of error. So since you\u2019ve already specified the <code>INTERFACE<\/code> why specify it again, and potentially get it wrong?<\/p>\n<p>What\u2019s interesting about Clascal is that it does the same thing! You define a class and its methods as an interface, and then its implementation doesn\u2019t require repetition. This may <em>sound<\/em> convenient but in the end it means you don\u2019t see the argument lists and return types at definition sites, so everyone wound up just <em>copying &amp; pasting them into comments next to the definition<\/em>!<\/p>\n<p>A couple of other things that are interesting about Clascal is that it sticks closer to Smalltalk terminology than most modern systems other than Objective-C (and, marginally, Swift): Instead of <code>this<\/code> it has <code>SELF<\/code> and instead of &#8220;member functions&#8221; it has &#8220;methods,&#8221; as PARC intended. This makes perfect sense as a bunch of the people who created and used Clascal came from PARC.<\/p>\n<p>So to define a class, you simply use <code>SUBCLASS OF SuperclassName<\/code> in a <code>TYPE<\/code> definition section, provide your instance variables as if they were part of a <code>RECORD<\/code>, and declare its methods using almost-normal <code>PROCEDURE<\/code> and <code>FUNCTION<\/code> declarations (not definitions!) that require an <code>OVERRIDE<\/code> keyword to indicate a subclass override of a superclass method.<\/p>\n<p>So the above code would look like this adapted to Clascal style:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\nUNIT Geometry;\n\nINTERFACE\n\n  TYPE\n    TPoint = SUBCLASS OF TObject\n               h, v: INTEGER;\n               FUNCTION CREATE(object: TObject, heap: THeap): TPoint;\n               PROCEDURE Set(h, v: INTEGER);\n               FUNCTION Equals(point: TPoint): BOOLEAN;\n             END;\n\nIMPLEMENTATION\n\n  METHODS OF TPoint;\n\n    FUNCTION TPoint.CREATE{(object: TObject, heap: THeap): TPoint};\n    BEGIN\n      { Create a new object in the heap of this class, if not\n        initializing an instance of a subclass. }\n      IF object = NIL THEN\n        object := NewObject(heap, THISCLASS);\n      SELF := TPoint(TObject.CREATE(object, heap));\n    END;\n    PROCEDURE TPoint.Set{(h, v: INTEGER)};\n      SELF.h := h;\n      SELF.v := v;\n    END;\n    FUNCTION TPoint.Equals{(point: TPoint): BOOLEAN};\n      Equals := a.h = b.h AND a.v = b.v;\n    END;\n  END;\n\nEND.\n\n<\/pre>\n<p>In addition to <code>SELF<\/code> there&#8217;s of course <code>SUPERSELF<\/code> to send messages to your superclass instead. And messages are sent via dot notation, e.g. <code>myPoint.Set(10,20);<\/code> to send <code>Set<\/code> to an instance of <code>TPoint<\/code>. It&#8217;s just about the most minimal possible object-oriented addition to Pascal, with one exception: It takes advantage of Lisa&#8217;s <em>heap<\/em>.<\/p>\n<p>Just like Macintosh, Lisa has a Memory Manager whose heap is largely organized in terms of <em>relocatable<\/em> blocks referenced by <em>handles<\/em> rather than <em>fixed<\/em> blocks referenced by <em>pointers<\/em>. Thus normally in Pascal one would write <code>SELF^^.h := h;<\/code> to dereference the <code>SELF<\/code> handle and pointer when accessing the object. However, since Clascal knows <code>SELF<\/code> and <code>myPoint<\/code> and so on are objects, it just assumes the dereference\u2014making it hard to get wrong. What I find interesting is that, unlike the Memory Manager on Macintosh, I&#8217;ve not seen any references to locking handles so they don&#8217;t move during operations. However, since there isn&#8217;t any saving and passing around of <em>partially<\/em> dereferenced handles most of the time, I suspect it isn&#8217;t actually necessary!<\/p>\n<p>Honestly, as late-1970s languages go, it isn\u2019t so bad at all. It wouldn\u2019t even be all that difficult for the editor to show this information inline anyway, it\u2019s the sort of thing that can be done fairly easily even in static language development environments from the 1970s.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>On January 19, Apple and the Computer History Museum released the source code to the Lisa Office System 7\/7 version 3.1, including both the complete Office System application suite and the Lisa operating system. (The main components not released were the Workshop environment and its tooling, including the Edit application and the Pascal, COBOL, BASIC,&hellip;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[3],"tags":[155,4,123,69,156],"class_list":["post-314","post","type-post","status-publish","format-standard","hentry","category-technology","tag-lisa","tag-mac","tag-oop","tag-pascal","tag-retrocomputing"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p74loH-54","_links":{"self":[{"href":"https:\/\/eschatologist.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/314","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/eschatologist.net\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/eschatologist.net\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/eschatologist.net\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/eschatologist.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=314"}],"version-history":[{"count":9,"href":"https:\/\/eschatologist.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/314\/revisions"}],"predecessor-version":[{"id":332,"href":"https:\/\/eschatologist.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/314\/revisions\/332"}],"wp:attachment":[{"href":"https:\/\/eschatologist.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=314"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/eschatologist.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=314"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/eschatologist.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=314"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}