In 2019, I started actively discussing Objective-Smalltalk as a significant part of its initial vision was realized.
My first presentation was at the European Smalltalk User Group’s (ESUG) conference, conference, held in Cologne (pdf). Returning to ESUG after my 2001 experience in Essen felt like stepping back in time. Despite Pharo being the focus of over half the presentations, the themes seemed to echo those of the past: Test-Driven Development, grappling with native threads (issues I encountered during my CocoaSqueak VM days), and 3D graphics that, while implemented in Smalltalk, weren’t any more advanced than those in other environments.
A key discussion point was enabling large, successful Smalltalk projects on mobile platforms like iPhones. Popular methods involved transpiling to JavaScript or translating VM code to JavaScript to run off-the-shelf images. Objective-Smalltalk, employing a combination of interpretation and native compilation, also falls into this category.
My next presentation took place at Frankfurt’s Macoun, Germany’s oldest Mac conference. While videos from the event are usually released later, here’s a reaction:
danke @mpweiher für den super vortrag zu https://t.co/bllQH4qANs gestern auf der #macoun — wer einen blick auf die zukunft erhaschen wollte, der hätte deinen vortrag sehen sollen. pic.twitter.com/Dw4M4QHLNl
— oɯʎxou (@noxymo) October 5, 2019
This translates to: “Anyone seeking a glimpse into the future should have attended @mpweiher’s talk.”
I’m flattered!
Additionally, two of my papers were accepted at SPLASH ‘19: Standard Object Out: Streaming Objects with Polymorphic Write Streams at the Dynamic Languages Symposium and Storage Combinators at Onward!.
An interesting aspect of these talks, which I didn’t emphasize, is that the presentations themselves were built using Objective-Smalltalk. The definitions were actually Objective-Smalltalk expressions, specifically complex object literals.
Below is a condensed version of the ESUG presentation:
` ``` controller := #ASCPresentationViewController{ #Name : ‘ESUG Demo’. #Slides : #(
#ASCChapterSlide { #text : ‘Objective-SmallTalk’. #subtitle : ‘Marcel Weiher (@mpweiher)’ } ,
#ASCBulletSlide{ #title : ‘Objective-SmallTalk’. #bullets : #( ‘Embeddable SmallTalk language (Mac, iOS, Linux, Windows)’, ‘Objective-C framework (peer/interop)’, ‘Generalizes Objects+Messages to Components+Connectors’, ‘Enable composition by solving Architectural Mismatch’, ) } , #ASCBulletSlide{ #title : ‘The Gentle Tyranny of Call/Return’. #bullets : #( ‘Feymnan: we name everything just a little wrong’, ‘Multiparadigm: Procedural, OO and FP!’, “Guy Steele: it’s no longer about completion”, “Oscar Nierstrasz: we were told we could just model the domain”, “Andrew Black: good OO students antropmorphise the objects”, ) } ,
#ProgramVsSystem { #lightIntensities : #( 0.2 , 0.7 )
} ,
#ASCSlideWithFigure{ #delayInSeconds : 5.0. #title : ‘Objects and Messages’. #bullets : #( ‘Objective-C compatible semantics’, ‘Interpreted and native-compiled’, ‘“C” using type annotations’, ‘Higher Order Messaging’, ‘Framework-oriented development’, ‘Full platform integration’, ) } ,
#ASCBulletSlide{ #title : ‘Pipes and Filters’. #bullets : #( ‘Polymorphic Write Streams (DLS ‘‘19)’, ‘#writeObject:anObject’, ‘Triple Dispatch + Message chaining’, ‘Asynchrony-agnostic’, ‘Streaming / de-materialized objects’, ‘Serialisation, PDF/PS (Squeak), Wunderlist, MS , To Do’, ‘Outlook: filters generalise methods?’, ) } ,
#ASCBulletSlide{
#title : 'In-Process REST'.
#bullets : #(
'What real large-scale networks use',
'Polymorphic Identifiers',
'Stores',
'Storage Combinators',
'Used in a number of applications',
)
} ,
#ASCBulletSlide{ #title : ‘Polymorphic Identifiers’. #bullets : #( ‘All identifiers are URIs’, “var:hello := ‘World!”, ‘file:{env:HOME}/Downloads/site := http://objective.st’, ‘slider setValueHolder: ref:var:celsius’, ) } ,
#ASCBulletSlide{ #title : ‘Storage Combinators’. #bullets : #( ‘Onward! ‘‘19’, ‘Combinator exposes + consumes REST interfaces’, ‘Uniform interface (REST) enables pluggability’, ‘Narrow, semantically tight interface enables intermediaries’, ‘10x productivity/code improvments’, ) } ,
#ImageSlide{ #text : ‘Simple Composed Store’. #imageURL : ‘/Users/marcel/Documents/Writing/Dissertation/Papers/StorageCombinators/disk-cache-json-aligned.png’. #xOffset : 2.0 . #imageScale : 0.8 } , #ASCBulletSlide{ #title : ‘Outlook’. #bullets : #( ‘Port Stores and Polymorphic Write Streams’, ‘Documentation / Sample Code’, ‘Improve native compiler’, ‘Tooling (Debugger)’, ‘You! (http://objective.st)’, ) } ,
#ASCChapterSlide { #text : ‘Q&A http://objective.st’. #subtitle : ‘Marcel Weiher (@mpweiher)’ } , ) }.
``` `
This code demonstrates several concepts:
- Complex object literals
- A 3D presentation framework
- Custom behavior through custom classes
- Framework-oriented programming
Let’s break down each concept.
Complex Object Literals
Similar to many modern languages, Objective-Smalltalk includes literals for arrays (or ordered collections) and dictionaries. Arrays use Smalltalk’s hash and round braces: #(). Unlike other Smalltalk dialects, entries are separated by commas (e.g., #( 1,2,3) instead of #( 1 2 3 )). I borrowed curly braces from Objective-C for dictionaries: #{}.
This allows us to represent complex property lists directly within the code. A frequent practice in Mac/iOS development is to initialize objects from property lists, like this:
` ``` presentation = [[MyPresentation alloc] initWithDictionary:aDictionary];
``` `
Complex object literals offer a concise way to achieve this by recognizing the space between the opening characters of array and dictionary literals as a placeholder for a name, specifically a class name:
` ``` presentation := #MyPresentation{ … };
``` `
This code snippet parses the text between the curly braces as a dictionary and then initializes a MyPresentation object using that dictionary. It utilizes the same -initWithDictionary: message shown earlier. Though seemingly minor, this convenience enables directly writing objects rather than writing code that constructs them—a subtle yet important difference.
This benefit becomes even more apparent when dealing with nested structures. Standard property lists lack class information, containing only arrays, dictionaries, numbers, and strings. The Objective-C example relies on external class information by passing the generic property list to a specific class instance.
(JSON shares a similar issue, which is why I prefer XML for object encoding.)
Therefore, either this knowledge needs to be supplied externally (e.g., by assuming uniform substructures) or custom, ad-hoc mechanisms must be created each time to embed this information within the dictionaries or arrays.
Complex object identifiers offer a standard solution: tagging each subdictionary or sub-array with the class of the object to be created, using a clear and distinct syntax.
A 3D Presentation Framework
One of the captivating aspects of Alan Kay’s Squeak demos was his ability to transcend the limitations of traditional slide-based presentations by incorporating live coding and interactive sketching directly onto slides. This effect, similar to characters breaking the “fourth wall,” was especially impactful on skeptical audiences who were initially dismissive.
However, these Squeak presentations often appeared somewhat simplistic and cartoonish, lacking a polished look.
Then came Apple’s SceneKit team with their presentations presentations, developed as Cocoa/SceneKit applications. This approach was remarkable because it allowed for extensive programmability and integration with custom code, much like Alan’s demos, but with a far more professional appearance.
The downside is that this application wasn’t reusable, required significant effort, and offered limited interactivity.
This led me to explore how to address these limitations.
My solution involved two steps: first, transforming the presentation application into a reusable framework (Slides3D), and second, controlling this framework interactively with Objective-Smalltalk from my Workspace-like “Smalltalk” application (presentation.txt). After some initial setup, including loading the framework (framework:Slides3D load.) and defining custom slide classes, the presentation is defined using the literal shown earlier and initiated by instructing the presentation controller to display itself in a window:
` ```
framework:Slides3D load.
class ProgramVsSystem : ASCSlide {
var code.
var system.
…
}.
class ImageSlide : ASCSlide {
var text.
var image.
#ASCChapterSlide { #text : ‘Q&A http://objective.st’. #subtitle : ‘Marcel Weiher (@mpweiher)’ } , ) }.
controller := #ASCPresentationViewController{ #Name : ‘ESUG Demo’. #Slides : #(
#ASCChapterSlide { #text : ‘Objective-SmallTalk’. #subtitle : ‘Marcel Weiher (@mpweiher)’ } ,
… ) }.
controller view openInWindow:‘Objective-SmallTalk (ESUG 2019)’.
``` `
The result? Polished, programmatically driven presentations that can be edited interactively and use a relatively straightforward format. This mechanism isn’t limited to presentations either; it can be employed to define other object hierarchies, including interactive GUIs.
Framework-Oriented Programming
This leads us to the driving force behind this approach: framework-oriented programming.
This concept deserves its own dedicated discussion, but its essence lies in shifting the majority of your code into frameworks instead of the application, even if your primary goal is building an application. Xcode exemplifies this approach. On my machine, the entire Xcode app bundle consumes nearly 10GB of storage. In contrast, the actual Xcode binary located in /Applications/Xcode.app/Contents/MacOS is a mere 41KB! Most of this size is attributed to bookkeeping and boilerplate code. Essentially, it contains a C main() function, likely resembling the one generated by Xcode itself.
Why is this approach beneficial?
The answer is straightforward: Apple frameworks (those with a .framework extension) are designed for composability, unlike .app bundles. You can combine frameworks into larger ones and reuse them across different applications, a feat that’s either challenging or impossible with apps (and no, hastily assembled AppleScript solutions don’t count).
Implementing this is surprisingly simple: create a framework target alongside your app target after creating an app project. Then, add this framework to your app and move all code and resources from the app target to the framework target, excluding the main() function. If you’re working with an existing app, transfer the code to the framework target, adjusting bundle loading code accordingly (the framework now replaces the app/main bundle). This is precisely how I derived Slides3D from Apple’s WWDC 2013 SceneKit App.
What I’ve described so far pertains to code organization. As you structure your code as an object-oriented framework, you’ll observe that it gradually transforms into a black-box framework composed of objects that are created, configured, and interconnected. This process can be tedious in the base language (consider creating views programmatically). Hence, the natural progression is to employ a DSL (like SwiftUI!). It’s important to note that a significant portion of this DSL revolves around creating, configuring, and connecting objects—in other words, utilizing complex object literals.