Intro Notes on the Smalltalk Environment

This is a follow-up to my post about Smalltalk language basics. Here I’ll talk very generally about the Smalltalk environment — at least what I remember about it. Like my previous post, this is meant to be a lightweight conceptual orientation.

The main thing to get is that the Smalltalk environment is a complete, self-contained universe of objects. The Smalltalk language defines an abstract syntax for creating objects and sending. The Smalltalk environment contains actual objects — things in memory being managed by a running process on your computer — that can interact with each other and that you can interact with.

Here's what happens when you do Smalltalk development:

  • You launch the VM (virtual machine), which is a runtime engine that will interpret and execute all your Smalltalk code.
  • You load a file called a Smalltalk image, which contains a snapshot of the aforementioned universe of objects.
  • Once the image has loaded, you're presented with a UI for writing and executing code.
  • Everything you do in that UI, even something as simple as moving a window, has some effect on that "universe of objects": you're either creating, modifying, or destroying one or more objects in that universe.
  • Before you quit Smalltalk, those changes are saved to the image file to be reloaded next time. (You need to know that there's actually a separate file called the changes file, but that's an implementation detail for purposes of this discussion.)

A rough analogy would be that the VM is like a game engine and the image is like a massive file containing the state of game play exactly as it was when you left off.

The image contains descriptions of all the objects that existed in the "universe" when the snapshot was taken. When the image is loaded, all those objects are re-instantiated. Since everything in Smalltalk is an object, including windows, this means all the windows that were open when you left off will re-appear right where you left them. If you had a debugger window that had stopped at a breakpoint in some code that was running, that debugger will re-appear in exactly the state it was in previously, and you will be able to resume executing that code as if you'd never quit Smalltalk, since the execution context was yet another object that was saved in the image.

Here are some kinds of windows you can open in the Smalltalk environment:

  • Browser. A browser window lets you browse the source code for all the classes that exist in the environment, including not only classes you wrote but also Smalltalk's built-in classes, which are themselves written in Smalltalk. (Classes are objects, so they are reconstituted and added to the environment when the image is loaded, just like all other objects.) Browsers are where you create your own classes.
  • Workspace. A workspace window is basically a scratch pad where you can type Smalltalk code and run it.
  • Transcript. A transcript is a special kind of workspace. You can write text to the global object named Transcript, and that text will be written to all transcript windows. You can use this anywhere in your code to print output. For example:

    Transcript show: 'About to add two numbers.'; cr.
    x := 3 + 4.
    Transcript show: x; cr.
  • Inspector. An inspector window displays the internal state of an object — its instance variables. You can open an inspector on any object by sending it an #inspect message (e.g. try this: 3 inspect) or, in some contexts, by right-clicking on it and selecting "inspect it" from the contextual menu.
  • Debugger. A debugger window lets you step through running Smalltalk code, examine code being executed, and inspect the objects involved. A handy feature of Smalltalk is that in general you can modify code in the middle of debugging, and the new code will be used without your having to restart the program you were running.

You can open as many of each type of window as you want. You generally open them by right-clicking and selecting from a contextual menu, or by hitting a keyboard shortcut.

Speaking of contextual menus, the last thing I'll mention is some operations that are available anywhere you can select Smalltalk source code. If you right-click one or more statements, the contextual menu will contain a whole bunch of options, of which these are the handiest to start with:

  • do it — Executes the selected code.
  • print it — Executes the selected code and prints the resulting value immediately after the selected text.
  • inspect it — Executes the selected code and opens an inspector on the resulting value.

There are typically keyboard shortcuts for these actions.

Quick Intro Notes on the Smalltalk Language

