다음을 통해 공유


Quote of the Week: "What can C# do that F# cannot?"

The F# community quote of the week was from Tomas Petricek in answer to a question on Twitter, see the pic on the right. 

What Tomas says is not 100% technically accurate: you can get NullReferenceException (NRE) in F# if you use C# libraries. C#-defined-types+the "null" literal, or some backdoors like Unchecked.defaultof<_>.

However what Tomas says does match people’s actual experience with the two languages, and it is certainly true that NREs are absent from routine F#-only coding.

For example, this comparison records that one C# project had 3036 explicit null checks, where a functionally similar F# project had 27, a reduction of 112x in the total number of null checks.  The other statistics in the comparison shown are also compelling, particularly the “defects since go live”. The two statistics are not unrelated.

After a recent F# meetup I grilled one of the guys who wrote this comparison to learn more about it. I can confirm that they are not making these stats up: these projects are both ETL (Extract, Transform, Load) systems which are broadly speaking in the same zone in terms of functionality, or if anything the F# implements more features. The F# project really did have a very, very low bug rate. The size difference is not due only to language differences; there are also differences in design methodology, with the C# characterized by the overuse of OO design patterns that is often seen in C# and Java projects.  Common symptoms include the presence of elaborate, unnecessary and buggy "internal design pattern frameworks" inside the OO code.  If you want to learn more about Functional-First design, the guys involved are members of NOOO (Not Only OO).

Sometimes it bewilders me that a massive reduction in explicit null reference checks in comparable programs doesn't shake the computing industry a little more. When other industries see such productivity gains, change happens rapidly. But regardless of the social dynamics, I strongly believe that for the sake of our children we need to abandon the hell that is trying to write robust software in a language where nulls pervade the code like a disease. Nulls were famously called the billion dollar mistake - but they probably cost the industry $1B every year by now, and rising. They are also a drain on human energy and talent. Luckily for those who get to use it, F# offers one way through this.

Unfortunately a sort of stasis, or lack-of-will seems to be gripping the industry at the moment. People stick with their existing languages, though occasionally make noises about needing non-null types (thus making C# and Java even more complex, when we should be ridding ourselves of this curse altogether). Even worse, language designers duly trot out new typed language designs (e.g. Dart, TypeScript, and even Scala) which allow nulls everywhere, for all reference types. That said, I know there are reasons these languages have nulls, given their design constraints, and some languages offer constructs which help make nulls abnormal, and that is some progress. But that still does not satisfy me - we didn't take the "easy" path of pervasive nulls for F#, despite living in the world of .NET. And F# users reap pervasive benefits from the simplicity this brings.

In practice F# (and OCaml, and a few others) shows that highly practical programming and software design is possible in a language where nulls do not pervade regular code. I'd encourage people to think hard about the huge time savings that came with that 112x reduction. And for those interested in language design, look carefully at the F# features which collectively eliminate nulls in routine programming (many come from ML, but not all) . Around the edges we make some compromises. But for the industry the hell of nulls should be diminishing, not increasing. People should be paying attention, and language designers should be held to account.  Is the future full of nulls, or not? Can I trust a value to be what it says it is, or might it not be there at all?

Don

Comments

  • Anonymous
    March 24, 2013
    Not only does Scala have null, it elevates the concept to the position of a special sub-class of all reference types, scala.Null -- so you can pose tests for null values as tests for the Null type instead; but then you can do as much in C#, relying on null values having no type and filtering with Enumerable.OfType<>(). I suspect that the pedigree of the languages has influenced their design, with Scala living in the world of the null-happy standard Java APIs, whereas the ML family didn't bring that baggage into F#.

  • Anonymous
    March 25, 2013
    Speaking as a Scala user - all that stuff is for Java interop.  Normal Scala uses Option[T] instead of null, and you'll sees the same lack of null checks that you would see in F#.

  • Anonymous
    March 25, 2013
    Let's not forget that one can design nulls out of a code base regardless of language, and without fancy new types. en.wikipedia.org/.../Null_Object_pattern

  • Anonymous
    March 25, 2013
    .Net should definitely add non nullable reference types. It might be harder to do now than it could have been in V1 but the effort will definitely pay of in the long run in my opinion.

  • Anonymous
    March 25, 2013
    Hi curt, can you explain what you mean be "fancy new types"? Cheers, Steffen

  • Anonymous
    March 25, 2013
    @Curt : this seems a way to work around the possibility of null. I think the point being made here is not to work around null, but to have null to exist at the type level, so that the compiler can enforce the validity of the operation done. You might like the object null pattern, but how can I, as a user of your lib, know about that. Equally, you have no way of knowing if other libs have support or not. In a small walled garden, with a lot of wrapping effort, this pattern is useful, although painful. But industry wide proof, that does not work, as can be seen by the numerous nullRefs, even in very widespread application that have seen extensive debugging.

  • Anonymous
    March 26, 2013
    I just spent 2 hours because of a system that was trying to be smart and allowed null values to be inputed. It of course did not warn me of my mistake, that was... somewhere..... 1 billion a year ? that's probably underestimating it

  • Anonymous
    March 26, 2013
    NRE = Null Reference Exception for the other people wondering. Please always add the definitions for acronyms, no matter how obvious they are perceived in the actual context.

  • Anonymous
    March 27, 2013
    The comment has been removed

  • Anonymous
    March 27, 2013
    A very interesting article.

  • Anonymous
    March 27, 2013
    NRE isn't an acronym.

  • Anonymous
    April 03, 2013
    Over a year ago, I suggested adding non-nullable reference types to C# (see visualstudio.uservoice.com/.../2320188-add-non-nullable-reference-types-in-c-). The suggestion has since become one of the top voted-for C#-related items. I have now commented my own suggestion, indicating that a different language design -- forbidding null by default -- would be even better, and that F# sets a good example. Unfortunately, it is not very realistic that such a fundamental design change will be introduced to a long-established mainstream language such as C#.

  • Anonymous
    April 06, 2013
    Shameless plug: trigged by this discussion I wrote "A tale of nulls" www.navision-blog.de/.../a-tale-of-nulls

  • Anonymous
    March 31, 2014
    The comment has been removed

  • Anonymous
    April 29, 2014
    It has baffled me for so long that not only do NULL's exist in C# (by default) that there are different kinds of nulls.  if(s != null) { // yay!, i might work }   if(ISDBNull(s) == false) { // yay another null check }