Long Names Are Long (2016)
journal.stuffwithstuff.com
journal.stuffwithstuff.com
A common example is what happens when you have an object that should just be a function (or static in javaland):
from io import reader
reader = reader.Reader()
reader.read(file)
Oof. Just import io
io.read(file)
Similarly when you have method names that share information with the class and arguments. BufferedReader bufferedReader = new BufferedReader()
bufferedReader.readBuffer(buffer)
Now languages support stuff to reduce this, like `val`, but still! When designing apis/interfaces, consider what they'll look like to a user and don't force your end users to stutter. SmurfLib::SmurfPtr<SmurfObj> pSmurf
= new SmurfLib::SmurfObj::CreateSmurf(); from io import reader
reader = reader.Reader()
reader.read(file)
Isn't this why you can say: from io import reader as r
And then: reader = r.Reader()
reader.read(file)
?Yes, variable names serve to disambiguate. But at what level -- function? File? Component? Business process? There's no clear rule here. And are they disambiguating merely between other variables that have already been defined, or from the wider scope of potential variables that a programmer might likely otherwise accidentally confuse them with?
If I have 20 different functions that all deal internally with only a single "price" variable, it may be legitimately helpful in preventing bugs to explicitly name these "priceWithTax" and "priceWithoutTax", or "priceLocalCurrency" and "priceForeignCurrency", or "priceList" and "priceSold". Or even use "priceWithTax" even if I never once use or define a "priceWithoutTax", because I know from experience that people are going to make a mistake otherwise.
In the end we're all trying to balance brevity with descriptiveness that prevents someone from accidentally misinterpreting and therefore misusing the variable when calling or modifying later on.
Sometimes I agree that yes variable names seem too long... but then I realize it may well be worth the peace of mind knowing that it's reducing chances of mistakes and bugs.
If you have 20 different functions that all internally deal with `price`, then either you're not writing OO code, or each of those functions is in its own object type, providing a clear context on what your code is talking about.
You're wrong, it's absolutely not.
It never once mentions being about objects, and the first half of examples don't involve objects at all.
I don't know where you're getting that from.
And even if it were, it has nothing to do with my point. Different objects could use different standards for a price variable. You claim that objects would provice a "clear context", but if it's not through variable names, then how? Just through comments around the variable names?
"Obviously it’s an object. Everything is an object. That’s kind of what “object-oriented” means".
So ... TFA is perhaps not explicitly, but _implicitly_ about object oriented code?
> // Bad:
> List<DateTime> holidayDateList;
> Map<Employee, Role> employeeRoleHashMap;
> // Better:
> List<DateTime> holidays;
> Map<Employee, Role> employeeRoles;
I'm not sure this is better. Yes, it's obvious that both are collections, but it's not obvious what you can do with them. Is `holidays` a map of date to information about the holiday? Is `employeeRoles` a set of all possible roles?
And once you remember that `employeeRoles` is a map, you're still a little lost about how to look into it. Is the key the employee id? Email?
Perhaps it could be called employeeToRoleMap? That distinguishes it from employeeIdToRoleMap, or possibleEmployeeRoles.
Maybe this is me disagreeing with a later statement by the author:
> Some people tend to cram everything they know about something into its name. Remember, the name is an identifier: it points you to where it’s defined. It’s not an exhaustive catalog of everything the reader could want to know about the object. The definition does that. The name just gets them there.
I don't know if I want to know everything, but I think I lean towards more than the author does. A variable name I changed today was originally called `initial-points`. It designated the set of intermediate points directly from a source to a destination, which would later be offset randomly (example: https://imgur.com/a/UxkGPfX).
While refactoring related code, I realized that although it was descriptive, it was descriptive _temporally_, rather than describing the identity of the points. I ended up changing it to `points-in-direct-line`. It still feels subpar to me, but at least it's clearer: the points exist not because they'll be changed later (initial-points), but as a direct line between the source and destination.
With employees, you might be right. The comments on the original article give two suggestions:
1. `employeesById`. This may imply a mapping type, but you aren't stuttering (saying map twice in the decl) at least.
2. Create an employee ID type and make the type declaration Map<EmployeeId, Employee>. (Where your id type might just bsubclass int) This way the semantic information is encoded into the type, and the type system prevents your from misusing things. For example you'd need to explicitly cast from int to employee ID.
This is completely true!
> The name doesn't need to tell you that, you already know and the language will prevent you from misusing it.
I think this is where we differ! I prefer to know what I can do with something without having to ask a compiler or IDE. This helps, for example, when looking at a pull request -- you don't have the code easily accessible for the compiler or IDE.
Possibly a difference is that I prefer to use languages that are more dynamically typed -- Clojure, Emacs Lisp, etc. And so you don't have `Map<EmployeeId, Employee> employeesById`; you only have the variable name.
But I do wonder -- even in the most explicitly typed, statically typed languages, what is the eser experience for finding this out? It seems that unless one is looking at the declaration, there must be at least one level of indirection to find out what the type of something is.
Say you're looking at a use of the variable `employees`. Here's the ways I can think of that would let you know what the type is:
1. Scan upwards until you find the declaration, if it even is on screen. Then look back to where the use was.
2. Move the cursor to the variable, and use the "go to definition" functionality built into the IDE. Then look at the declaration, and use the "go back" IDE function.
3. Move the cursor to the variable, and the IDE has somewhere that tells you the type. This is relatively simple, but still requires you to move to the variable, and to look somewhere else and back.
On the other hand, with a name like `employeesById`, all you need is in that thirteen characters.
I think, in accordance with "variables won't and constants aren't", I would propose the less pithy: "any invariant that is not enforced is broken". The compiler doesn't check that the capabilities or roles advertised by your variable names are actually present, so eventually they won't be. That means that you can't just check the variable name when reviewing code, must must check the declaration (as well as possibly elsewhere) in case the variable names lies; and, once you're checking that, what have you gained in reviewability?
So I disagree that you must check that `employeesById` or similar are still a map type if a PR uses it. If it wasn't a map type, I would have expected that to be flagged in the PR that introduced the variable.
Same (I mostly use python, which puts your dynamic languages to shame), so structure your code such that this is possible.
Keep your functions relatively short. Then the declaration of a type, if its a local or an argument, will be close by (usually the same screen).
Globals should be defined at the top, so those are easy too. That leaves only class members that might require spelunking. Be judicious with those, they're often more pain than is worth it.
As for your statement about employeesById not being a map should be called out in review, that's true. But over time those things can change unless enforced. You're not just relying on the initial review, but all follow up changes not breaking the invariants.
Opinion: It's (nigh) 2020. If you aren't using an editor which supports inspection and jump-to-declaration, you are doing it wrong. Similarly, if you are using a dynamic language and not leveraging type hinting or similar, you are doing it wrong. Where "doing it wrong" == doing a disservice to yourself, your coworkers, and future you.
We have so much powerful metadata available right now. So with the examples above, you should be able to focus on `employeeRoles`, hit `hotkey`, and see a gist of the types, key format, what to do with that thing, and why it's there.
Names should be unique and memorable enough that once you form that mental schema, you can quickly scan through code and understand its role in the greater machinations, without too much mental re-parsing.
I think it's relatively agreed-upon that single-character variable names are bad, because they don't tell us enough about what the variable holds. So, too, do I feel about the type, when it's not obvious from context. So `age` would be clear enough that it's numeric^1. But what about the other example of `employees`? I would not know what I can do with that without having to jump back to the definition! Naming it `employeesById` tells you what you can do with it. And that seems like a win to me.
[1] It might not differentiate between integers and floats, but that feels too nitpicky to me to put in the name.
imo, functions and variables should be named in such a way that the reader can get a pretty good idea of what's going on without jumping into a bunch of other scopes. try not to make them too long, but err on the side of too long rather than too short.
It's a risk/reward thing. Overlong names are annoying. Brief names are destructive. So I always err on the side of length.
Naming things is hard. But it is a lot easier if you can rely on it not breaking things all the time.
If I've a record with a half-dozen int properties that are semantically IDs, I have to clarify which is the key for this hashtable.
People really diss Erlang for its syntax but I find it to be extremely readable and understandable in most cases because pattern matching is a first class citizen in the language. Humans are excellent at recognizing patterns.
Same goes for the ML family.
More information is available on the net if you're curious. A cursory Duck Duck Go leads me here: https://www.pullrequest.com/blog/google-code-review-readabil...
Instead of
DockableModelessWindow window;
I might write DockableModelessWindow dmw;
if this is a local variable whose type is nearby. Using acronyms also help people remember its type.For global variables, though, it would not be appropriate.
factorial(0,F,F).
factorial(N,A,F) :-
N > 0,
A1 is N*A,
N1 is N -1,
factorial(N1,A1,F).
Because 90% of variables in 90% of the Prolog code I've ever read * are named A, B, C, D, H, I, J, K, L, M, N, P, Q, R, S, T, U, V, W, X, Y or Z. Or with an "s" (as in Xs, Ys, Zs...) to denote a list. Or a number to denote a new version (where you'd like to put a "'" but syntax doesn't let you).Theeere you are. No need to think hard.
:dusts hands:
(Tail-recursive factorial predicate from here: https://www.cpp.edu/~jrfisher/www/prolog_tutorial/2_2.html)
______________
* Including my own code that I've written before reading it.
Very cool presentation by Andrew Gerrand, with the following rule of thumb:
The greater the distance between a name's declaration and its uses, the longer the name should be.
Earth to FreeRTOS ...
Seems like if they tried hard to join all the worst things from all worlds: mix your ALLCAPS with lower case, add a pinch of CamelCase mingled with some snake_case, and top it with a confusing blend of semantic + sintactic prefix notation.
Seriously wtf were they thinking about?
EDIT: Adding the first example that I could find, which already shows all of the mentioned "features" (scroll down for the code): https://www.freertos.org/Hardware-independent-RTOS-example.h...