Disclaimer: This is a personal web page. Contents written here do not represent the position of my employer.

Saturday, March 02, 2024

 

Safety shouldn't be opt-in

Many years ago, when I was starting to have my first experiences with Functional Programming (thanks to learning the amazing F# language), I discovered what seemed to be a problem that was not really a concern in the OOP world that I was leaving behind: stack overflows caused by stack recursion.

I was puzzled: from one point of view, mutability is dangerous and has to be avoided; however, if you avoid it in a certain way by using recursion, you might hit this other kind of problem. Sure, an exception at runtime is much better than the typical problems associated with mutability (race conditions or heisenbugs, which are very hard to debug and fix), however there was no way of making sure that you don't hit this problem in your recursion-based algorithms unless you made sure that... you use TailCall-friendly recursion.

Huh? What in the world does TailCall-friendly recursion means? When I first tried to wrap my head around this concept, it was hard, and to this day it is still is. But the real problem I saw is that there was no deterministic way to make sure that your recursive algorithm that you tried to make TailCall-friendly, is indeed TailCall-friendly. That's why I thought: wouldn't it be nice that the compiler warns me when I didn't succeed in making this algorithm TailCall-friendly? And that's why I filed an enhancement request in the proper channel for that which, at the time, used to be UserVoice.

Several weeks or months later, I discovered that my enhancement request was the most upvoted of all?! Maybe because the F# team had recently moved to UserVoice and I had been lucky to be one of the first people to file a ticket, which subsequently got a lot of views? I don't know, but I was happy. And the team agreed to implement it "in principle" (as far as I remember).

Some years passed, and I started getting better and better at F#, to the point that I started my own opensource project using it. But in the back of my mind I was still concerned about this downside that I had discovered when delving for the first time in Functional Programming. So I looked for the UserVoice ticket to see if it had been finally implemented: sadly, the F# team had moved out from UserVoice to a different way of tracking enhancement requests, and therefore, all the upvotes of it had vanished, and it was no longer "the most upvoted". Also, it hadn't been marked as fixed or actioned at all. There were some people talking about it and proposing ways of doing it, but nothing had really been set in stone.

Until last December! It turns out someone had finally worked on it and contributed it upstream. I was very happy and delighted that you can finally protect yourself at compile-time from this pitfall. However, when I saw how the feature can be used, I was a bit heart-broken. The initial feature request was titled along the lines of "Emit a compiler warning when a recursive algorithm is not tail-recursive", then probably Microsoft renamed it to "Enable a compiler-warning when...". Then I thought: ah, they are going to make it opt-in at the project level, probably. Boo! They didn't make it opt-in at the project level, not even at the module level! They made it opt-in at the function level! :-(

Oh well, it's a step in the right direction for sure. But don't we all think that SAFETY SHOULDN'T BE OPT-IN? Especially as F# developers, because many of us moved away from C# because F# has safer defaults, for example: if you want to use mutability, you can still do it, but then you need to use a keyword to opt-in: `mutable` (not like in C# in which safety is opt-in, e.g. the keyword `readonly`).

So then... I decided to do something about it.

If you're a lover of static typing, you rather prefer to get compiler errors than runtime errors. And if you like compiler errors, there's a chance that you also like warnings. And if you like warnings, there's a chance that you like to turn on "warnings as errors". And if you like warnings as errors, there's a chance that you also normally like to go the extra mile: you like linters and static analysis tools. And so, if you're already using a tool like this, wouldn't you expect them to give you extra protection? And wouldn't you want that the strongest protection layers that these tools provide are enabled by default, not opt-in? Enter EnsureTailCallDiagnosticsInRecursiveFunctions!!: a new FSharpLint rule that my team has implemented which will not shut up until you have marked all your recursive functions with the `[<TailCall>]` attribute, to make sure you are properly warned when you're not protected from potential stack overflows.

I merged the PR that implemented this rule this week, and made a release, 0.24.2, so that you can already adopt it in your project, and be protected by default (because the rule is enabled by default). And so if you're using FSharpLint, safety is not opt-in anymore, it is default. And you can opt-out from safety by disabling this rule (completely, or in a case-by-case basis) in case you need it.

Labels: , ,


This page is powered by Blogger. Isn't yours?

Categories

RSS of the category Gnome
RSS of the category Mono
RSS of the category C#
RSS of the category Programming
RSS of the category Mozilla
RSS of the category Web Development
RSS of the category Security
RSS of the category Open Source
RSS of the category Engineering
RSS of the category Misc
RSS of the category Politics

Contact with me:
aaragonesNOSPAMes@gnNOSPAMome.org

Archive
My Photo
Name:
Location: Hong Kong, Hong Kong
Follow me on Twitter