S09E04: Functional programming

As I said last week, I’m trying out the functional programming paradigm for this year’s advent of code. It’s a really enjoyable whole-brain workout, and I feel like I’m finally properly gripping recursion, iterators, and even decorators. I am becoming more and more confident as a Python dev every day.

This is not supposed to be happening

I’m still crunching forward with capability-based pay. There’s some change happening around me, which is making things a little slippery. I’m taking on more of the hands-on work to keep things moving, but I can’t shake the feeling that it’s not the best way to spend my day – but then I feel like a snob.

I’m not yet doing anything that feels stretching – at least in the scope of my work. It’s currently just a lot of meetings, a lot of getting to know people, a lot of “watering the roots” with little sign of any leaves. It’s so nice to be somewhere where we have this kind of time, and I should lean into it: my last couple of roles have meant I’ve generally met people for the first time in the middle of a crisis. But at the same time I am eager for a bit more pace: so, naturally, I’m going to pick up a couple more things. I had a fun meeting this afternoon where I my boss and I went through some outcomes that need to be achieved, so I’m looking forward to starting Monday by kicking off some new work.

Speaking of the framework, though: someone messaged me out of the blue having read my weeknotes and offered to help. Which was astonishing and completely joyful, and made my entire week. I hope you’re reading this week because I’m deeply grateful.

Christmas is well on its way. I do not feel Christmassy. I’m a bit of a Grinch generally, but this year I’m feeling even more of a humbug. It’s bringing home to me how much I rely on social and environmental cues to know what to feel: I don’t feel excited by Christmas, or even like it’s actually on its way, because I’ve not had any kind of prompt.

Even Advent of Code – more of which later – is not putting me in the Christmas spirit. As I said in the intro, it’s definitely sharpening my Python skills. At this point I think I’d comfortably put myself in the top 10% of Python developers in the public sector, but that’s not what I’m supposed to be doing in this role. It’s still completely, fantastically joyful though.

The other joyful thing about Advent of Code is that it’s only tangentially about code. It’s also about algorithmic thinking: for example, there’s this optimisation in my day 09 code that Maria came up with. These puzzles are as much about figuring out the trick as they are about writing code.

Iterators are very strange things. They’re a sort of…magic black box. You can give the magic box a kick with a method called next, and it will pop out the next thing in the box. You can’t go backwards, and unless you caught the thing it just spat out you’ve lost it forever. Finally it will run out of things, at which point it will explode if you kick it again. How do you know if it’s run out?

  1. You count all the things going in and count all the things that came out. Tricky, particularly if the magic box does magic things and turns two things into one thing, or four things, or one billion things
  2. Kick it again

Iterators are a really interesting brain-breaking problem, because they model time. Time is streaming past you all the time, a second every second, and you can’t go backwards. And eventually it will very suddenly, and with no warning, stop.

Sometimes when you have an iterator the best thing to do is recurse. For example, here is a recursive function. You give it y-values on a bell curve and it works out when to stop:

def minimum_of_curve(current_total: int, totals: Iterator[int]) -> int:
    next_total = next(totals)
    if next_total > current_total:
        return current_total
        return minimum_of_curve(next_total, totals)

It takes our iterator as an input and gives it a kick. If that value is higher than current_total, then the curve has started to go upwards. Otherwise, we run the method again: but this time next_total will be used for current_total, and the iterator totals will be one shorter.

It is the neatest and most beautiful thing I’ve ever written. Probably.

The last thing I’m excited to have written this week is a decorator. This is a bit tricky to explain, so you’re welcome to depart here if you’d like.

A decorator looks like this:

def counter_wrapper(func: Callable[[], Iterator[]]):
    def wrapper():
        iterable = func()
        for i, value in enumerate(iterable):
            print(f"{func.__name__} yields the {i}th value: {value}")
            yield value
    return wrapper

Okay, so in Python functions are objects. We can pass them around, even before they’re called. In this case I’ve written a function that takes another function as an argument. Inside that function, I define another function, which has access to the function that was passed to the first function…

Let’s start again. I made a machine that produces, I don’t know. Slippers. I want to look inside that machine, and I could do it by writing some extra code inside the machine. But that’s messy, and besides, the machine is made up of other machines. And so on.

Instead, I can make a new machine that can x-ray my slipper-machine and report on it, without stopping it working in any way. This is extremely helpful for quick and dirty debugging, but also for adding functionality to things or extending existing code.

Alright, enough code. That’ll do for this week.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s