How close can pure functional programming get to C?
rtfeldman.com
rtfeldman.com
C/C++ code can trivially reach into some remote bit of state and mutate it directly. The compiler rarely objects, no matter how ugly the access method or pattern. It is up to the programmer to make sure the invariants hold and the compiler can’t always see through it. These elided copies add up, though it usually has a maintainability and safety cost.
It is related to why functional languages tend to be mediocre for supercomputing in practice despite having nice properties for massive parallelism in theory.
Are there any common resources for this kind of access methods and patterns that specifically aim to bridge the soft-/hardware gap? The best one I can come up with is Performance Analysis and Tuning on Modern CPUs[0] by Denis Bakhvalov.
I also like this very readable paper https://xnning.github.io/papers/perceus.pdf, about reducing the costs of reference counting in context of functional languages.
I agree with his point, and Clojure is my favorite programming language, but the computer itself is place-oriented. It runs on reading from and writing to locations in memory, and extracting maximum efficiency from it requires direct control over that memory.
[1] https://debasishg.github.io/blog/push-ifs-up-fors-down/ [2] https://venge.net/graydon/talks/VectorizedInterpretersTalk-2...
You could similarly argue that the computer is in fact fundamentally functional, since the core instruction cycle (Fetch Decode Execute) does not modify any memory.
The key insight into that system is that we've embedded a functional system within a mechanism that makes it imperative. The functional cycle can output commands to fetch it more data, that it can then use in the next cycle. It becomes imperative by the addition of time, which it needs because it's limited in the amount of input and output.
Miniz_oxide is an intentionally simple implementation. It would be more fair to compare it to the newer zlib-rs[1], which even beats zlib-ng (fastest implementation in C)[2].
[1]: https://lib.rs/crates/zlib-rs
[2]: https://trifectatech.org/blog/zlib-rs-is-faster-than-c/
- The faster package may have been available for a shorter amount of time and so downloads have not had the time to catch up yet.
- Many people may not be aware of the faster package, but they would switch if they knew.
- Maybe people are aware of the faster package, but there are switching costs that prevent them from adopting it right now that are greater than the costs incurred from the slower package.
It's fun trying to reorganize your code to make it pass the `pure` checking.
I always thought having a working program should be opt-out, not opt-in.
(For example, my ideal language supports dynamic typing, with the compiler directive #jammode, or alternatively #explodeinprod)
Part of the problem is `pure` is transitive, i.e. a pure function calling an impure one is not allowed. This makes it hard to incrementally make the code pure.
But when one succeeds at it, it is very satisfying.
There is an escape hatch, however. Prefixing a statement with `debug` allows the dependent statement to be impure. This is very handy when one needs to insert some print statements to debug a program. (Calls to printf are impure!)
By the way, hi again! We chatted briefly in person after Handmade Seattle 2021, and I remember being really impressed to learn that you're one of the few people (maybe the only one?) who has ever implemented a working C++ compiler solo.
Anyway, hope all is well, and let me know if you're ever interested in recording a podcast episode sometime - oss@rtfeldman.com
Anyhow, I'll be speaking at NWCPP soon:
THURSDAY, Nov 19, 2026 at 6:30 PM to 8:00 PM
Elegant D - There’s More to Programming than Getting Code to Work Bellevue City Hall, Room 1E-121 450 110th Ave NE Bellevue, WA 98004
nwcpp.org
Please come! All are welcome!
So glad to have been writing production Haskell for almost a decade
I spent a few months designing my ideal language, just based on all the pain points I ran into everywhere else. I just took all the pain, and deleted it. "Let's make it so this particular brand of suffering becomes physically impossible forever."
The result was roughly halfway between Haskell and Rust, in design-space. (Ergonomics and syntax not yet specified.)
Finally I understood what all those weird rules were for, by seeing what happens when you don't have them...
---
On that note, I have heard that certain languages are badly designed. I disagree. Badly designed implies that someone sat down, carefully thought things through, thought through the second-order consequences, and then somehow still came to all the wrong conclusions.
In several such cases it is clear that a language is not badly designed, but rather was not designed at all.
debug printf("I made it this far before crashing\n");
which turns off the purity checks for the dependent statement.Asking cuz I'm preparing an article on how fast JS can get and found the same thing, you can get in 10% of optimised C-code with basically any code if you torture V8 enough (excepting SIMD where there seems to be a bug in V8 where it doesn't optimise to SIMD even when beyond obvious). Figured out I could extract it in a lib and do it more streamlined but not sure how well manual transforms generalise.
Also, when working on the roc-deflate, did you find recurring transformations that could fit that model or were most improvements specific to compression?
>where it doesn't optimise to SIMD even when beyond obvious What was that code?
> What was that code?
literally:
``` function numericKernel(a, b, out, n) {
for (let i = 0; i < n; i += 4) {
out[i] = a[i] * b[i] + 0.25;
out[i + 1] = a[i + 1] * b[i + 1] + 0.25;
out[i + 2] = a[i + 2] * b[i + 2] + 0.25;
out[i + 3] = a[i + 3] * b[i + 3] + 0.25;
}
}
```It then links to a video where a consultant mentions using in production (for several years already?), which confused me.
How can you justify using immature technologies for client work? Or is it an extra benefit when it breaks and you have to fix it?
I'm genuinely curious, maybe it's not as bad as I think. But I've used immature hosting platforms, and that certainly didn't go well for me!
Ask everyone that's using generative AI?
I suspect this will be the best way to delegate things to AI while still staying in driver's seat.
Compiled ones already translate to the theoretically fastest imperative language (machine code) using correctness preserving transforms. And plenty of them already occupy the top tier of performance for garbage collected languages. But being able to match the performance people can achieve with C, C++, etc. requires manual memory management, and that is a difficult nut for functional languages to crack because it interacts poorly with closures.
We have had a good technology for doing that since the late 1950s, and, like I pointed out in my first reply, it already works very well.
Replacing it with a probabilistic model would not improve things. In this case, it would do the opposite of what you want because you would be replacing something that is provably and reliably able to produce correctness preserving translations with something that is fundamentally and inherently loosey-goosey.
Peephole optimization that can't be made global due to combinatorial explosion?
ghci> :{
ghci| for_ [1 .. 10] $ \i ->
ghci| print i
ghci| :}
1
2
3
4
5
6
7
8
9
10
It doesn't require loops to be implemented at the language level, however. The language is rich enough to support user-defined loop constructs.I also may have gotten some detail slightly wrong, the code in Data.Foldable is kinda weird and I haven’t Haskell’d in a while
Without data on compression levels it is hard to make any useful conclusions.
> I didn't expect to beat libdeflate's performance across the board, but I wanted to see how close it could get on the non-streaming APIs (as roc-deflate hasn't implemented streaming yet), using the classic Silesia corpus for benchmarking and verifying that it produced identical outputs.
Looking at it again, do you actually have any working correctness check? If you simply ask the AI to test the code for correctness and speed against the benchmark suite, it could easily hyperoptimise for the suite in both regards.
What's the evidence for this claim?
Indeed nobody wants to review LLM generated slop.
So being able to know that this code cannot contain memory bugs, race conditions, etc is extremely valuable.
I might go a far as to claim: I'd be okay with the AI using an obscure language, if I can know the code is free from memory issue, race conditions and has no side effects, etc.
The argument that with LLMs pure functional language that I didn't bother to write before is now viable.
> gizzilion times slower than a tortoise… [people who disagree with me about a tech issue, who are] flat-earthers getting freaked out