Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This is very cool, but it still makes me extremely skeptical. In brief, the more posts of this type I see, the more I'm implicitly being convinced that writing performant code in high-level languages requires a series of tricks that get around the "natural" or "naive" way to write the code. The impression I get is that high-level languages allow you to express your thoughts concisely, or they perhaps allow you to mimic the performance of low-level languages, but not both at the same time.

Notice that it takes an entire blog post to use Haskell to emulate the performance of a straightforward 20-line C program that can be written in under 60 seconds. There are high-performance tasks which are practically trivial in an imperative language but which merit a conference paper when accomplished in Haskell.

So it's hard to see something like this as an argument that higher-level languages are simplifying anything.



You should think of this post as being aimed at Haskell library writers. The benefit to the application developer is that more and more optimizations are available through identical or nearly identical, familiar interfaces such as those for lists.

In my case (joe average haskell dev) that means I can just stick to normal constructs and switch out the underlying data structure to get the speed benefit. This article actually might lead to such a change in a computer vision package I work on.

You can expect to see this pattern over and over with Haskell, so that by the time you get to data structures that abstract away GPU-based calculations, nobody will be wishing people would just write the darn code in C.


Exactly. A this point you can just write idiomatic haskell-98 list code and just do an

    import qualified Data.Vector.Unboxed as V
and get blazing-fast performance.

Even with the complication of laziness, getting great (for most reasonable definitions of great) performance out of pure haskell code was a solved problem years ago. Doing high-performance IO looks like it's getting there too.


Solved how? Most Haskell performance advice (like Tibbel's recent presentation) is to add strictness annotations, which is both non-Haskell98 and non-lazy. So it is solved, but not elegantly or hidden in core libraries.


Fair point. I meant solved in that it's not difficult with a bit of experience and profiling. I actually don't mind the workflow of writing naive (or non-prematurely-optimized) code and then adding strictness hints where the compiler needs it.


Ah, also meant to say that adding strictness with `seq` is haskell 98, and there's nothing so strange about forcing evaluation in a lazy language.


Meant to upvote, fat-finger downvoted instead, my bad. Hopefully somebody compensates.


This is wildly off-topic, but an idea caught my interest. I upvoted the parent of your comment to compensate for your downvote and it made me wonder how many other people have done the same. It put a bunch of thoughts in my head that I can't seem to form into coherent words, but those thoughts have to do with gaining quite a bit of comment karma from one person's accident, karma trolling, etc. I can't imagine that occurring on HN too any extent, but it's an interesting thought.


Tangentially, there are people in CS who study trust/reputation systems (like karma), and how to design those systems so that people want to participate and "do the right thing". Maybe related.


It's simpler to write efficient low-level code in C, but you have to pay the cost of efficiency (in effort, clarity, and LOC) everywhere in your program, in every single function you write. For some kinds of programming, such as kernel coding, almost all of your code needs to be as efficient as you can make it, so that's fine. For a lot of application programming, though, low-level efficiency only matters for a small percentage of the code, so it's a good trade-off to use a more elegant and expressive language, if optimizing the performance-sensitive parts isn't so difficult that it cancels out the savings elsewhere.

That caveat makes analyses like this one interesting, because the trade-off hinges on how hard it is to write efficient code when you need to. For me, the significance of this article is that it's possible to write high-performance Haskell in a simple and readable style if you're a Haskell expert who knows a lot about how the compiler optimizes code. That's a useful thing to know if you're considering using Haskell for a project. (The next thing I would ask is whether you're screwed if you're just an intermediate programmer who knows the language but not the implementation.)


Not to take away from Haskell, but this is exactly why many people in academia use Python. It's easy to express an idea in it, and test at a low performing level. Then when you know where your bottle necks are, you can re-write that tiny bit in Cython and gain 90% of the speed improvement of writing the whole thing in C.


As someone who knows a bit of haskell but not a lot I don't see this so much as a series of tricks. Ignoring all the explanatory pieces it comes down to "use the fusible Data.Array.Vector library and unbox your doubles". The unboxing is a given, optimization 101 in haskell, and the fusion stuff is the cool bit.

You can probably make the argument that the tail recursive function is not the "natural" way to write this code but conceptually that's on the same level as loop unrolling in C.

It doesn't take an entire blog post unless you are explaining the compiler tricks working behind the scenes and comparing the assembly produced.

So basically I agree that there is some express concisely vs. low level performance tension here but the takeaway is how little of it there is. This is probably lost if you are also having to try and parse this weird haskell stuff at the same time.


The thing is, barring the intervention of marketing departments, compilers don't get more dumb over time. If this is possible now, it ought to be easy soon (for some value of "soon").

Haskell's main benefit, to my mind, is its type safety. That's what saves programmer time in the long run. If that can be fused (hah!) with decent performance - even if it's not quite up to C without contortions - it's a net win.


I've always wondered why language designers don't strive to optimize for the trivial cases. There is a natural way to design code for a beginner, and having that map to the most efficient constructs seems to be one of the areas of programming language research that is lacking. Maybe it's the need for the 'sufficiently smart compiler' that is holding things back, but given that PLs are abstractions, couldn't the underlying abstraction be completely different than the user visible abstractions?


I believe they do optimize for the simple cases, but just in very different ways. Haskell optimizes for composition, abstraction and expressiveness in a function-application sense. C optimizes for imperative loops, simple functions and controlling your memory layout and execution precisely [There is probably a better characterization of C, please comment].

I think there are plenty of tricks involved in writing performant C code that aren't obvious. Things like cache behavior, memory access patterns, etc. The job of the compiler and PL is to help us by making it unnecessary to worry about such things unless we really need to.

It's a testament to the power of modern programming languages and computer speeds that there are many programmers who don't understand registers, caches, assembly, virtual memory, etc.


In the design of Rust, as a rule of thumb, we've always tried to make the simplest code also the fastest code, to minimize performance footguns. (Writing the Rust compiler in Rust and constantly running it through Instruments.app helps to keep us honest.)


There is a natural way to design code for a beginner

Smalltalk was purposely aimed at children. The thing is, our culture is quite complicated, and there are expectations set up in different fields. A grade school child might find operator precedence confusing, while an engineer would view it as second nature and a non-negotiable feature. Likewise, someone steeped in the Unix way of doing things might find a language incorporating Sed/Awk syntax to be very "intuitive" while someone from a different discipline might find it to be cryptic.

given that PLs are abstractions, couldn't the underlying abstraction be completely different than the user visible abstractions?

They almost always are for PL.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: