mccd

pub   rsa4096 2023-09-22 [SC]

2F30 1709 2782 FD05 CCCF 7964 647E 396D 1F4E AE7B

uid              Marc  Coquand  <marc@mccd.space>

comma mastodon rss

Purity is Great for Collaboration

When people talk about the benefits of purity in functional programming, the benefits that are often mentioned are equational reasoning (if the language is lazy), ease of testing, memoization and ease of parallelization. To me, these are all great benefits, but might not be enough to convince a skeptic.

What I often do find missing in these conversations is how much of a benefit purity can be to collaboration, specifically for dependencies. Purity is a contract that says: I will not do any kind of I/O; I.E. no network calls, no db calls, no file reading, no reading environment variables etc. That means that third-party dependencies won’t do any of these things, because if they try , the program won’t compile. That is in some sense a very simple portable permission model.

Today, software is probably more vulnerable than ever to supply chain attacks, especially in languages like JavaScript and Rust that have a culture of importing dependencies for every small problem they have.

As an added negative in JavaScript, auditing code for exploits and defects is practically impossible. The code uploaded to npm is often minified to reduce asset size, and it does not have to match the source code hosted on a VCS. To audit, you would therefore often have to review minified code, which is very hard to read. I have never met anyone who does that.

Some might argue that this is a cultural issue and that we need to just stop depending on so many things; a dependency should be brought in seldom and if it is brought in, then it should be audited. It could also be argued that importing lots of dependencies adds bloat to projects. I sympathize with this sentiment. Even so, the solution to “stop using so many dependencies” is incredibly hard to implement, and also gives up the productivity gains of reusing already-built resources.

In elm, a pure functional language, auditing code for security vulnerabilities is a lot easier. There are basically two places where information disclosure attacks could occur in elm code: in functions that have Html or Cmd in their signature, as those are the two signatures that allow a function to have side effects. So when you audit a dependency, you can basically Ctrl+f Cmd|Html to find the functions that do side effects, and focus your efforts on auditing those functions. The code that has those signatures tend to be extremely simple because of the culture around pure languages, so reviewing it is easy. You’d see clearly if there is any attempt to do a network call or other suspicious activity. Barring some kind of exploit, the pure functions in the dependency would not be able to send credit card details or other unwanted behavior. The only supply-chain attacks that I can think of with pure functions are creating an infinite loop, divide by zero, or somehow providing broken cryptography algorithms. There might be some crazy exploits I do not know of but it would still be significantly harder than in a non-pure language. This mitigates the risk of supply chain attacks significantly, and actually makes auditing code achievable. If you can think of more ways to exploit pure functions please let me know, I’d love to learn about it.

Finally, since pure functions can not be redefined or removed in a pure language, you can have dead code elimination on a function level to remove unused bloat from your dependencies and vastly reduce the compiled asset size. This is called dead-code elimination. The equivalent in JavaScript, tree shaking, can not reach anywhere near that level of optimization. This means that in pure languages you can potentially vastly reduce the bloat that comes with dependencies, as you only include functions needed to run your program. You can see elm’s result of asset size optimization in their blog post from 2018. And as a cherry on top, my personal experience is that even with all optimizations in place, elm still manages to compile fast, on a level similar to that of go.