Misnomers in the Docs on Swift Parameters

[I filed this as Radar #31462992. I might be overthinking things — this is a little like quibbling over whether something's a metaphor or a simile — but word choices matter to me. You should see how I overthink my variable names.]

In "The Swift Programming Language (Swift 3.1)" the terms "argument label" and "parameter name" are misnomers. I have suggestions for solutions.

Misnomers

Programmers often use the terms "parameter" and "argument" interchangeably, but they have distinct meanings.

  • A parameter is a specification of a value that a function expects to be passed to it. It is an inherent part of the function signature. A programmer who calls the function needs to understand this specification so they can call the function correctly.

  • An argument is an actual value that is passed to a function when the function is called. The programmer who implements the function needs the argument to have a name so their code can reference that value.

The way the Swift documentation uses these terms is the reverse of the above meanings. Consider this function:

func wave(toward person: Person) { print("Hello, \(person).") }

According to the docs, "toward" is the argument label and "person" is the parameter name. This is backwards, because the "toward" is the part that the caller of the function must get right, whereas "person" is a local name that's used in the implementation of the function, and that doesn't matter to the caller at all. The word "toward" is part of the function signature, whereas the word "person" isn't.

To drive this point home, imagine that wave(toward:) is a method of a class. Overrides of the method can replace "person" with whatever name they like, or leave out the separate name altogether:

// class GenericWaver
func wave(toward person: Person) { print("Hello, \(person).") }
 
// class FriendlyWaver
override func wave(toward guest: Person) { print("Welcome, \(guest)!") }
 
// class HostileWaver
override func wave(toward enemy: Person) { print("Get lost, \(enemy)!") }
 
// class LazyWaver
override func wave(toward: Person) { print("Whatever, \(toward).") }

If we use the conventional meaning of "parameter" and "argument", then in all four methods, "toward" is part of the parameter description, whereas "person", "employee", "company", and "toward" are different names for the same argument — contrary to the terminology used in the docs.

Possible solutions

Fixing this misnaming is not quite as simple as switching the terms "parameter name" and "argument label", because those terms actually make two distinctions:

  • parameter vs. argument
  • label vs. name

Consider again the first "wave" function above. Linguistically speaking, what's happening is that the prepositional phrase "toward person" is broken up so that the noun "person" can be referenced in the function implementation. The preposition "toward" is used as a cue to the programmer calling the function, as a signpost, a kind of fill-in-the-blank. The term "label" makes sense. It's analogous to a label in a form-based user interface:

Mail fields for To, From, etc.

By this reasoning, the most accurate correction would be to use the terms "parameter label" and "argument name".

If I only cared about formal precision, I'd leave it at that, but these names feel heavy to me, and still potentially confusing.

Another solution would be to use the wording Daniel Steinberg uses in A Swift Kickstart: every parameter has an "external name" and an "internal name". This approach gets rid of the label-vs.-name distinction, and this simplification buys increased clarity — the distinction between "external" and "internal" is immediately clear. As a bonus, the rest of the documentation would be a little more free to use "parameter" and "argument" interchangeably without risking confusion. That might or might not be a good thing, depending on your taste.

Yet another solution would be instead to get rid of the parameter-vs.-argument distinction, and call the things "argument label" and "argument name".

One more thing. If you choose to have different external and internal names, the docs describe the external name as something that you add to the left of the internal name. To my mind, it's the other way around: I'd suggest describing the internal name as something you insert after the external name. The difference is subtle, but to me it's meaningful, because the external name is primary and essential, whereas the separate internal name can be thought of as an optional implementation detail. (This is a bit of an oversimplification of the thought process behind having an internal name, but I think the general point still stands.)

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.