Headless Pharo, GNU Smalltalk & Deployment
41.1 Command-line Pharo
A script (script.st):
Stdio stdout, Stdio stderr, and Stdio stdin give access to the standard streams; inside a command-line handler, self arguments answers the command-line arguments.
./pharo Pharo.image --help # list command-line handlers
./pharo Pharo.image eval "(1 to: 10) inject: 0 into: [:a :b | a + b]" # → 55
./pharo Pharo.image eval --save "Metacello new baseline: 'Bank'; repository: 'github://me/bank:main/src'; load"
./pharo Pharo.image st --quit script.st # run a script file and quit
./pharo Pharo.image test "Bank-.*" # run tests for matching packages
./pharo Pharo.image save BankDev # save a copy as BankDev.image
./pharo-ui Pharo.image # open with the UI| total |
total := (1 to: 100) inject: 0 into: [ :a :b | a + b ].
Stdio stdout
nextPutAll: 'Sum: ' , total printString;
lf;
flush.
Smalltalk snapshot: false andQuit: true.41.2 Custom command-line handlers
Recent Pharo also includes CLAP for declarative command-line parsing.
CommandLineHandler << #GreetCommandLineHandler slots: {}; package: 'Bank-CLI'.
GreetCommandLineHandler class >> commandName
^ 'greet' "./pharo Pharo.image greet Ada → Hello, Ada"
GreetCommandLineHandler >> activate
Stdio stdout nextPutAll: 'Hello, ' , (self arguments ifEmpty: [ #('world') ]) first; lf; flush.
self exitSuccess41.3 Deploying a server image
Start from a clean, versioned image (zeroconf or CI).
Load your baseline; run tests.
Register startup code so the server starts when the image launches:
Save the image and ship it with the VM (or build a container).
Run headless under a supervisor (systemd, Docker, Kubernetes); log to stdout.
Dockerfile sketch:
Other dialects: VisualWorks *Runtime Packager*, VA Smalltalk *Packager* (stripped images), Dolphin Smalltalk produces Windows executables, GemStone deploys code into the shared repository.
BankApp class >> startUp: resuming
resuming ifTrue: [ self start ]
SessionManager default registerUserClassNamed: #BankApp.FROM ubuntu:24.04
RUN apt-get update && apt-get install -y curl unzip ca-certificates && rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN curl -L https://get.pharo.org/64/130+vm | bash
COPY src /app/src
RUN ./pharo Pharo.image eval --save "Metacello new baseline: 'Bank'; repository: 'tonel:///app/src'; load"
EXPOSE 8080
CMD ["./pharo", "Pharo.image", "eval", "--no-quit", "BankApp start"]41.4 GNU Smalltalk: file-based scripting
GNU Smalltalk (gst) runs plain text files, with a bracketed class syntax:
Reading input: stdin linesDo: [ :line | line displayNl ]. GNU Smalltalk also includes packages for sockets, XML, databases, and a Seaside port (older), but development has been largely inactive since 3.2.5.
#!/usr/bin/gst -f
"animals.st — run with: gst animals.st"
Object subclass: Animal [
| name |
<comment: 'An animal with a name'>
Animal class >> named: aString [
<category: 'instance creation'>
^ self new setName: aString
]
setName: aString [ name := aString ]
name [ ^ name ]
sound [ ^ self subclassResponsibility ]
speak [ ^ name , ' says ' , self sound ]
]
Animal subclass: Dog [
sound [ ^ 'Woof' ]
]
| animals |
animals := OrderedCollection new.
animals add: (Dog named: 'Rex'); add: (Dog named: 'Fido').
animals do: [ :a | a speak displayNl ].
(3/4) printNl. "printNl: printString + newline"
'Hello from GNU Smalltalk' displayNl. "displayNl: displayString + newline"
Smalltalk arguments printNl. "command-line arguments (gst -f file.st a b c)"gst animals.st
gst -f animals.st arg1 arg2
gst # interactive REPL41.5 Chunk format (file-out)
The traditional interchange format, readable by Squeak, Pharo (FileStream fileIn:/drag-and-drop), VisualWorks, and GNU Smalltalk:
Chunks are separated by !; !Class methodsFor: 'protocol'! starts a series of method chunks terminated by ! !.
Object subclass: #Greeter
instanceVariableNames: 'name'
classVariableNames: ''
poolDictionaries: ''
category: 'Demo'!
!Greeter methodsFor: 'greeting'!
greet
^ 'Hello, ' , name! !41.6 Dialect differences cheat sheet
VisualWorks / VA Object << #Foo slots: {...}; package: 'P'
Object subclass: #Foo instanceVariableNames: '...'... category: 'P' Object subclass: Foo [ | a |... ]
Smalltalk defineClass: #Foo superclass:... (VW) / subclass:... (VA) Git + Tonel (Iceberg)
Monticello .mcz (Squeak), Git .pck.st (Cuis) Store / ENVY (VA), parcels new calls initialize no — call it yourself Morphic + Spec2 Morphic (and MVC) Blox (Tk/GTK, old)
VisualWorks UI painter / VA Composition Editor yes (VW namespaces)
🧪 Part III Exercises
Write a method that, given a class, generates accessors for all its slots at runtime with compile:classified:; then remove them again.
Use thisContext to implement Object>>callerName, answering the selector of the method that called the current method.
Instrument OrderedCollection>>add: temporarily with a MetaLink that counts calls while opening a Playground; uninstall it afterwards.
Create a TComparableByKey trait providing <, <=, >, >=, and between:and: from a required key method; use it in two unrelated classes.
Implement the Visitor pattern for an arithmetic expression tree with Evaluator, Printer, and Simplifier visitors.
Add Announcements to a TodoList model (ItemAdded, ItemRemoved, ItemCompleted) and write a Transcript logger and a counter that subscribe to them.
Write a producer/consumer pipeline with three processes and two SharedQueues that reads lines from a file, uppercases them, and writes them to another file; make it terminate cleanly.
Build a Spec2 application for the TodoList with a list, a text input, Add/Remove/Complete buttons, and live updates via Announcements.
Write a Teapot REST API for the todo list with JSON output (STONJSON or NeoJSON) and test it with ZnClient.
Call qsort (or strlen, getpid, time) from libc with UFFI; then profile (1 to: 1000000) asArray shuffled sorted with the Time Profiler.