__repr__()

I think repr() is Python’s genius touch. What?! I’m sure you’re yelling at the screen right now, incoherent as items from the long list of things you love about Python fight each other to get gain command of your voice. But it’s what I think. And as you know, Lisp got there first.

Now, of course, you’re really mad at me. But listen. What do we use Python a lot for? We use it for anything that requires our language to be nimble, fluid and user friendly, like data analysis and algorithm prototyping. And why do we use Python? Because it has a REPL. We’ll think of something to do, we’ll write a set of functions and classes to do it (sometimes never leaving the REPL) and then run our data on it.

The key feature of the REPL is being able to see the results of a computation immediately printed below what we did. This is a user comfort feature, and user comfort has a disproportionate impact on our motivation to keep trying out new ideas.

Python’s built in types have decent printed representations, but these can get crowded (dicts with many keys get hard to read quickly, for example). And what about user defined classes? Often, the results of a computation are not a few strings or simple, single numbers. The result of adding two arrays or two tables can be visually complex, for instance. Representing these as unreadable combinations of Python’s built in types or equally unreadable pointer ids starts to defeat the purpose of the interpreted, REPL workflow.

To mitigate this, Python supplies us with the special repr() method. When we create a class we don’t have to define this method, but if we do define it, making it return a succinct string which we find useful as a summary of the object, at the REPL we are then rewarded with these succinct, readable representations.

For example, say we are doing some work with vectors and are interested in how the magnitude of the resultant vector changes as we add vectors together. We could do this as follows:

class Vect:
  
  def __init__(self, x, y):
    
    self.x, self.y = x, y

  

  def __radd__(self, other):
    
    return Vect(self.x + other.x, self.y + other.y)

  

  def __add__(self, other):
    
    return Vect(self.x + other.x, self.y + other.y)

  

  def __repr__(self):
    
    return "({}, {}):{:0.3f}".format(self.x, self.y, (self.x ** 2 + self.y ** 2) ** 0.5)

There are some things here in addition to repr which enable us to do vector additions. We will ignore those here.

So now when we create some vectors, we get a nice printout

Vect(3,4)   # -> (3, 4):5.000
Vect(5,12)  # -> (5, 12):13.000

This feature plays wonderfully with Python’s built in types, so a Set of these classes, a dict with these classes as keys or values, or a simple list of these classes all work as you would hope.

[Vect(3,4), Vect(5,12)]       # -> [(3, 4):5.000, (5, 12):13.000]
{Vect(3, 4), Vect(5, 12)}     # -> {(3, 4):5.000, (5, 12):13.000}
{Vect(3, 4): 'A', Vect(5, 12): 'B'}  # -> {(3, 4):5.000: 'A', (5, 12):13.000: 'B'}
sum([Vect(3,4), Vect(5,12)])  # -> (8, 16):17.889

PS. I forgot – I dropped a teaser about how Lisp got there first (as usual). In Common Lisp when you define a structure, the structure gets a default “printer” which you can override:

(defstruct vect x y)    ; -> uses default printer

(make-vect 😡 3 :y 4)   ; -> #S(VECT :X 3 :Y 4)

Now we can replace this with our own printer

(defstruct vect x y)    ; -> uses default printer

(make-vect 😡 3 :y 4)   ; -> #S(VECT :X 3 :Y 4)

(if (too (many parentheses)) (use parinfer) ‘())

When I first started learning Common Lisp I was a little annoyed by the syntax. After practicing writing Lisp code I have grown to like the simplicity of the basic language. However, like C/C++, even though you don’t have to format the code (i.e. add whitespace) it helps readability immensely if you do. In such cases a decently smart editor helps a lot, and the parinfer plugin is really, really neat.

For me, formatting Lisp code like it was Python improves it’s readability by a huge amount. Syntax highlighting, for Lisp, can’t do so much since Lisp syntax is minimal, but indenting things like meaty if statements, nested function calls and lengthy ‘cond’ constructs makes things easy to follow.

Some editor features, for example paredit, tailored to Lisps, are very helpful but, as the plugin manual itself notes:

ParEdit helps **keep parentheses balanced** and adds many keys for moving S-expressions and moving around in S-expressions. Its behavior can be jarring for those who may want transient periods of unbalanced parentheses, such as when typing parentheses directly or commenting out code line by line.

Jarring is right. I use Atom and had paredit running and had a session once where I thought my keyboard was broken because I couldn’t type parens. And not being able to type parens is pretty traumatic when learning Lisp.

Parinfer brings a very useful concept to the domain of Lisp editing. Like a most editor assists, it will close parentheses for you when you open them, and place the cursor in the right place. However, it does a bit of sophisticated parsing to handle the following condition:

(defun func (x)
  (setf x (+ x 3))
  (if (< x 3) t nil))
(defun func (x)
  (setf x (+ x 3)))      <--- note we now need a close parens here
  ; (if (< x 3) t nil))  <--- parinfer automatically generates it
                         <--- when we comment out the last line

(As a side-note, WordPress’ syntax highlighting for the tag does not have Lisp as an option, but I got the desired effect by claiming this was Python!)

Parinfer does take a little getting used to if you’ve been managing parens and indentation by yourself. I had to learn in which part of my S-exp to hit the “return” key to get the desired effect – the two outcomes being advancing to a new level of indentation (nesting) under the parent expression or dropping out to a new, separate expression

Don’t hang up!

I started using multiuser Unix systems in graduate school and I quickly learned to love 'nohup'. It let you do the impossible thing: you logged into a machine, started a long computation and then logged out! You didn’t have to hang around guarding the terminal till 2am. You could go get some sleep, hang out with friends, whatever.  This post is a short survey of ‘nohup’ like things that I’m still using and learning about, a decade and a half later.

Screen: For the longest time nohup <some command> & was my staple. I’d package all my code so that it was non-interactive, reading instructions from a parameter file, and writing out everything to data and log files, fire it off and come back the next day. Then someone introduced me to ‘screen‘. This was amazing. It let me reattach to a terminal session and carry on where I left off, allowing me to keep sessions open as I shuttle between work and home. I like doing screen -S <a name> to start sessions with distinct names.

Mosh: The one thing about screen is that I have to ssh back into the machine and start it up again. Mosh is a client server system that allow roaming, which means in practice, I can open a terminal, work on it, send my computer to sleep, work on the train with no WiFi, get into the office, and when my computer finds a WiFi again, Mosh takes up where it last left off, seamlessly – except for a little glitching on the display. Mosh doesn’t quite replace screen though – if I reboot my laptop or shutdown the terminal app, I lose the Mosh client. The session keeps running, but I can no longer interact with it.

reptyr: Ok, this is a fun one. Now suppose you have a running process that you started in a normal ssh session. Then you realize, this is actually a long running process. Oh great, the flashbacks to the late night terminal guarding sessions in grad school come back. There’s a program caller reptyr that lets you hop a running process from one terminal to another. What you want to do in this case, is start up a screen session and then invoke reptyr from the screen session.

Unfortunately I’ve not been able to get it to work properly – I always get

Unable to attach to pid 11635: Operation not permitted
The kernel denied permission while attaching. If your uid matches
the target's, check the value of /proc/sys/kernel/yama/ptrace_scope.
For more information, see /etc/sysctl.d/10-ptrace.conf

In my original terminal the program just stops and hands me back my command prompt. Nothing happens in my new terminal, but I can see that the program is running. It’s a bit like doing disown, which in turn is like doing nohup retroactively – your process can keep running, but there is no terminal. Fortunately you redirected stderr to a file by doing 2> err.txt, right? right?