restrictive or: notes from the dark side

:: Programming Languages, Teaching

By: John Clements

Okay, it’s week four of data structures in Python. In the past few days, I’ve read a lot of terrible code. Here’s a beautiful, horrible, example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# An IntList is one of
# - None, or
# - Pair(int, IntList)
class Pair:
    def __init__(self, first, rest):
        self.first = first
        self.rest = rest

    # standard definitions of __eq__ and __repr__ ...

# A Position is one of
# - an int, representing a list index, or
# - None

# IntList int -> Position
# find the position of the sought element in the list, return None if not found.
def search(l, sought):
    if l == None:
        return None
    rest_result = search(l.rest, sought)
    if (l.first == sought or rest_result) != None:
        if l.first == sought:
            return 0
        else:
            return 1 + rest_result

This code works correctly. It searches a list to find the position of a given element. Notice anything interesting about it?

Take a look at the parentheses in the if line. How do you feel about this code now?

(Spoilers after the jump. Figure out why it works before clicking, and how to avoid this problem.)

Okay, that’s just awful. The intent was to write

1
if (l.first == sought) or (rest_result != None):

instead of

1
if (l.first == sought or rest_result) != None:

… but the student misparenthesized. The student’s code works because if l.first is equal to sought, the or evaluates to True, which is in fact not equal to None, so you wind up in the right place. Otherwise, the or winds up being the value of rest_result, which is then correctly compared to None. Note also that there’s no else case, meaning that the function secretly returns None in the fall-through, which is also correct.

I hope you agree with me that this code is abominable, and we’d like to prevent students from writing it.

What’s the fix? I think the obvious fix is to ensure that or only works on booleans. This is guaranteed by most typed languages in the type system, or by a dynamic check in the implementation of or.

Does Racket have this problem? No, it does not. In the case of Racket, though, it’s because of the notion of “student languages,” an idea to which many people pay lip service but which few people actually carry out.

Anyway: types or language levels FTW. Or, more precisely:

Python FTL!