Cantilever Laptop Stand

I'm really liking this laptop stand. It's a bamboo cutting board clamped to a bamboo file sorter. Looks nice, very sturdy, and I can either sit or stand.

When I'm sitting, this arrangement allows me to bring the screen closer to me than, say, if I rested the laptop on a stack of books. This helps me avoid the habit I have of leaning forward to peer at the screen, which has caused me lots of neck and shoulder pain over the years.

The screen is at just the right height for me but might have to be higher for someone taller.

Assembly involved no measuring, no cutting, no drilling, and no skills. Also no cost, since it's made from things I already had.

I should make this into a kit and sell it at 100% markup. I'd call it "The Diveboard", and I'd make up a bogus story about months of research into design, ergonomics, and sustainable materials.

laptop stand

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.