DEV Community

Cover image for Confessions from a Golfer Programmer
Ardi
Ardi

Posted on • Updated on

Confessions from a Golfer Programmer

I love code. Not all code, just beautiful code.

One of my favorite activities is to refactor - transmuting a program that works into another that does exactly the same thing, but better: Making it run faster, changing it so that it's shorter or more idiomatic, whatever that means; removing some nasty duplication, etc.
It makes me happy in the inside to see commits that have more removals than additions, which is probably the opposite of what the product manager likes to see.

This is not a good quality to have, however. It's easy for me to get engrossed in the beauty of my own - or someone else's - code, and not get anything done.

"Look!" I conceitedly exclaimed. "I've reduced the size of the function to one third its size"
"Nice, have you added the feature already? we really need it" My coworker inquired
"On it..." Realizing that I spent half an hour and it's still unstarted.


I come from javascript, where every time you try to do something minimally declarative your javascript engine dies a bit on the inside, and the garbage collector wishes you very bad things.

Let's say that our backend has a function that requests IDs. These IDs can be empty (""), and we want to ignore those. It gets the users from a dictionary in memory, and then returns the ones that it has found that are validated.

function getValidatedUsers(userIDs: string[] ): Users[];
Enter fullscreen mode Exit fullscreen mode

There are two ways to achieve this.

The imperative way:

function getValidatedUsers(userIDs: string[]): Users[] {
    const ret = [];
    for i in userIDs {
        if (i === "") continue;
        const user = UsersDictionary[i];
        if (user !== undefined && user.isValidated()) {
            ret.push(user);
        }
    } 
}
Enter fullscreen mode Exit fullscreen mode

The declarative way:

function getValidatedUsers(userIDs: string[]): Users[] {
    return userIDs
        .filter(x => x !== "")
        .map(x => UsersDictionary[x])
        .filter(x => x !== undefined)
        .filter(x => x.isValidated());
}

// Which could even be written more 
// concisely with arrow function syntax.
Enter fullscreen mode Exit fullscreen mode

To me the declarative way more beautiful and clear, the steps look simple and ordered...

...But you would be a fool to use it.

The engine copies the array 4 times which involves a heap allocation, a memory copy and later on a garbage collection pause that is guaranteed to happen at the worst possible time, all of the time. Not to mention the closures generated too. You won't see me writting javascript that looks like that in code that needs to be even minimally performant, especially not in node, which is single threaded and your pretty little declarative function is slowing down the rest of the server.


I love Rust (I promise this is not another blog of Rust propaganda), which probably doesn't come as a surprise given that I'm your stereotypical nerd. Rust is great, but it's also my greatest demise.

My first contact with the expresiveness of Rust blew me away. You can use the declarative way, you can use beautiful declarative code, all without it slowing anything down (it even has the potential to get optimized into being faster!).

Rust is famous for its error handling. It does not support exceptions, instead opting for returning the errors to the caller. The first time I heard of it I thought that it was a step backwards, a return to C and its error code returning. But I was wrong, rust has exceptional tools for working with errors.

Here's an example from selenium-manager, maintained by the selenium project and a contribution I made

This calls three functions that can fail: set_timeout, set_proxy and resolve_driver

If any of them fails the program exits with a return code.

match selenium_manager.set_timeout(cli.timeout) {
    Ok(_) => {}
    Err(err) => {
        selenium_manager.get_logger().error(err);
        flush_and_exit(DATAERR, selenium_manager.get_logger());
    }
}
match selenium_manager.set_proxy(cli.proxy()) {
    Ok(_) => {}
    Err(err) => {
        selenium_manager.get_logger().error(err);
        flush_and_exit(DATAERR, selenium_manager.get_logger());
    }
}

