I recently rewrote a script to pull NBA statistical data from a Google Sheet and to calculate fantasy NBA rankings in three different languages: Python, JavaScript, and Go. I plan to do quite a bit more work with NBA data in the future, and wanted to see if I strongly preferred one over the others (or any at all) before embarking on bigger projects.
I’ve created a GitHub repo here. There’s a different branch named for each language.
I chose Python, JavaScript, and Go because I had some prior experience with each one, each seemed “in demand,” and I wanted the benefits that came with languages that have large communities. If I felt I had more time, I’d have included ReasonML or OCaml, Elixir, and Haskell — though, if I had a truly unlimited amount of time, I imagine I’d rewrite it in every language installable on my machine as a “grand experiment.”
I guess I was really trying to determine whether I should quickly pick up Python or Go as a secondary language to JavaScript, or just continue down the path of specialization.
The script has two major steps: 1) fetch data using the Google Sheets API and writing it to a JSON file, and 2) reading the JSON file, calculating fantasy NBA rankings, and writing the result to a second JSON file. I did not force myself write the exact same script in each language. I tried to let the implementation flow naturally from what I felt like I should do in that language and environment.
I think there are a number of flaws with the algorithm I currently use to create the rankings. I plan to address this in future blog posts and updates.
For the sake of “accuracy,” I’d note that I completed some version of these rankings in Node and Python prior to packaging them into this repo and writing the Go version. So, these weren’t really created back-to-back-to-back.
My experiences with Python prior to writing this script mostly consisted of completing various online courses, though I’ve completed more than a few. I went through a beginner and intermediate Treehouse track, watched a Treehouse course on web scraping, went through some lessons on Flask from Treehouse, and went through a course on Lynda.com for building an API with Flask. While typing this out, I felt embarrassed by the amount of courses I went through before doing anything independently with Python. While subsequently editing this paragraph, I still feel embarrassed. Maybe foolish is a better word than embarrassed.
I feel compelled to learn Python because I’m interested in getting into data analysis and machine learning. Also, whenever I visit weworkremotely.com, I see a lot of Python job listings.
But I don’t really like Python per se, or at least it’s one of my least favorite languages. I feel neutral to slightly-more-hostile-than-neutral about Python. If I used it regularly, I imagine that I’d come to terms with my grievances and find other things I really like about it.
I found the Python code that I wrote to be the least readable. Two features of the language that are supposed to make it beginner friendly and enhance readability, I find, do the opposite: 1) the lack of variable declarations and 2) Python’s use of whitespace. I suppose it could be that I’ve trained myself to read code in a certain way due to my background in JavaScript, but I find variable declaration keywords, such as let
or const
in JavaScript or var
or :=
in Go, quite helpful for mentally interpreting code. I heavily rely on variable declarations to glean intent and to understand the scope of a variable — what it’s meant to do and how long I have to care about it.
I prefer brackets over whitespace both for editing and reading code. I use Prettier in JavaScript, and have noticed that if I’m restructuring JavaScript code that would change my indentation, I tend to just make sure the opening and closing brackets are there and “hit save” to let everything snap into place. While writing Python, it felt a bit tedious and confusing to go back and make sure each line was indented correctly. I also found it more difficult to understand the scope of what I was reading when I had to compare whitespace versus looking for brackets.
JavaScript is “my language.” I’ve worked extensively in JavaScript for three to four years.
I like JavaScript. I can probably do anything web related faster in Node than in any other server-side language I know (PHP, Python, Go), or at least I’d feel more comfortable in Node.
JavaScript is the only language in which I used a utility library. I installed and started using Ramda before even considering whether this experiment would be better off without any dependencies.
On code readability: I used patterns that I’m comfortable with, but much of my code would require the reader to also know those patterns and/or language syntax to quickly see where the code was headed. However, given that you’re familiar with JavaScript and “functional light” programming, I feel like it’s easier to understand what’s going on in the node
branch than the python
branch, in part because the code is terser. It reads more like short-hand instructions or a diagram, and less like a narrative in English.
My JavaScript branch is the only one were I packaged data structures to make the overall application flow easier to follow. I added each function that was used to calculate the rankings to a higher-order, pipeline function that accepted one parameter and spit out the final object to be written to a JSON file. To follow the calculations, you just need to walk through the order of the pipeline. To accomplish this, I made sure each function accepted one parameter and returned the data needed for the next function. I packaged some data together under a single object that maybe didn’t naturally fit together.
I’ve previously written database migration scripts and API endpoints in Go, but wasn’t given the time to really learn the language beforehand. I referred to Go by Example as needed, and just looked around my employer’s code base for similar things and mimicked them. I also spent quite a bit of time generally Googling and skimming blog posts to solve problems as I encountered them.
Similarly, before writing this script in Go, I hadn’t read any books or taken any courses on Go.
I’m intrigued by Go, and it seems like it could become a highly employable language to know quite quickly.
Go’s motto could be, “don’t be clever.” I’ve previously described Go to people as a language that brings Python’s spirit of simplicity to the speed of C. This is a misleading statement in at least a couple of ways — and I don’t really have enough experience in C or Python to making a statement like that — but it feels true. I’m also very confident that I stole that line from a colleague or some article on the internet, which makes it seem almost certainly true.
I feel like the Go version took me much longer to write than the other two, even though it was the last of three that I attempted (and thus had all the component steps down in my head). I think this was due to not being overly familiar with the type system, and with what the idiomatic solution to certain problems would be in Go. Go forced me to immediately make design decision based on the types I used, and I implicitly made some without realizing the tradeoffs until later.
I’ve found one of the interesting features of using Go as a web language is the ability to define a struct
along with defining how that struct
will be converted to and from JSON. This could look something like:
type Player struct {
Assists float64 `json:"AST,string"`
Points float64 `json:"PTS,string"`
}
So, if I had a variable player
of type Player
in Go, it would have a member Assists
that could be accessed like player.Assists
. If I converted that variable to JSON, Assists
would automatically get mapped to a key of AST
. I think similar things would generally be accomplished in Node by writing everything out directly in a callback function and just kind of winging it with temporary variables. In Go, it’s like you have a predefined contract upfront for quick conversions.
The drawback: I found myself frequently wanting to range
over a struct
, like how I might iterate over an object in JavaScript or a dictionary in Python. This couldn’t be accomplished without installing the reflection module, and the reflection module seemed to be frowned upon in the Go community. It seemed that the reflection module resulted in messy code and a slower application.
If you really don’t know the data structure’s keys or members ahead of time, you could do something like map[string]interface{}
, and be able to range
over the variable, but you’d lose the benefits of having such specific types, which I think are: a potentially faster application, a better coding environment (in terms of the IDE and compilation error checking), and the quick marshalling and unmarshalling between a Go struct
and JSON.
I frequently ended up resorting to more verbose and less clever code, which I suppose is the Go way. I’m still not sure whether I think of what I wrote as “good.” It felt and still feels very wrong to be writing things out like…
player.Points = someFunction(x)
player.Assists = someFunction(x)
player.Rebounds = someFunction(x)
…instead of iterating over all members and calling a function on each iteration.
I don’t think I really gained clarity on a specific path forward from this experiment. I mostly confirmed that I personally find Python to be aesthetically distasteful, and that I find Go to be an interesting language because it forces me to come up with different solutions than I would in Python or JavaScript.
It seems like there’s a time and a place for every language, and that no matter what decision you make when creating software, you’ll regret it in about six months if you made the wrong decision, and three years if you made the right decision.
I’ll probably just start working on an NBA API in Django tomorrow.