Incorporating Objective-Smalltalk

Ilja was looking for a scripting language to use with his E-Bay listings manager (GarageSale) and, naturally, I suggested Objective-Smalltalk. It’s a bit concerning, but hear me out.

Objective-Smalltalk has been powering my own applications for over ten years and the entire http://objective.st site without any issues. It even handled a Hacker News “Hug of Death” without breaking a sweat. Still, it’s a bit unconventional.

Implementation is fairly easy. You just include two frameworks in your application bundle, and the code to run the interpreter is minimal. I’ve done it often enough that it’s become second nature.

Naturally, I’m biased, but I think it’s the best choice. Another embeddable Smalltalk for macOS, FScript, has also been used successfully in several products. Ilja was kind enough to entertain my suggestion and asked how the code would look in practice:

It is not obvious to me how such a custom script in Objective-SmallTalk would look like. Here is a JavaScript pseudo-code example that ends & restarts certain listings. How would this look like in Obj-ST? pic.twitter.com/UjbwUD0Q4b

— Ilja A. Iwas (@iljawascoding) May 13, 2020

I’m happy to explain, but it’s a bit much for Twitter, hence this blog post.

Let’s start by replicating the logic closely, swapping the loop with a -select: and using Objective-Smalltalk syntax:


` ``` runningListings := context getAllRunningListings. listingsToRelist := runningListings select:{ :listing | listing daysRunning > 30 and: listing watchers < 3 . } ebay endListings:listingsToRelist ended:{ :ended | ebay relistListings:ended relisted: { :relisted | ui alert:“Relisted: {relisted}”. } }

``` `


Note the use of “and:” instead of “&&” and the cleaner syntax. While I’m not a fan of the pyramid of doom, the keyword message syntax makes it more bearable.

Swift even adopted open keyword syntax for multiple trailing closures, but that’s a topic for another time.

We can simplify further. “Context” is vague, and “getAllRunningListings” feels specific to one website’s structure.

Let’s use URLs with an “ebay:” scheme to represent EBay resources:


` ``` listingsToRelist := ebay:listings/running select:{ :listing | listing daysRunning > 30 and: listing watchers < 3 . } ebay endListings:listingsToRelist ended:{ :ended | ebay relistListings:ended relisted: { :relisted | ui alert:“Relisted {relisted} listings”. } }

``` `


The relisting callbacks seem unnecessary since we wait for each step. Let’s make it sequential:


` ``` listingsToRelist := ebay:listings/running select:{ :listing | listing daysRunning > 30 and: listing watchers < 3 . } ended := ebay endListings:listingsToRelist. relisted := ebay relistListings:ended. ui alert:“Relisted: {relisted}”.

``` `


(Objective-Smalltalk currently allows defining variables by assignment in scripts. This can be disabled.)

Shouldn’t listings handle their own actions? Let’s make “relist” and “end” methods:


` ``` listingsToRelist := ebay:listings/running select:{ :listing | listing daysRunning > 30 and: listing watchers < 3 . } ended := listingsToRelist collect end. relisted := ended collect relist. ui alert:“Relisted: {relisted}”.

``` `


If batch operations are common, a listings collection should understand them:


` ``` listingsToRelist := ebay:listings/running select:{ :listing | listing daysRunning > 30 and: listing watchers < 3 . } ended := listingsToRelist end. relisted := ended relist. ui alert:“Relisted: {relisted}”.

``` `


I’m assuming ending/relisting can fail, so we return the successful listings.

Let’s name the predicate and use a Higher Order Message for clarity. And since we have Unicode, use ‘←’ for assignment if you like:


` ``` extension EBayListing { -shouldRelist { self daysRunning > 30 and: self watchers < 3. } }

listingsToRelist ← ebay:listings/running select shouldRelist. ended ← listingsToRelist end. relisted ← ended relist. ui alert:“Relisted: {relisted}”. ``` `


This is practically executable pseudocode - a clear specification of the task.

We can condense it further by removing temporary variables (assuming the extension) and make it a one-liner:


` ``` relisted ← ebay:listings/running select shouldRelist end relist. ui alert:“Relisted: {relisted}”.

``` `


This one-liner isn’t about squeezing code, but removing unnecessary elements for clarity.

Pipe separators can further enhance readability, though they’re not strictly needed here due to precedence rules:


` ``` relisted ← ebay:listings/running select shouldRelist | end | relist. ui alert:“Relisted: {relisted}”.

``` `


Whether you prefer the one-liner or the step-by-step version is a matter of preference.

Licensed under CC BY-NC-SA 4.0
Last updated on Aug 22, 2023 05:11 +0100