Mathias Brandewinder on .NET, F#, VSTO and Excel development, and quantitative analysis / machine learning.
by Mathias 3. March 2013 11:01

Recently, I had a few interesting discussions on F# code readability. One argument I often hear about F# is that by virtue of its succinctness, it increases the signal-to-noise ratio. I certainly found this to be true: when the entire code fits on your screen, and you don’t have to scroll around to figure out what is going on, navigating a code base becomes significantly simpler.

Relatedly, because the F# syntax is so much lighter than C#, some of my coding habits evolved. I stick to the “one public type per file” guideline in C#, and initially did the same in F#. That didn’t last long: declaring a Record Type in F# is a one-liner, and dedicating an entire file to it seems… overkill:

type Person = { FirstName: string; LastName: string; BornOn: DateTime }

As a result, my F# solutions tend to contain less files, and each file is more “self-contained”, usually declaring a couple of types and implementing some operations involving these types in a module. Again, less navigation required: open one file, and the truth, the whole truth, and nothing but the truth is right there on your screen.

In my experience, this also makes refactoring tools much less important in F# than C#. The lack of refactoring tools in F# used to be one of my main gripes with using the language. At that point, I don’t really care that much any more, because I don’t really need them that badly. Sure, it would be nice to propagate a rename automatically – but lots of the refactoring tools I commonly use with C# deal with navigating around or moving pieces of code from file to file (extract class, method, etc…), all problems that are minor when your code sits in just a couple of files, and the “what class owns what responsibility” issue vanishes because your functions are at a module level.

Conversely, I have found myself annoyed a few times looking at F# code where succinctness erred on the side of obfuscation. This tendency for terse naming conventions seems to be a cultural heritage from other functional languages, and makes sense to an extent – functional code tends to focus on applying generic transformations to “things”, and not that much on what the “thing” might be.

As an illustration, I have seen often code along these lines:

match list with
| x::xs -> ...

No need to go full on Java on your code, but a bit of naming effort goes a long way in making code intelligible: 

match list with
| head::tail -> ...

That being said, extreme terseness can be fun, at times – I don’t think I’ll see a C# Game of Life implementation that fits in a Tweet any time soon Smile.

Another readability aspect I found interesting with F# code is that the order of declarations matters, and the order of the files in the project matters as well. This seemingly odd constraint has a good reason – it makes the awesome F# type inference work.

Over time, I actually began to appreciate this not as a constraint, but almost as a feature. One problem I keep running into when I look into a C# code base I am not familiar with is “damn! where should I start?”. There is no clear way to proceed through the code, and I have ended up countless times navigating haphazardly from class to class, hoping to stumble upon a solid starting point.

I don’t really have that problem with F# code bases – essentially, I either start from the first line of the first file, and read forward, or the last line of the last file, working my way back. Either way works; the first one reads like a constructive proof, walking you every step to the ineluctable conclusion, the second one starts with the “high point” of the code base, digging progressively into the nitty-gritty and assumptions that were made to get there.

Someone reacted by saying that I was “just rationalizing”. There is probably some truth in that – but I believe there is something to be said for having a natural reading order in a code base. As a side-note, this is also one of my minor annoyances with GitHub: in the browser, the files from an F# repository are displayed in alphabetical order, which loses the logical project organization.

I might also change my tune when I have to deal with a truly large F# code base, which hasn’t happened to me yet. At that point, I may long for more freedom in organizing my code, and, say, arrange it by topical folders. For the moment, though, this hasn’t been an issue for me!

Comments

3/4/2013 3:50:43 AM #

Narkado

I don't agree with your example. In a pattern, an expression such as x::xs always means that x is the head, and xs is the tail of the list. Naming them head or tail is precisely the kind of noise you would like to reduce (it is nothing but redundant).

By saying x and xs, you clearly state that x can be anything, that you don't know its proper nature, and that your function is generic. But if you do know something about it, such as its type or the kind of data it represents, clearly you should name it accordingly — not head, but rather person, animal, etc.

Narkado France | Reply

3/4/2013 6:54:53 AM #

Vesa Marttila

Agreed. In Clojure at least using x and xs is so common that they are easily understood whereas something like head and tail are not.

Vesa Marttila Finland | Reply

3/4/2013 8:08:26 AM #

7sharp9

I suppose who is the audience of the code or code snippet.  If they are functional developers then x and xs will be obvious, whereas if you are presenting to a potential audience it isn't.  

There is a fine line between terseness and readability as long as you are aware of it and don't turn writing code into a minimum line and character count competition at the expense of readability then everything will be fine Smile

7sharp9 United Kingdom | Reply

3/4/2013 8:39:08 AM #

Mathias

My point, exactly Smile

Mathias United States | Reply

3/5/2013 10:10:40 AM #

pingback

Pingback from scoop.it

On F# code readability | Functional programming | Scoop.it

scoop.it | Reply

3/10/2013 8:04:33 AM #

pingback

Pingback from sergeytihon.wordpress.com

F# Weekly #10, 2013 | Sergey Tihon's Blog

sergeytihon.wordpress.com | Reply

6/15/2013 7:32:17 AM #

Andrei

I think head::tail conveys nothing more than x::xs, except for people who have never seen F# in their life, but you shouldn't write code to an audience that is completely novice in the language. A novice learns about lists quite quickly and from them on they will know what x::xs means.

If it were point::listofpoints it's another thing though, but in generic code you can't name the elements.  

Andrei Brazil | Reply

7/6/2013 8:57:37 AM #

Mathias

I actually totally agree with your point, and it's a great clarification. I received a surprising (to me) amount of flak for that opinion, and what you stated is close to my current thinking. If you know what the head and tail are (like in your point::listofpoints example) it doesn't hurt to provide some hints with names. If the function is totally generic, I am now slowly leaning to the x::xs notation; names don't clarify much, and conciseness is good. I guess my broader point is that the OO idea of "good descriptive names" shouldn't entirely be discarded, there is a trade-off between conciseness and naming hints Smile

Mathias United States | Reply

Add comment




  Country flag

biuquote
  • Comment
  • Preview
Loading



Comments

Comment RSS