The dot operator

One of my biases in life is to consider the value of processes in terms of the outcome. For me, a process that produces good outcomes is a good process, but lots of people dislike aspects of the processes themselves and will decry processes that produce good results. If I am using the term correctly, I consider myself a consequentialist.

I am prepared to entertain the idea that there are some fundamental, categorical reasons why some things might be true. There are people who argue that objects and methods are bad due to the mixing of two wildly different things. It’s true that you can think of data and processes upon data as different things, but it doesn’t seem obvious to me that this a reason not to mix them.

Chronic disagreements in philosophy

To start, I want to define what is good and what is bad in terms of programming. If you can accept that definition then read on and I will try to show why I think mixing data and processes in the same components pushes you away from doing desirable things and pushes you towards doing less desirable things. And if you can accept that certain outcomes are superior and some are inferior then you just need to come around to a consequentialist way of thinking to agree with me, although I’m going to skim over the sales pitch for that here – while I would be happy to settle chronic disagreements in philosophy for reasons of space I’ll do it another day.

Do not repeat yourself

Do not repeat yourself (henceforth: DRY), is widely considered to be good advice for the programmer. Repeated code is harder to maintain and more prone to errors. We can argue about readability another time. In a long standing project I have worked on, there was a common thing that coders wanted to do but it wasn’t obvious or easy so a “recipe” for doing it was followed and multiplied around the codebase, years later it dawned upon us that in fact the popular “recipe” was flawed but at this point many different variations of the “recipe” lurked in dark corners and sorting this out was not trivial or quick. This is the risk you run if you do not follow DRY. Applying a consequentialist assessment of DRY leads me to think that DRY is good advice, it’s also widely considered to be good advice which probably means less but it’s not a controversial opinion at all and one I expect you as a reader to comfortably agree with.

Recently, I wanted to sort some data for presenting to users. Users don’t want to stumble over unsorted data and computers can sort data very quickly. The items of data had a number of levels to them and as the user clicked through the “tree” I wanted to put the folders at the top and the files at the bottom while keeping the folders and the files in order relative to each other of their kind.

Conceptual friction

I was using an unapologetically Object-Oriented language. So convinced of the wholesomeness of Object-Orientation were the designers of this language that it was not possible to create anything that didn’t live in a class. This was annoying because all I wanted to do was create a function that compared thing A with thing B. The all seeing eye of the language designer has seen all possible uses of the language and decided that I need to enclose everything in a class, even such simple and fundamental tasks as comparing two items of data. I probably called the class ThingComparer as a nod to the fact that it was purely procedural and contained no data. If you’ve written any code in a large OOP codebase there will be lots of names running along similar lines giving off a slight smell of conceptual friction. The method within the class looked like this:

1
2
3
4
5
6
7
8
9
10
11
class ThingComparer : IComparer<Thing>
{
public int Compare(Thing a, Thing b) {
bool aIsFolder = Type == Thing.FOLDER;
bool bIsFolder = b.Type == Thing.FOLDER;

return aIsFolder && !bIsFolder ? -1 :
!aIsFolder && bIsFolder ? 1 :
a.Value.Project.Account.Name.CompareTo(b.Value.Project.Account.Name);
}
}

I have exaggerated what I needed to write for dramatic effect but do you see the repetition here? Getting the Name property is done multiple times and it’s a common process that I shouldn’t want or need to repeat. The problem here is there is no way to separate the data in a from the process of accessing it, really I want to be able to specify this process without referencing a particular instance of data, the process is rather obviously not exclusively related to the specific instances of a or b. However, the result of mixing data and process leaves me in a situation where I have repeated myself. I want to move the .Value.Project.Account.Name process away from particular instances of data but I am unable to do it without fighting against the language.

Async aside

Aside: Asynchronous programming in this language demonstrates the point just as clearly. For boring reasons that need not detain us here, to get your asynchronous code to perform better you need to configure its behaviour away from the default behaviour chosen by the all seeing language designers. This is an example copied off the net:

1
var customer = await GetCustomerAsync(id).ConfigureAwait(false);

Again the language obstructs you from removing the process of .ConfigureAwait(false) away from particular instances of the data. I recently looked through a very small codebase at my work and found that in one small project this code fragment .ConfigureAwait(false) was repeated over 300 times! Who knows how many times the programmers on this project forgot to include this magic suffix? If the API changes or another, superior, library for asynchronous programming appears, then adopting it will require changes in 300 places rather than one.

This is why, from my consequentialist vantage point I think that mixing data and processes into the same components as dictated by OOP is a mistake. If you accept that DRY is a fundamental principle of programming and then it follows from this example that OOP pushes you away from what you should be doing.

Leaving the realm of the dot

You can do what I did and write an anonymous function that takes a Thing and returns the .Value.Project.Account.Name of it, which is to effectively write in a functional style. Writing functions allows me to stick to the principle of DRY, writing functions is good because it produces good outcomes (DRY) and writing OOP pushes me to repeat myself.

Opinions on OOP vs FP from across the intelligence spectrum