Easy Typewriter

Fooling the Bear in a Game of Russian Roulette

Stumblings

I stumbled through some awesome lists and onto beartype.

I was delighted to learn that type-checking was possible in Python at runtime. Better yet, you can activate it selectively and it claims to be O(1) and near real time.

Wait a sec, how is that possible with what might be complex types?

It turns out the answer involves guessing.

I built a toy program to demonstrate this.

Bear Roulette

Although I started by experimenting with lists of lists of lists of integers as shown in the docs, I decided to narrarrate the situation as a game of Russian roulette.

So, a Python function prepares a revolver that may or may not be loaded with one bullet.

def prepare_revolver():
    # get a gun
    revolver = [0] * 6

    # flip a coin
    if random.choice(['heads', 'tails']) == 'heads':
        # load the gun
        idx = random.randint(0, 5)
        revolver[idx] = 'bullet'

    # hand the gun back
    return revolver

There is also a function to shoot the gun, potentially harming the bear.

def shoot(revolver: list[int]) -> str:
    # spin the cylinder
    position = random.randint(0, 5)
    if revolver[position] == 'bullet':
        return 'the bear is dead'
    else:
        return 'safe for now'

But to try and protect the bear, let's use beartype to inspect the revolver at runtime.

@beartype
def inspect(revolver: list[int]) -> None:
    pass

This function may not appear to do much, yet, but it's important to the bear's life.

An experiment function stiches these together, and also listens for an exception from beartype.

def experiment() -> str:
    revolver = prepare_revolver()

    try:
        inspect(revolver)
    except roar.BeartypeCallHintParamViolation:
        return "that thing's loaded!"

    return shoot(revolver)

There are three possible return values from this experiment function: "safe for now", "that thing's loaded!", and "the bear is dead". But which will happen?

Long Live The Bear

I ran the numbers, and the bear didn't make it every time.

How did this happen?

The decoration @bearblog on the function inspect adds a runtime type check on the arguments to the function. But beartype randomly samples data to check.

For example, after the call to prepare_revolver, the contents of the gun might be [0, 0, 'bullet', 0, 0, 0]. But on the call to inspect, beartype might type check the 0 at index 4. Satisfied that at least one element of the list contains the correct type, beartype allows program execution to continue.

But now the bear has a loaded gun pointed at its head, and the call to shoot might find a 'bullet'.

Other times beartype might get lucky and check the index of the list that contains the bullet. At this point it would raise an exception called beartype.roar.BeartypeCallHintParamViolation. To parse this beast with spaces, it means beartype is roaring about a function call paramater that violates the type hint.

The experiment function catches this exception and safely returns a message about the danger with `"that thing's loaded!".

Still other times, there might be no 'bullet' in the revolver, and beartype will correctly allow execution to continue.

After tallying the numbers for 100 executions of the experiment, the bear was safe after pulling the trigger 87 times, the program found a loaded gun 5 times, and the bear died 8 times.

Usefulness

What does all of this mean for designing useful software?

I'm not sure. It's unbearably early on a Saturday morning as I'm writing this and I haven't been feeling useful these days.

But my gut tells me that randomized type-checks at runtime provide some valuable safety without bogging down execution by exhaustively checking every piece of data.

Although the lack of deterministic certainty makes me nervous, I love the clever pragmatism of a one-way random walk over the expected data structure.

So beartype can be fooled, but that doesn't mean it's foolish.

End Matter

I posted a gist with the full Bear Roulette code on GitHub.

Here is a note to my future self to find or create a tool for inserting code snippets from a source file into Markdown. It should make it convenient to update a Python file, then propagate those updates to code samples in a Markdown file describing the code's purpose.

Thanks for reading!

#game #python #random