Here’s a quick demo video of the status of my quixotic project to get
Squeak running as a kind of standalone userland on a modern cellphone:
(The sound is bad! I recorded it on my other cellphone…)
Currently, I develop by coding in Squeak on my Linux desktop, using my
graphics tablet as a kind of proxy for a touchscreen. I use the FFI
and AIO/OSProcess support in Squeak to read events from
/dev/input/event.... For event sources that present absolute axes, I
create instances of HandMorph in the World and animate them
according to the incoming events.
Every now and then, to test on the real hardware, I use rsync to
copy the changes and image files up to the cellphone, and then log in
to the phone over ssh to restart the Cog VM.
At the moment, the image has enough smarts to figure out how to read
the touchscreen and offer basic touchscreen click support. This lets
me do simple things like open, move and close windows, and lets me
save and/or quit the image.
Next steps are to make it harder to misclick – perhaps by increasing
the size of some of the touch targets – and to think about coding up
a simple onscreen keyboard.
Alternatively, on a parallel path, I’ve been reverse-engineering
(really nothing more sophisticated than strace of cbd and rild)
the Samsung protocols for booting and operating the cellular modem.
The code is short and simple. Perhaps instead of an onscreen keyboard
I’ll code up a quick dialer Morph and get Squeak making phone calls.
I’ve made some
minor image changes
to adjust cached glyphs in TrueType fonts in Squeak when the DPI
changes. Here are the results:
Squeak, under the illusion that the screen is 96 DPI
Squeak, correctly configured for 535 DPI (!!)
On the left, a stock, fresh-from-squeak.org unconfigured image, that
wrongly believes the screen to be 96 DPI.
On the right, my dev image as I left it on my desktop PC, simply
scped up to the phone and run, set to the correct 535 DPI resolution
for the phone. Much better!
Back in 2007, when Openmoko
was first a thing, I
wrote an Erlang-based userland
that got to the point of being able to take and make calls and receive
and send SMS. The project stalled: the Openmoko GTA01 was too slow,
its power-management too primitive, and Erlang’s GUI facilities too
rudimentary to make further work worthwhile.
Modern cellphone hardware is much more capable. Is it time to have
another run at the idea of a mobile personal computer?
Last week, I installed PostmarketOS on my
previous cellphone, a Samsung Galaxy S7 (using PostmarketOS’s
samsung-herolte
configuration).
PostmarketOS turns out to be a beautifully engineered system that’s
easy to understand and modify. The basics of kernel and Alpine Linux
userland installed cleanly and easily on the phone, and it’s running
well as a development platform. I’m looking forward to getting into
PostmarketOS more deeply.
htop running on my cellphone. Six cores!
Running htop on the phone shows what an amazing little machine it
is! So much power. Loads of cores, lots of RAM. Plenty of space to
explore alternative visions of mobile personal computing.
However, the built-in demos, such as the
Weston demo (shown above
at right), currently leave quite a bit to be desired. Perhaps some of
the other
user interface options
included with PostmarketOS could get me closer to a day-to-day usable
cellphone - but I’m interested in running my own software! Let’s get
hacking.
Running my own programs
PostmarketOS is a plain, clean Alpine Linux distribution. You can SSH
into it initially
via USB networking.
From there, you can
configure wifi using nmcli,
set up SSH keys, and then access it directly using SSH over wifi.
lflow: Framebuffer demo
Building software is just as simple:
apk add alpine-sdk
To experiment with drawing to the framebuffer and reading touchscreen
input via /dev/input, I compiled and ran an old
quick and dirty framebuffer hack
I wrote years ago. The results (shown at left) were encouraging: the
program effortlessly animates tens of thousands of points at 30 frames
per second, responding to touch inputs. Display is via brute-force
pixel output to the mmap‘d frame buffer. It doesn’t even use a full
core.
PostmarketOS turns a phone into a fully capable Linux machine, with
total control over the attached hardware, and with everything
accessible to the developer in the usual places using the usual tools.
But Unix tools are inappropriate for a mobile personal computing
platform. We’ll need something else.
Smalltalk could make an ideal basis for a mobile personal computing
platform.
I’ve enjoyed using,
developing with, and contributing to the
Squeak Smalltalk implementation since the mid
’00s.
So I compiled the
Cog Smalltalk VM
on the phone itself, making use of the 64-bit ARM support code that
landed extremely recently.
And lo and behold, it runs! Shown to the right is a bleeding-edge,
fully up-to-date Squeak 6.0-alpha image running on the phone itself.
(Click here or on the image to embiggen.)
From here, I can experiment with new ideas using the full power of a
modern Smalltalk environment.
What next?
My previous Openmoko experiments foundered, in part, on the GUI aspect
of the system; GTK+ via Erlang was fine for quick prototyping but
wasn’t really up to the task for a day-to-day usable machine.
I recall getting Squeak running on my GTA01, in order to see if it
could provide a viable UI. However, I remember being stymied by the
mismatch between the expectations of the Smalltalk environment and the
realities of the phone.
Squeak wants a mouse and keyboard. It assumes a monitor-sized display,
in everything from widget and font sizes to window management. To work
well on a phone, it needs a touchscreen-based, high-DPI UI in addition
to its existing toolset.
So I think using Erlang/Syndicate-style
Actors to structure a
Smalltalk-based phone userland, perhaps with cgroups-based
sub-virtual-machines and images, could work well.
My initial experiments have concentrated on
fixing the tiny fonts (the DPI-change support code in the image
needs work, and the support in the VM seems to be absent (?)),
reading from the touchscreen (probably
like this),
thinking about how to structure Actor supervision hierarchies and
Dataspaces
for a mobile phone (probably borrowing some design elements from my
earlier
Openmoko Erlang-based userland),
and
thinking about how to layer a touchscreen (panel-based?) GUI atop
Squeak’s Morphic UI.
I’ll write more on this blog under the tag #squeak-phone as things
develop.
I am doing some fascinating and rewarding contract work that makes
direct use of some of the skills I developed and knowledge I acquired
during my PhD studies. It’s bloody wonderful and I’m very lucky.
I’m even luckier that it’s not currently a full-time gig. This means I
have, in principle, plenty of time to pursue my own ideas. Pandemic
and family life permitting, of course.
While there’s a lot of joy in building things just for myself, it’s
also a lot of fun to share the things I make with others. So I’ve
decided I’ll aim to write more here about what I’m doing.
I’ve just figured out (with the assistance of pages like
japaric’s rust-cross guide)
how to cross-build a small Rust project for FRμITOS, which is an
Alpine Linux based distribution (using musl libc) for Raspberry Pi,
using a Debian x86_64 host.
It was ultimately very nicely straightforward.
Install the Debian binutils-arm-linux-gnueabihf package.
Use rustup to install support for the armv7-unknown-linux-musleabihf target.
Set the CARGO_TARGET_ARMV7_UNKNOWN_LINUX_MUSLEABIHF_LINKER environment variable appropriately.
Run cargo build --target=armv7-unknown-linux-musleabihf.
Recent discussions (e.g.
1,
2)
about potentially revising Racket syntax for Racket2 have reminded me
I never properly announced #lang something,
an experiment from back in 2016.
The main idea is S-expressions, but with usually-implicit parentheses
and support for prefix/infix/postfix operators. Indentation for
grouping is explicitly represented in the S-expression returned from
the reader.
(+) keeps a semi-structured input format: reader yields ordinary syntax
(-) uses indentation (though it doesn’t have to; see for example this module)
(-) the function syntax isn’t function(arg, ...)
(More links at the bottom of this post.)
In addition to the reader, #lang something provides a small
selection of special forms that take advantage of the new syntax, and
#lang something/shell adds Unix-shell-like behaviour and a few
associated utilities.
This program:
#langsomethingfor{x:1..10}defy:x+1printf"x ~a y ~a\n"xy
… reads as this S-expression:
(modulesomething-modulesomething/base(#%rewrite-body(for(block(x(block(1..10))))(block(defy(block(x+1)))(printf"x ~a y ~a\n"xy)))))
The #%rewrite-body macro, together with its companion
#%rewrite-infix, consults an operator table, extendable via the
def-operator macro, to rewrite infix syntax into standard prefix
S-expressions using a Pratt parser.
The block syntax has many different interpretations. It has a macro
binding that turns it into a Racket match-lambda*, and it is used as
literal syntax as input to other macro definitions.
For example, here’s one possible implementation of that for syntax:
Notice how the block S-expressions are rewritten into a normal
S-expression compatible with the underlying for from racket/base.
Generally, all of these forms are equivalent
x y z x y z: x y z { a; b }
a a
b b
and they are read as
(xyz(blockab))
and are then made available to the normal macro-expansion process
(which involves a new infix-rewriting semi-phase).
Colons are optional to indicate a following suite at the end of an
indentation-sensitive line. Indentation-sensitivity is disabled inside
parentheses. If inside a parenthesised expression,
indentation-sensitivity can be reenabled with a colon at the end of a
line:
ab(cd:ef)=(ab(cd(blockef)))ab(cdef)=(ab(cdef))
Conversely, long lines may be split up and logically continued over
subsequent physical lines with a trailing \:
abc\d\e=(abcde)
Semicolons may also appear in vertically-laid-out suites; these two
are equivalent:
x y z
a
b; c
d
x y z { a; b; c; d }
Suites may begin on the same line as their colon. Any indented
subsequent lines become children of the portion after the colon,
rather than the portion before.
This example:
xyz:abcde
reads as
(xyz(block(ab(block(cd)e))))
Square brackets are syntactic sugar for a #%seq macro:
[a; b; c; d e f] → (#%seq a b c (d e f))
[ → (#%seq a (b (block c)) (d e f))
a
b
c
d e f
]
Forms starting with block in expression context expand into
match-lambda* like this:
The map* function exported from something/base differs from map
in racket/base in that it takes its arguments in the opposite order,
permitting maps to be written
A few years back, I decided to try to put a little bit of structure on
how I kept records of such things as
ideas and thoughts I have, related to my work
procedures I performed in setting up machines and software
phone calls I’d had for arranging real-life things
important identifiers and numbers and so on
meeting notes
to-do lists
I’ve ended up with a loose collection of journal-like documents, each
with a different feel.
A master org-mode document, which is
always open in a buffer in my Emacs session.
It contains
a plain-text time-stamped journal of thoughts, ideas,
workings-through of proofs and formalisms, book and paper
reviews, talk notes, meeting notes, phone call notes, etc.
detailed step-by-step records of how I’ve installed and
configured various pieces of software for specific tasks;
“how-tos”, essentially, for when I have to do the same kind of
thing again
detailed step-by-step records of how I’ve set up various servers
to-do lists
I use org-mode sections, with one top-level heading called
Journal containing the bulk of the entries.
To-do items each get a top-level heading of their own and an
org-mode TODO tag. Completed to-do items are demoted to
second-level and moved into a “done items” top-level heading.
Here’s a sample of just a few headings - each entry in the real
document also has a bunch of text contained within it.
* Journal
:PROPERTIES:
:VISIBILITY: children
:END:
** (2011-05-08 14:31:13 tonyg) Vertical interpretation vs Horizontal interpretation :STUDY:
** (2011-05-19 00:00:00 tonyg) Inter-network routing: should be *tunnelling* not *chaining*
** (2011-05-19 11:49:40 tonyg) Memory Pool System - API for virtual machine interface? :STUDY:
** (2011-05-19 18:30:50 tonyg) Scripting languages integrate with system languages. :STUDY:
** (2011-05-23 00:00:00 tonyg) Origins of Credit-based Flow Control and Acks, functional this time
** (2011-05-24 10:13:33 tonyg) Message buffer size should be determined by arrival-time jitter :IDEA:
** (2011-05-24 10:14:08 tonyg) Fine-grain scheduling in a distributed system using PLLs :IDEA:
* TODO Build an imperative workalike os.rkt that uses real threads :PROJECT:
* TODO Upload ~/src/racket-kademlia
* TODO racket-rc4: RSA, DSA, DH, ECDH, ECDSA, etc
* TODO Thank-you notes for xmas gifts!
* Old, done to-do items
** DONE Configure Flashbake and git-syncing for Uni
** DONE Write presentation
** DONE View mini-DVD and write up evaluation
Some of the journal entries have gone on to become blog posts here.
The document lives in a git repository, and a git commit is
executed by cron every five minutes. I have checkouts of the
repository on the two or three machines I use most frequently. I
make extensive use of a variant of Emacs’ time-stamp feature:
… which inserts text like 2019-05-19 13:15:43 tonyg when I run
M-x stamp.
A second such document, very similar, that also includes slightly
more in the way of private or personal information, that I don’t
have checkouts of on as many machines.
A paper journal, which I use when I need the different style of
thinking it affords. The freedom to draw sketches and rough lines
connecting thoughts is useful from time to time. I use a nice
fountain pen that a friend gave me, even though it smudges horribly
because I’m left-handed.
A Google doc that is a project-specific journal for one of my main
projects, Syndicate. It’s a Google
doc because I share it with various collaborators, and because I
occasionally want to put pictures in the file. Otherwise it’s
similar in feel to the main org-mode document I use: a record of
thoughts, ideas and workings-through.
In every case, the documents function as append-only logs of thoughts
and workings-through. I draw on them when digesting and summarising in
later write-ups and in writing actual software. They’re mostly useful
as an audit log, for finding out what I was thinking or what I did
in the past, or for trying to reconstruct an argument.
The electronic documents are searchable, of course. It’s inconvenient
that they are in different places, and I sometimes don’t know which of
the small number of documents has the item I am looking for, but it’s
not too unmanageable.
The paper documents are a different story. I intend eventually to
photograph each page of my paper journal as a kind of backup, though I
haven’t yet started doing so. Indexing that archive will require a bit
of work too.
About two years ago I wrote an
Erlang-style Actors implementation for Squeak Smalltalk,
based on subclassing Process, using Smalltalk’s Message class to
represent inter-actor messages, and using Promise for RPC. Roughly a
year ago, I finally dusted it off, documented it, and released it.
It draws on my experience of Erlang programming in a few ways: it has
links and monitors for between-actor failure signalling; it has
library actors representing sockets; it has a simple tracing facility.
There’s crude and no doubt heavily problematic support for basic
Morphic interaction.
It’s by no means as ambitious as
other Smalltalk Actor systems:
it only deals with single-image in-image messaging between actors, and
doesn’t have the E-style ability to refer to objects within a vat.
Instead it follows Erlang in having references denote actors (i.e.
vats, roughly), rather than anything more fine-grained.
Next steps could be:
a Workspace that was actor aware, i.e. each Workspace an actor.
better Supervisors.
tools for visualizing the current constellation of actors,
perhaps based on Ned Konz’s Connectors?
an ActorEventTrace subclass that is able to draw message interaction
diagrams as a Morph.
It could in principle work in Pharo, as well. I did try to port it to
Pharo, but found two main obstacles. First, Pharo doesn’t have an
integrated Promise implementation;
the Actors project makes heavy use of promises.
Second, I couldn’t get Pharo’s sockets to behave as reliably as
Squeak’s. I don’t remember details, but I’d be very pleased if a Pharo
expert were to have a try at porting the code across.
The project has recently been discussed on
HN; please feel free
to get in touch if you have any
questions, comments or feedback.
Smalltalk is three things in one. It is a language; it embodies a
language design idea; and it is an operating system.
Learning Smalltalk gives you three things:
An understanding of Smalltalk, the language. This is least
essential. It’s also the kind of thing you can pick up in a single
30-minute session.1 The language is tiny and
simple.
An understanding of the design idea, uniformly object-oriented
programming. This is crucial and will connect with other styles
of programming including Actor-based and pure-functional. This is
something you will never get from Java, which is not a uniformly
object-oriented language.
An understanding of a completely different and (these days)
unusual way of designing an operating system. An object-oriented
operating system, to boot. The IDE, the debugger, and the other
tooling all integrates to give a level of manageability in many
ways far superior to e.g. Unix or Windows.
After a while, you may find yourself discovering subtle things about
the interplay between the three aspects of Smalltalk that you can then
apply in your own designs. For example, Smalltalk is a “dynamic”
language, but the way classes and methods are arranged is very rigid,
giving a lot of static structure to the system organisation. This
static structure is what unlocks the powerful tooling, and is what
simplifies the programmer’s mental model to make the system
understandable. Comparable OO and almost-OO languages, such as Python,
have a more “dynamic” system organisation, making it harder to write
tools for automatically manipulating Python systems and making it
harder for programmers to understand how Python code, data, state, and
processes come together to form a running program image.
Yesterday, I became inspired to learn about the kinds of generalized,
pure-functional getters and setters that the FP community calls
“optics”: Lenses, Prisms,
Folds, Traversals, and so on.
I quickly realised that I needed to brush up on some of the core
typeclasses involved, namely Functor and Applicative. I spent the rest
of the day reading up on the laws and definitions involved.
Brent Yorgey’s
Typeclassopedia and Miran
Lipovača’s
Learn You A Haskell
were both great starting points. The latter for the basics, some good
examples, and lots of context-setting and motivation, and the former
for completeness, concision, and connections to the other foundational
ideas in the Haskell standard library.
Applicative laws vs. Monoidal laws
The
laws for Applicative functors
are complicated, and have obscure, largely unhelpful1 names like
“homomorphism” and “interchange”.
The
laws for Monoidal functors
are simple and familiar: just left and right identity, and an
associativity law.
However, each set of laws implies the other!
The two formulations are equivalent. I spent most of yesterday evening
proving this, as suggested by
the exercises
in the Typeclassopedia section dealing with Monoidal functors.
The main thing I learned from this exercise is that the Applicative
laws are self-contained. They imply the Functor laws, the Monoidal
laws, and the “naturality” law all by themselves. By contrast, the
Monoidal laws are not self-contained: in order to derive the
Applicative laws, you need appeal to the Functor and “naturality” laws
from time to time.
On the one hand, the Applicative laws are ugly, but self-contained. On
the other hand, the Monoidal laws are cleanly factored and separate
from the Functor laws. But on the gripping hand, programming with
Applicative is reported to be much less of a pain than programming
with Monoidal. I believe it!
All this suggests that choosing the Applicative formulation over the
Monoidal one makes sense when designing a language’s standard library.
After all, proving that an instance of Applicative respects the laws
can be done either in terms of the Applicative laws or the
Functor+Monoidal laws, meaning that not only does the programmer have
the better API, but the implementor has a free choice of “proof API”
when discharging their proof obligations.
A handy lemma
Another result of working through the proofs was discovery of this
handy lemma:
pure f <*> u <*> pure h = pure (flip f) <*> pure h <*> u
It’s intuitively obvious, and something that I think many users of
Applicative will rely on, but it took a bit of thinking to realise I
needed it, and a little more thinking to get the proof to go through.
(Exercise: Imagine replacing pure h with v. Why does the new
statement not hold?)
Onward to Foldable, Traversable, Lens and Prism
I think today I’ll read the rest of the Typeclassopedia, with
particular focus on Foldable and Traversable. If I have time, I’ll get
started on Lens.
The names make sense in light of their
category-theoretic origins. I’m not well-versed in this stuff, so
for me they are only vaguely suggestive. ↩