Leave Haskell behind

For almost a full decade – from discovering Haskell around 2009 until moving to a job where I mainly used Ruby and C++ around 2019 – I would have called myself a Haskell programmer first and foremost.

Not necessarily a dogmatic Haskeller! I was – and still am – proudly a polyglot who jumps between languages ​​depending on the needs of the project. However, Haskell was my default language for new projects, and absent very compelling reasons to use other languages, I would push for Haskell. I used it for command line tools, for web services, for graphical applications, for small scripts…

At this point, however, I think my Haskell years are behind me. I didn't swear it aggressively - it's not a "Haskell is dead to me!" piece, but it's not my default language anymore, even for my personal projects, and I certainly wouldn't deliberately look for "work in Haskell" the way I once did.

So, I wanted to talk about why I moved away from Haskell. I should say up front: This explains why I left Haskell, not why you should. I don't think people are wrong to use Haskell, nor that Haskell is bad. In fact, if I had written this article the way I hope to write it, I hope people will read it and come away with a desire to maybe learn Haskell themselves!

What attracted me to Haskell?

The biggest draw for me when I came to Haskell wasn't monads, DSLs, or even types. The biggest plus was the ability to reason about code symbolically and algebraically.

Yeah, I know, that might sound like pretentious nonsense. Support me.

The idea here is that some transformations in Haskell are always correct, allowing you to reason about code in a mechanical yet powerful way. A simple example: in Haskell, we can look at a function call and replace it with the body of that function. Let's say that if we have a function double n = n + n and we call it with an expression x, we can replace double x with x + x, no matter what x is.

This property is not true in most other programming languages! For example, in many imperative languages, this transformation will fail if we call double with i++ for a variable i: the reason is that i++ modifies the value of i, so double(i++) (which increments i once) will of course produce a value not equal to (i++) + (i++) (which increments i twice.)

“So what? you might ask, and that's right. However, it's starting to be very attractive in that certain ways of modifying your code will still be mechanically correct. It's incredibly liberating. It allows for a kind of fearless refactoring, in which certain mechanical transformations, each easily verified as correct, can be stacked on top of each other to make wild but ultimately safe changes to your code as a whole. Performing large refactorings in, say, a Ruby codebase can be deeply tedious, while doing large refactorings in a Haskell codebase can be child's play.

Or, as the mathematician Hermann Weyl once said:

We now come to the decisive stage of mathematical abstraction: we forget what the symbols represent. ...[The mathematician] need not remain inactive; there are many operations he can perform with these symbols, without ever having to look at what they represent. »

This same approach (forgetting the meaning of code while being able to transform it in productive and powerful ways) is more possible with Haskell than with any other language I've used.

Of course, the other great thing about Haskell is the type system. There's a lot to be said for the whole modern Haskell type system, but the core Haskell language strikes a spectacular balance between having a strict type system without it being too noisy or restrictive . Type inference means that most types are implicit in code, which makes the process of writing types significantly less onerous than in something like Java, and the flexibility and convenience of typeclasses means that even when you have to think about types, they're often not too picky (compared to, say, OCaml's requirement that you use different versions of the arithmetic operators for integers and floats.) At the same time, the fact that the type system can sometimes get in your way is part of the reason for using Haskell.

I would describe good Haskell code as "brittle", and I mean that as a compliment. People tend to casually use "brittle" to mean "subject to breakage", but in materials science what "brittle" means is that something breaks without bending: when a brittle...

Leave Haskell behind

For almost a full decade – from discovering Haskell around 2009 until moving to a job where I mainly used Ruby and C++ around 2019 – I would have called myself a Haskell programmer first and foremost.

Not necessarily a dogmatic Haskeller! I was – and still am – proudly a polyglot who jumps between languages ​​depending on the needs of the project. However, Haskell was my default language for new projects, and absent very compelling reasons to use other languages, I would push for Haskell. I used it for command line tools, for web services, for graphical applications, for small scripts…

At this point, however, I think my Haskell years are behind me. I didn't swear it aggressively - it's not a "Haskell is dead to me!" piece, but it's not my default language anymore, even for my personal projects, and I certainly wouldn't deliberately look for "work in Haskell" the way I once did.

So, I wanted to talk about why I moved away from Haskell. I should say up front: This explains why I left Haskell, not why you should. I don't think people are wrong to use Haskell, nor that Haskell is bad. In fact, if I had written this article the way I hope to write it, I hope people will read it and come away with a desire to maybe learn Haskell themselves!

What attracted me to Haskell?

The biggest draw for me when I came to Haskell wasn't monads, DSLs, or even types. The biggest plus was the ability to reason about code symbolically and algebraically.

Yeah, I know, that might sound like pretentious nonsense. Support me.

The idea here is that some transformations in Haskell are always correct, allowing you to reason about code in a mechanical yet powerful way. A simple example: in Haskell, we can look at a function call and replace it with the body of that function. Let's say that if we have a function double n = n + n and we call it with an expression x, we can replace double x with x + x, no matter what x is.

This property is not true in most other programming languages! For example, in many imperative languages, this transformation will fail if we call double with i++ for a variable i: the reason is that i++ modifies the value of i, so double(i++) (which increments i once) will of course produce a value not equal to (i++) + (i++) (which increments i twice.)

“So what? you might ask, and that's right. However, it's starting to be very attractive in that certain ways of modifying your code will still be mechanically correct. It's incredibly liberating. It allows for a kind of fearless refactoring, in which certain mechanical transformations, each easily verified as correct, can be stacked on top of each other to make wild but ultimately safe changes to your code as a whole. Performing large refactorings in, say, a Ruby codebase can be deeply tedious, while doing large refactorings in a Haskell codebase can be child's play.

Or, as the mathematician Hermann Weyl once said:

We now come to the decisive stage of mathematical abstraction: we forget what the symbols represent. ...[The mathematician] need not remain inactive; there are many operations he can perform with these symbols, without ever having to look at what they represent. »

This same approach (forgetting the meaning of code while being able to transform it in productive and powerful ways) is more possible with Haskell than with any other language I've used.

Of course, the other great thing about Haskell is the type system. There's a lot to be said for the whole modern Haskell type system, but the core Haskell language strikes a spectacular balance between having a strict type system without it being too noisy or restrictive . Type inference means that most types are implicit in code, which makes the process of writing types significantly less onerous than in something like Java, and the flexibility and convenience of typeclasses means that even when you have to think about types, they're often not too picky (compared to, say, OCaml's requirement that you use different versions of the arithmetic operators for integers and floats.) At the same time, the fact that the type system can sometimes get in your way is part of the reason for using Haskell.

I would describe good Haskell code as "brittle", and I mean that as a compliment. People tend to casually use "brittle" to mean "subject to breakage", but in materials science what "brittle" means is that something breaks without bending: when a brittle...

What's Your Reaction?

like

dislike

love

funny

angry

sad

wow