> In this case consider conf or maybe c will do if the lifetime of the variable is short enough.
This seems petty. Is it really that problematic to type out a few extra characters?
> In this case consider conf or maybe c will do if the lifetime of the variable is short enough.
This seems petty. Is it really that problematic to type out a few extra characters?
Well-known abbreviations are fine, like "iter" and "prev", but single-letter variable names notoriously impede readability for me.
If the name is more than a few characters long, it starts to become non-instantaneous to recognize it. Things get much easier to follow with visually-instantly-recognizable symbols.
So in conditions where a variable is used over a short area in the code (or where it's used _constantly_ over a wide area), I prefer short variables.
If it's less "boilerplate" code, I'd go for more descriptive names. I think.
I like the sorta described "rule" in this document; the longer the variable is used, the more descriptive it should be.
id identifier
i loop counter
j inner loop counter
c general purpose non loop counter
a, b, comparator methods
k, v, key value setters, array iteration
e, event, error or exception. Contextual rule but rarely collides.
Other than those I pretty much always write entire full word names. If it's abbreviated or shorthand it likely sounds funny in my head, or it leads to ambiguation. If I need to even ask / recall for a split second it's not good enough.
Programmers incorrectly place far too much weight on time to type. I write like ten lines of code on a good day. It's just not important. I want readable code that is clear and concise as possible. Every branch, block or shorthand variable raises a question I have to think about before moving on.
Clever code is why I call ten lines of code a good day. It sure as shit isn't my typing speed.
As an extreme example, scalaz is similarly a very specialized DSL. Or in my personal experience, functional constructions like map, flatMap, foldLeft and reduce (which I never learned in school).
Up to four or five single letter variables is pretty trivial to remember. Especially when three of those are i, j, and k. More than 6 or 7 rapidly becomes painful. But if you are manipulating 6 or 7 variables _in the same scope_ you are doing to much.
I also agree with Pikes comments about typography here: http://doc.cat-v.org/bell_labs/pikestyle
Also names vary with there contextual scope. Larger scopes mean longer names generally. Russ Cox gives a succinct description here: https://research.swtch.com/names
A name's length should not exceed its information content.
For a local variable, the name i conveys as much information as
index or idx and is quicker to read. Similarly, i and j are a
better pair of names for index variables than i1 and i2
(or, worse, index1 and index2), because they are easier to
tell apart when skimming the program. Global names must convey
relatively more information, because they appear in a larger
variety of contexts. Even so, a short, precise name can say more
than a long-winded one: compare acquire and take_ownership.
Make every name tell.
Variables should generally have short scope (we don't want a lot of global, or even package level variables). So _variable names_ in particular should be short.This is only true because i is a specific, common abbreviation for index. When writing arbitrary glue code, a single letter variable would be a meaningless abbreviation without shared context. If you encounter "i" and it doesn't mean "index of a for loop", you're going to be taking additional time parsing meaning.
`usersList.stream().forEach(u -> someSet.add(u))`
It's immediately obvious that u is a user in usersList. I realize that it's debatable if u is really more readable than spelling out user, but I prefer it, and I don't think anyone is going to be confused by it. If the chain does get really long, also, I will spell it out explicitly.
For Javascript at least that would work.
`usersList.stream().forEach(someSet::add)`
Many times your function should be longer, rather than shorter.
Short methods and functions used just for the sake of being short just move the complexity of understanding in the interaction between them, making logic harder to follow an algorithm when it could have been all in the same place (for related functionality of course, I don't advocate having a function do 2 different irrelevant things).
Yeah, I noticed that for myself, too. so instead of i I might use frame_index, or whatever it "actually is". Up to a certain length it seems faster to just read what is there, without an additional mental translation step. But to be honest, I just do it because I like it.
> Functions should do one thing only. ... In addition to be easier to comprehend, smaller functions are easier to test in isolation, and now you’ve isolated the orthogonal code into its own function, its name may be all the documentation required.
Using single-caller functions as a substitute for comments makes the workings of a specific operation much harder to follow, as you have to jump around the source to understand its effects.
A long function is easier to understand than an exploded one.
Also tests should target specific operations (aka functional tests), not every single function in the program.
EDIT: Every function you add becomes part of your internal API. Any API, exported or not, should comprise a cohesive collection.
This is a pretty controversial position, and quite situational in my opinion. I absolutely agree that having to hop all over the source to understand something is frustrating, but that doesn't mean that very long functions are the right solution. Some combination of reasonably named helper methods and a function flow that makes the logic easy to parse should be the goal; either end of the spectrum is a problem.
> If a function is only called from a single place, consider inlining it.
> If a function is called from multiple places, see if it is possible to arrange for the work to be done in a single place, perhaps with flags, and inline that.
> If there are multiple versions of a function, consider making a single function with more, possibly defaulted, parameters.
> If the work is close to purely functional, with few references to global state, try to make it completely functional.
> Try to use const on both parameters and functions when the function really must be used in multiple places.
> Minimize control flow complexity and "area under ifs", favoring consistent execution paths and times over "optimally" avoiding unnecessary work.
Single-caller functions attract other callers over time, gain backwards-incompatible features, and result in regressions.
It’s a bummer that more languages don’t support them, though you can get there with lambdas too, sometimes at the cost of more syntax.
One of the (few) things I like about Javascript is the ability to define a closure anywhere in the containing function, so it appears in the order of operations:
function f() {
setTimeout(fDing, 2000);
g();
function fDing() {
...
}
}I am curious with this approach though...you're nesting behavior and/or logic, doesn't this further obscure the meaning of the code and contribute more to the need to jump around the source in order to figure it out?
The Go Programming Language book (Kernighan and Donovan) has some examples of them.
function fDing() {
...
}
Honestly with modern JS I am not sure this feature is that great. Looks more like a code smell these days imo.Edit: Formatting
You write the test (where any is needed) for the outer function.
Edit: the start of the discussion was about using nested functions to decompose what otherwise would have been “unpartitioned” long functions. Such blocks of code nested within a long function would not be possible to unit test, either.
Unit tests, rather than integration tests, are usually bogus, anyway, though.
Your employer doesn’t want you testing getters, anyway, but rather features. Unit test fanatics need to stop.
I guess unit tests were useful for C++, when it was constantly crashing everything :-(
C++ and its legacy need to ride off into the sunset, already.
Pascal, like Algol, had nested subroutines for decomposing longer operations without leaking the details.
Nested functions is one of the things I like about JavaScript as well.
For better or for worse, taking advantage of that forces you to keep source files fairly short and cohesive in functionality.
If you just call it from the enclosing function it's fine.
As long as what are doing is approximately the same type of thing, it is fine to do a lot of things without breaking readability.
The function:
func ManyThing(i int) int {
fmt.Println(i)
return i+1
}
does two things, and it's two lines long. The function tcp_send_message_locked (https://github.com/torvalds/linux/blob/master/net/ipv4/tcp.c...) does one thing at it's 261 lines long.Shorter code is _indicative of_ orthogonality (what he asks you to break on)__but_ it is not the same thing.
Well, that's extremely good, and standard, advice.
https://en.wikipedia.org/wiki/Single_responsibility_principl...
GetData()
FormatData()
UploadData()
Those could do one thing but could be called at several points within a larger program, which i think you are fine with. Or they could only be called once in which case i think you are saying it might make sense to just inline them.
If I understand you correctly, then I agree.
Though it also depends on whether I'm doing something familiar or something new, if I'm doing something new I might split things up more to help me conceptualize the problem. But when I'm just making a quick CLI tool, I might put it all in main first and only split it up as needed.
configuration := NewConfiguration()
configuration[word] = parameters[values[line][column]] - parameters[values[column][line]]
vs conf := NewConfig()
conf[w] = params[vals[i][j]] - params[vals[j][i]]
It takes your brain more time to parse the first line. Now, there is an obvious limit, this is probably too much c[w] = p[v[i][j]] - p[v[j][i]]
unless maybe the scope of the vars is very limited. conf[w] = params[vals[line][column]] - params[vals[column][line]]
or even: conf[w] = params[vals[l][c]] - params[vals[c][l]]
if you really want to save a couple of characters. Confusing which loop index is indexing which dimension is far too easy.