Hacker News new | ask | show | jobs
by dchichkov 4823 days ago
Please use more readable names in your code. Use of names like 'fs' in key places makes it unreadable.
2 comments

Generally this is of course good advice, but in Haskell it is common practice to use very short names (e.g. x, x', xs, ...) if the context is clear (which it usually is due to small scope, clear function names, type signature, etc.). This makes code much more concise and readable (it also makes it look very "mathematical").
> (it also makes it look very "mathematical")

Has that ever been an advantage?

For people that like math, sure, why not. When implementing mathematical concepts, if you squint at Haskell code you can see the original formulas, which should make it easier for people used to this way of thinking.

EDIT:

I'm not implying it's useful just for programming "math stuff", after all, everything can be reduced to a mathematical problem - including game engines[1], web application frameworks[2], etc.

[1] http://www.cse.unsw.edu.au/~pls/thesis/munc-thesis.pdf [2] https://github.com/yesodweb/yesod

And it's probably one of the most significant things limiting adoption of Haskell.
Exactly. From my point of view, Haskell is the perfect language which unfortunately comes with the worst naming conventions. (I generally develop in C#, F# and JavaScript)
The naming conventions are fine, but they're very different from C#, F# or JS - it's basically the difference between reading English text and reading formulas (i.e. compositions of weird letters and symbols).
Indeed.
The convention for Haskell is to keep the active scope of variables very small. Any variable with an active scope of larger than maybe 3 lines, I make longer. Since these examples were hardly longer than that, I feel quite justified with short names.

In Haskell, if you see a short name, look up and down 3 lines for the definition. If you can't find it then complain.

I think you are right, and it is a convention in the functional world. People are using (and worse, reusing in a close proximity!) meaningless names like that. And I think these people have zero regard to anyone who is reading their code.

Well... more power to python, and culture that embraces 'what your see is what your get' and super-readable code.

There is actually an interesting technical reason for having short names in generic Haskell functions. Because of parametricity, the behavior of the function doesn't depend on what the values actually are. The shortness of the names really is meant to convey "don't think about what this is doing, because it's not important for this function". In the traditional example for map,

  map f [] = []
  map f (x:xs) = f x : map f xs
You're supposed to infer from the short function names that f and x could be anything. The only important bit is that you can apply one argument to f (so, for example, f could take two parameters, and then map is just doing a single partial application). In that context, x and xs is actually a better convention than "first" and "rest", because they indicate the adherence to the type system. The naming here is saying that x is of the type of elements of xs, and that this is the only important information for map. This seriously helps in more complicated functions like zip, etc.
I'm not so sure that briefness and adherence to that convention improves readability. Of course f, x, xs is much much better than 'first' and 'rest', or 'a', 'b', 'c', but something like 'func' and 'iterable' gives more context. And frees one's attention to more important things, than looking up and down the code.

Compare:

    map f [] = []
    map f (x:xs) = f x : map f xs
With:

    map f xs = [f x | x <- xs]
Or even better, in Python:

    map = lambda func, iterable: [func(x) for x in iterable]
Which one is more readable?

First one requires looking up and down in order to understand what is going on. Second one is better, context is limited to one line. And the last one doesn't require you to remember context at all.

I find

  map f xs = [f x | x <- xs]
the most readable, but given that list comprehension is basically a map and a filter joined together, that definition is kind of cheating.

I find the python version hardest to read (even though it's also "cheating"), which is largely because both the identifiers and the control constructs are alphabetic.

  [func(x) for x in iterable]
I have to read the words to figure out that for/in are the keywords and x/iterable are identifiers. func(x) is at least pretty obviously a function application. I'm glad it's not

  [call func x for x in iterable]
If you compare this to

  [f x | x <- iterable]
The parentheses-less function application might take some getting used to, but then it's pretty easy in my opinion. The | nicely divides it into two parts. A function application on the left, and a "take each element x out of iterable" on the right.

The only other thing is that because "iterable" is such a long word I expect it to be an identifier imported from a library or somewhere else in the program, and certainly not earlier in the line.

In summary, I think a lot of what we find "readable" depends on what we are used to.

>> In summary, I think a lot of what we find "readable" depends on what we are used to.

I think a general rule is that source code readability is inverse to the amount of context information that one needs to remember in order to interpret and understand the code.

If you can look at the code at any place (and at any scale, ranging from a single line to the whole call graph), and without any context understand what is happening at this particular place - code is readable. And on the opposite, if you need to know a lot about the context of each particular line of code - code is unreadable.

We'll have to agree to disagree. I think long variable names for short-lived variables decreases readability. Oftentimes these "points" are just used to glue functional pipelines together and have little-to-no intrinsic meaning. The true documentation comes from the types and is thus more trustworthy.
Oftentimes there is ML code in which both, types are implied and variable names are typical to functional programming (a,b,c,d,e) style.

In C or C++ this newer was the case, because type information was never implied (until recently, when auto was introduced). And in dynamic languages, like Python, this is also almost never the case, because good mainstream developers use object names consistently.

In the functional world however, mainstream (if there is any mainstream, as I often see each developer working in his/her own unique style) folks just say phrases like 'true documentation comes from the types' and write their recursions freely, and with no regard to the reader.

So yes. We'll have to agree to disagree.