[I'm posting this for some friends who have asked about Smalltalk.]

"Smalltalk" refers to both the Smalltalk language and the Smalltalk environment. I've forgotten a lot about the environment, and even more about the standard libraries, but I remember the language pretty well, partly because Objective-C is a hybrid of C and Smalltalk. Here's a quick intro; hope it helps.

The one main thing to know is that Smalltalk is all about objects sending messages.

  • Everything is an object.
  • Smalltalk code does only two things: assign variables and send messages.
  • Objects are instances of classes, which are themselves objects.
  • Classes live in an inheritance hierarchy.

An object is a thing that:

  • has state (in the form of instance variables),
  • has behavior (in the form of methods, which are named blocks of executable code associated with the object's class), and
  • can respond to messages.

There are only objects. That includes numbers, strings, windows, menus, classes, code blocks (including methods), and so on. Anything that's a "thing" is an object. There are no enums, no structs, no C-like "primitive" types as in Java, Objective-C, or C++. Only objects.

A message consists of a method name, called the selector, plus zero or more arguments. An object responds to a message by executing its corresponding method, if it has one. The code inside the method sends messages to other objects.

So: an object can respond to messages, and a message is a thing you can tell an object to do. An object responding to a message is called the receiver.

The terms "message" and "method" are sometimes used interchangeably, but it's important to understand the distinction. A message is like an interoffice memo that says "do X". A method is like the page titled "X" in the employee handbook. Note that different receivers of the same message can have different employee handbooks, and may therefore do "X" differently.

Although methods are essentially functions, this message-sending paradigm is different from the function-call paradigm in procedural programming. When you call a function in C, you identify the function by name and specify zero or more arguments. For example:

moveToXY(myTurtle, 5, 10);

By contrast, when you send a message in Smalltalk, you're asking an object to respond to a combination of method name and arguments. The receiver then decides what code to execute — roughly speaking, what function to call. For example:

myTurtle moveToX:5 y:10.

In this statement:

  • The receiver is myTurtle.
  • The message being sent to myTurtle is moveToX:5 y:10.
  • The selector in that message is #moveToX:y:, pronounced "move to x colon y colon". (IMO it's okay to just say "move to x y" for brevity if it's clear you don't mean moveToXY:.)
  • The arguments in the message are 5 and 10.

Your code is allowed to send any message to any object. When you run the code, the object will figure out upon receiving the message whether it knows how to respond. (Well, technically it's the Smalltalk runtime that figures this out.)

Every Smalltalk statement, other than variable assignment, has the same form as above. That's almost all the syntax you need to know. In particular, there is no control-flow syntax. If-then, while-do, for-each, and so on are done with message sends. Historically, the answer to "How on earth is that possible?" has been one of the first "aha's" people experience when they're new to Smalltalk. These days, with languages like Ruby in the mainstream, it's not so much of an "aha".

I'm running out of steam so that's all for now. 🙂

Notes After Finishing Advent of Code 2016

I finished this year's Advent of Code. I'd love to go back and clean up my code and write notes about each problem and the process I went through and the things I learned. Maybe I'll get around to that, maybe not.

Here are some ways of doing things that I found useful.

  • Write code to help visualize the problem. Helps me; YMMV.

    • Write code that draws pictures of the data. Take a hint from the problem descriptions — generate pictures similar to theirs. I found this helpful for the maze problems, among others. And sometimes it just makes things more fun.

    • For one problem I found that simply writing code to generate the following from the input helped me feel less confused about what had to be what mod what:

      we want t % 13 == 11
      we want t % 19 == 7
      we want t % 3 == 1
      we want t % 7 == 2
      we want t % 5 == 2
      we want t % 17 == 6
    • Another textual "visualization" aid: I used comments and indentation to mark up the "assembunny" code so that I could understand better what it was doing (here's what I did).

    • I didn't do much analog drawing (on paper or whiteboard), only a little during the later exercises. I used to be a compulsive whiteboarder — I'd like to recultivate that instinct.

  • If something feels like a hint it probably is, at least for AoC.

    • Example: for some reason Day 22 (moving data from disk to disk) was by far the hardest for me. I'm not sure how much or or how little that was due to inherent difficulty of the problem. But I do know I kept sabotaging myself by not letting the hints sink in, even when I was vaguely aware that the author of the problem was trying to tell me something.
  • Study the data — look for simplifying assumptions you can make that might help you get the answer by solving a less general case than the problem description alone might lead you to believe.

    • Day 22 is an example where this made all the difference in the world. Another that comes to mind is the "assembunny" problems.
  • If something is taking crazy long to execute, you may either have misunderstood or misread the problem, or you may be overlooking a hint.

  • Instrument your code with printf's that periodically show things like the size of your cache. This can provide clues to where there are leaks or inefficiencies in your code. For example, I realized my A* implementation was not properly putting nodes in the closed set, which led to an explosion of the open set — no wonder it was taking so long!

  • Testing. Do regression tests. Test the example data, which should run almost instantaneously, before trying your solution on Part 1. If Part 2 requires a lot of additional work, see if you can do sanity checks along the way to see if your code still solves Part 1. Sometimes your work on Parts 1 and 2 will be different enough that you can't really do this.

Swift vs Python for Advent of Code

Lately my daily addiction has been Advent of Code. It's a two-part programming challenge posted every day at midnight from Dec 1 to Dec 25. As long as you submit the right answer, you can use any language you like, or pencil and paper for that matter. It's not like some programming-challenge websites where they validate your solution by executing code that you submit.

I've been going back and forth between Swift and Python, both languages I'm new at. It's been a useful learning experience, both at the nitty-gritty level of language details and a more meta level about how I could maybe solve problems better. Sometimes I port my solutions from one language to the other, either to compare how the languages feel, to compare how the code performs, or simply for practice.

Lately I've been strongly preferring Python as my go-to language for these exercises. I have three main reasons.

Reason One: my Python code launches quicker without needing a moment to compile like Swift does, which means I can test and iterate faster, which means more immediate gratification.

Reason Two: simple string manipulation and array slicing are much quicker to code in Python, which means I do less typing and my simple intentions aren't buried in syntax. I wonder how daunting the learning curve seems regarding Swift strings, whether for new programmers or programmers coming to Swift from other languages. I wonder if there are some simplifications I'm missing.

Reason Three: it's trivial in Python to get an MD5 hash, which a few of this year's exercises have been requiring. People have written Swift wrapper code that does MD5 hashes, but every time I start to explore my options I think, "I'd rather work on solving the puzzle I wanted to solve, and I can do that right now in Python." Again, immediate gratification. Also, the Swift wrappers I've seen all require using an Objective-C bridge, which as far as I know requires using an Xcode project, which makes things heavier than I'd like.

One thing I'd like to revisit and get better at is using Swift playgrounds (with a lowercase "p" as in Xcode, not an uppercase "P" as in iPad; it drives me curse-out-loud nuts when I try to Google for the former and only get results for the latter). I tried using playgrounds for the early Advent of Code exercises, but I felt too attached to using breakpoints and lldb, which aren't available in playgrounds. Instead I've been using CodeRunner, which conveniently integrates debuggers for both Swift and Python. It's just right for this kind of lightweight coding and experimenting.

Here's my code. Note that this is not production-quality code. It's often sloppy, it often misses opportunities to solve the problem in a smarter way, it almost always assumes valid input, it's under-commented, and in at least one case it's way over-engineered.

Notes After 8 Weeks at the Recurse Center

I just finished week 8 of a 12-week "batch" at the Recurse Center, which is a diverse community of people who come together to help each other become better programmers. The community includes both current attendees and "alums" who stay connected in various ways.

Each week I've been here has been better than the last. Yesterday, for example, I learned more than I do on an average day:

  • I learned some Python.
  • I saw a neat algorithm using matrix multiplication.
  • I got an explanation of Markov chains.
  • I attended an excellent talk that was an introduction to dynamic programming.

All this learning was unplanned except the talk on dynamic programming, which I had signed up for in advance. For example, yesterday morning I noticed some people were working on a problem on HackerRank. I got the urge to tackle it myself, and used it as an opportunity to practice Python coding, which I've been meaning to "get around to" for years. (In case you're wondering, my solution passed all of HackerRank's automated tests, but only after a bunch of fixing and reworking.)

All learning at the Recurse Center is attendee-driven. There is no lesson plan, there are no formal deliverables, and there is no certification at the end. Indeed, RC's motto is "Never graduate." We attendees show up on day 1 with some idea, possibly vague, of what we want to work on. It is perfectly fine, even encouraged, to change our minds about this over time, as long as we stay ambitious. We spend most of our days coding, studying, "pairing", doing self-organized workshops and presentations, and socializing.

RC occupies an office space in Soho, a stone's throw from Chinatown. It's a quick subway ride from my apartment, and I can walk home in less than half an hour. I think there's about 50-60 of us here during peak hours, including staff.

The interior resembles a typical open-plan tech startup office. I'm generally against open-plan design, but I like it in this case, because it serves one of RC's goals, which is to encourage us to interact with each other. I don't feel like the activity around me is intrusive; on the contrary, I like overhearing what people are talking about. Also, since there is no pressure, competition, or judgment regarding what I'm working on, I don't feel the exposed anxiety I would feel in an otherwise similar workplace setup.

Besides, the space isn't 100% open. There's a library with books on all kinds of programming topics, and there are rooms where people can hold meetings or just sit and program quietly when we don't feel like working in the open area. Every room is named after a famous computer scientist. Right now I'm sitting in Lovelace. The biggest meeting room is called Hopper. Various parts of the open area also have names: Dijkstra, Ritchie, etc.

Another nice aspect of the physical facility is that the WiFi is very fast and reliable. This means one less hassle in our day-to-day lives getting in the way of progress.

The Recurse Center strongly emphasizes diversity and support for groups that have been underrepresented in tech. I've met attendees from countries including Poland, the UK, and Singapore. All experience levels are represented. We skew pretty young, as in like 20's or 30's, but there are middle-aged folk like me as well. The community includes gay people, trans people, and black people — all in small but still, to my mind, significant numbers.

By design, roughly half the attendees are women, and WOW does that make a difference. I've worked with and for women before, but never alongside this many, not by a long shot. It changes things in a way that I'm not sure yet how to describe, except that I feel more comfortable being myself.

Speaking of comfort, there are four gentle "social rules" we're expected to observe:

  • No "well actually's".
  • No backseat driving.
  • No subtle "-isms" (sexism, racism, ageism, etc.).
  • No feigned surprise (as in "I'm surprised you didn't know X", which can have the effect of belittling relative beginners).

I suspect I was the first person in my batch to break one of these rules. On the first day, I brought some batch-mates to Chinatown for dim sum. I caught myself assuming I was the only one proficient with chopsticks. Subtle racism — mea culpa.

The Recurse Center selects attendees through an application process that includes two Skype interviews. You don't have to be a genius or a rock star to get in, although there are definitely people here who impress the heck out of me. My application process felt like the best "job interview" experience I've ever had, in terms of both making me feel understood as a person and making me feel like these were people I wanted to work with.

Attendance is 100% free. RC supports itself through job placements for its attendees. There are lots of support activities for people preparing to go job hunting, including interview practice sessions. There's no obligation to go through RC to get a job or even to be on the job market. And job-related activities should not distract from the primary business of learning and improving as programmers.

I hope to keep sharing more about this place. The staff, for example, is terrific. I might try to think of some complaints; nothing's perfect, after all. Until then, I'll just say this is the best thing to happen to me in a long time.