Hacker News new | ask | show | jobs
by MichaelGG 3749 days ago
I've been using F# for a while and it's been excellent. It's just so less frustrating than writing C# in all its verbosity. There's really no reason to not use F# other than legacy or poor management. (Some folks just don't "get it". Perhaps the same kind of people that use a 20 char variable name when 3 would do. Or that are cautious about using local type inference. I don't know. But too many people conflate verbosity with readability and get scared.)

I tried porting a small demo program, a few hundred lines, directly from C# to F#. It required only 1/20th of the type annotations. I've written web APIs in F# and many took about half the lines of code.

I particularly like the ease in which I can define local functions to reduce redundancy. In, say, C#, there's so much overhead involved that it's just not worth it.

Going back to F# I've written years ago hasn't been hard either. Since the code is so compact, it's not difficult to figure things out.

F# should be MS's flagship. While F# isn't perfect (could use more inference, traits or typeclasses, and macros), in terms of tooling, ecosystem, language features it come out near the top.

2 comments

> in terms of tooling, ecosystem

Does F# run on .NET Core yet? Last I checked .NET Core and ASP.NET Core didn't support F#, which is a really big demerit for new projects considering this is the platform Microsoft is pushing for future server side applications (as they should be).

yes, using .NET CLI ( http://dotnet.github.io/getting-started/ )

You can check https://github.com/enricosada/fsharp-dotnet-cli-samples/wiki... .

Remember to use last know good version of .NET CLI (see https://github.com/enricosada/fsharp-dotnet-cli-samples/wiki... ), until latest version is fixed

> use a 20 char variable name when 3 would do.

I generally think functional programming is a smart idea, but knock it off with the short, generic function names. We're not writing Fortran on an 80-char terminal any more. Name shit what it is, it's going to auto-complete anyway after you type 3-4 characters, so you might as well give it a name that you won't have to puzzle about later.

I'm talking more like locals. Consider:

  var newCustomers = getNewCustomers()
  foreach(var newCustomer in newCustomers) {
     newCustomer.this .that etc.
  }
Depending on the length of the surrounding function, you're better off using "xs" and "x". Or "cs" and "c". I think people trick themselves into thinking that long names somehow provide more context. At a high level, this is correct: modules, exported functions, etc. But for programming "in the small" it's just needless noise.

It's not just the hassle of typing long names out; a good editor can help there. It's the visual overhead of having so many extra pixels lit as you read things.

Short functions are good for local functions:

  var sb = new StringBuilder()
  sb.AppendLine bla
  ...
  sb.AppendLine foo // And another 5 places in the following 10 lines of code.
It's nicer to instead have: let al = sb.AppendLine al bla ...

This is especially true then the local code pattern is 2, 3, or more lines or when it contains branches.

I perfer the verbosity because it helps with eliminating ambiguity when the code changes hands or goes into "maintenance". This is especially true in weakly, or duck, typed languages.

Both have merits though.

> having so many extra pixels lit as you read things

Your brain is excellent at turning words, even long ones, into instantly recognizable shapes. That's why transposing lteters as I'm doign in this sentnce doesn't harm readability very much.

Good, easily-maintained code is not poetry, and it's not math. It's Hemingway: short, terse sentences that say only what they need to say and nothing more. They are self-contained and self-evident.

On a semi-related note, there's a study showing that non-programmers have a much easier time reading code with more white space. To adapt your example above:

  var newCustomers = getNewCustomers()
  
  foreach(var newCustomer in newCustomers)
  {
     newCustomer.this .that etc.
  }
It's much easier for your brain to pull out different symbols when there's more white space.
> short, generic function names

fit the length of the functions. Long names might be indicative of some deeper problem with coding style.

I think the reason functional programmers like short, terse names is because they need to see the structure of the code more than the individual identifiers. More verbosity makes it hard to get a picture of whats going on locally. Pattern Matching and Recursion creates structures that are easier to understand if it fits on the screen.
There's a happy middle ground there. At the call site is where it matters a lot more. If you have a function that's basically nothing but a pattern matching expression, the name of a parameter matters a lot less than the name of the input when you call that function. Same goes for what you name the result.

If you do this:

    let r = f x
Then you might be a fucker. But most good F# code I've seen is more reasonable:

    let res = getResult input 
Granted there's some exception, such as defining your own operators. F# does this itself, where "|>" is "let (|>) f g = g f". I don't think you gain much from adding verbosity to that.
In general, short names tend to be used for the most generic variables. Renaming "g" to "theFunction" in the (|>) one-liner doesn't really clear up anything. Just seeing it applied to something makes it clear enough that it's a function, etc. For something less generic the identifier can introduce the relevance of the variable better. But the thing about functional programming is that much of the code is written to be very generic, and so there's not much room to use longer names.

Also, another thing that I've noticed is that it becomes much easier to deal with short variable names in functional programming, because you know the value of the variable isn't about to change out of the blue. By restricting mutable state and side effects, it's possible to just substitute the values for variables wherever they exist. So for example,

    let r = getResult input
    let s = "abc" + r
    let t = doSomethingWith s
    printfn "%d" t
is known ahead of time to be exactly identical to

  "abc" + getResult input |> doSomethingWith |> printfn "%d"
Once you start introducing mutable state, something like that becomes a whole lot harder to read because you can never be sure if the values of those variables are changing, so you can't substitute in the declaration site.
Well, if you need to scroll to read the whole name, that's indicative of some deeper problems, alright. That's kind of what I was getting at, just that the code also needs to fit in my head. Use of verbose identifiers might be linked to verbose code and that might or might not be harder to follow. There is a sweet spot.

I am just defending that I don't know what car means mainly because I don't know LISP and I refuse to read documentation, but out of lazyness mind you.

On a related note, I often discover that trying to shorten text, sentences become a lot clearer when they are less verbose. Compare that to: On a related note, I often notice shorter sentences are clearer. Compare that to the overly terse Short Sentence Clarity!

In many ways code is similar to math, and in math they mostly use one-letter identifiers. And there is a very good reason for that. I have seen texts where authors tried to make formulas more reader-friendly by replacing letters with words, but in that case they might as well have replaced formulas with prose...
Code has similarities to math, but it's not math.

function a(b) ... requires reading the whole function definition to understand what it does.

function createNewUser(userInfo) ... tells you exactly what it is and what it does. Especially if you're dealing with pure functions, it makes reading and understanding a code base much faster (and decreases the need for documentation).

Math notation isn't as much concerned with implementation as it is the architecture.