match selenium_manager.resolve_driver() {
    Ok(driver_path) => {
        selenium_manager
            .get_logger()
            .info(driver_path.display().to_string());
        flush_and_exit(0, selenium_manager.get_logger());
    }
    Err(err) => {
        selenium_manager.get_logger().error(err);
        flush_and_exit(DATAERR, selenium_manager.get_logger());
    }
};
Enter fullscreen mode Exit fullscreen mode

A programmer playing golf

This is where my golfer self comes in, I want to make this code as concise as I can.

First, let's use if let Err, which could translate to javascript's if (my_fn() instanceof Error) and extract the error to a function

let err_out = |e| {
    selenium_manager.get_logger().error(e);
    flush_and_exit(DATAERR, selenium_manager.get_logger());
};
if let Err(err) = selenium_manager.set_timeout(cli.timeout) {
    err_out(err)
}
if let Err(err) = selenium_manager.set_proxy(cli.proxy) {
    err_out(err)
}

match selenium_manager.resolve_driver() {
    Ok(path) => {
        selenium_manager
            .get_logger()
            .info(path)
        flush_and_exit(0, selenium_manager.get_logger());
    }
    Err(err) => {
        err_out(err)
    }
};
Enter fullscreen mode Exit fullscreen mode

But Rust has so much more to offer. Notice that there's a happy path (continue) and a gutter, where all of the errors go.

Image description

This is a common pattern, and Rust has really nice tools for working with these in a declarative way. We can use all of the expressiveness of rust to golf this down

selenium_manager
    .set_timeout(cli.timeout)
    .and_then(|_| selenium_manager.set_proxy(cli.proxy))
    .and_then(|_| selenium_manager.resolve_driver()
    .and_then(|path| { // If all of them succeed
        let log = selenium_manager.get_logger();
        log.info(path);
        flush_and_exit(OK, &log);
    })
    .unwrap_or_else(|err| { // If any of them fail
        let log = selenium_manager.get_logger();
        log.error(err);
        flush_and_exit(DATAERR, &log);
    });
Enter fullscreen mode Exit fullscreen mode

This is reminiscent of the code we saw earlier in javascript, this time with errors, which in my eyes makes all of it much easier to read and understand, and most importantly: More beautiful. The only metric that matters.

All of these changes are cool and all, but they provide no value. The end user doesn't care, the compiler doesn't care either (they're equivalent), but I care. I'm in for the aesthetics, but don't tell my boss.

When I have to make functional changes they break the perfect aesthetic, but given the time to refactor it will become something even more beautiful than before, complexity that has been tamed into a coherent piece of machinery.

With no time given to making it look beautiful it will become an ugly mess, but an ugly mess that works. There's value in that.


One time when I read the code of a junior coworker of mine, I physically shivered, horrified at some of the things I saw, and I felt an urge to fix it (this is one example of such):

public isDebug(): boolean {
    if (window.localStorage.getItem("debug") == "true") {
        return true
    }
    else {
        return false
    }
}
Enter fullscreen mode Exit fullscreen mode

I'm not kidding, that's real code. I emotionally had the need to rewrite it to

public isDebug(): boolean {
    return localStorage.getItem("debug") === "true";
}
Enter fullscreen mode Exit fullscreen mode

I doesn't make me happy that they save it to localStorage, that's part of a bigger bodge that I don't even want to look at.

But that was a part of the code that didn't affect me in any way, I just tumbled with it and couldn't look away.
It might not be pretty but their code worked, and at the end of the day that's what matters. I have so much to learn from them :)


That was it, I hope you liked my first post.
Remember when I said I wasn't going to make Rust propaganda? I kind of lied a bit there :)

Top comments (1)

Collapse
 
lexlohr profile image
Alex Lohr

You can find a middle ground between being declarative and efficient (just to take your first code block as example):

function getUsers(userIDs: string[]): Users[] {
    return userIDs.reduce((validUsers, userId) => {
         const user = UsersDictionary[userId]
         if (user) validUsers.push(user)
         return validUsers
    }, []);
}
Enter fullscreen mode Exit fullscreen mode