8 months of OCaml after 8 years of Haskell in production

Hi everyone :wave:

I’ve been using Haskell in production for the previous 8 years. I’ve been quite active in the Haskell community, and some of you may remember my past contributions to the community and the ecosystem.

For the last 8 months, I’ve been using OCaml at Bloomberg, and my focus switched to a different language from the ML family.

I wrote a blog post comparing two languages from my POV:

I tried to provide a reasonable and balanced overview of these two languages, although my experience in both is significantly different. I hope this blog post can give a rough idea of both languages :relieved:

And who knows, maybe both OCaml and Haskell can improve and get better by inspiring each other :hugs:

Enjoy! And let me know any feedback you have!

29 Likes

If I come to an existing Haskell project, the worst thing previous developers could do… Well, my previous 8 years of Haskell experience can’t prepare me for that :sweat_smile:

That’s why I feel more productive in OCaml.

Interestingly, John Carmack said something similar in an interview (I think with Lex Friedmann), when describing why he prefers C over more powerful languages (amongst other reasons): small language surface, easy onboarding, less over-engineering.

I wonder if that’s why he lost interest in Haskell too.

I personally don’t have a lot of trouble staying away from the half baked language features (type families is half baked) in my own projects, but yes, you get exposed anyway.

5 Likes

I would have thought an incremental approach would be the best option here: surely it must be easier to see what feature or extension is required after putting together a small prototype, rather than trying to imagine what’s best to use before any code has been written.

Just use the basic features of the language to begin with for the prototype, adding more advanced ones only as needed (the developer equivalent of call-by-need :-) That way, if you find out later that some code has to be replaced, at least you’ll be in a better situation than trying to envisage and do “everything all at once” only to find out that large parts of it was wasted effort.

P. S. I noticed in the comparison table an entry labelled Ergonomic mutability - in the interest of fairness, can an entry like Ergonomic laziness be added?

Same story here. I might avoid using them but I still spend that bit of mental effort when I first think about solving the problem. The knowledge cursed me.

And yes, I might avoid using those features, but there’s an entire community that uses those features in both OSS and industrial projects.

I once wasn’t able to finish a refactoring in a Haskell project at work because the project used a MultiParamTypeClass with 3 types, and I wasn’t able to figure out how to fix the GHC error message :disappointed:

5 Likes

Unfortunately, the cost of rewriting is often much higher than the cost of implementing for the first time :disappointed:

Also, time and money are limited. To understand the drawbacks of some features, you have to use them first. There’s no other way. So those features are being used by various people in various projects (I’m guilty here as well). And now some other people have to maintain this legacy code.

This was enabled by the sole fact of having those features in the first place.

P. S. I noticed in the comparison table an entry labelled Ergonomic mutability - in the interest of fairness, can an entry like Ergonomic laziness be added?

There’s a point Laziness by default. I think it’s the same as being ergonomic :slightly_smiling_face:

2 Likes

I wonder if that’s why he lost interest in Haskell too.

I feel like there are so many issues with GHC (no way to restrict variable lifetimes; overreliance on GHC optimizations instead of a way to write low-level code directly), the ecosystem (datatype and C FFI libraries need to be written from scratch; everybody is afraid of laziness) and the functional approach as a whole (untrodden application design territory), that “high skill ceiling and features you don’t need” weren’t anywhere near the top of the complaint list.


I also feel like the community is in part to blame for the “I’m more productive in other languages” meme. Sure, the Simple Haskell pamphlet exists, but… it’s a “keep it simple, stupid, and don’t hurt yourself” quotes page. Libraries don’t follow any of it, and in return noone expects simple extensible libraries. And at this point I don’t even think anyone cares.

4 Likes

Abso-howling-lutely! It would be nice to see an innovation in GHC that, for example, got rid of some great number of semi-related language extensions, make GHC run 80% faster, etc !


…but as you also noted:

Since maintaining code also means rewriting it, there could be an opportunity to slowly (over time) refactor the problematic extensions out of existence (depending on how long you’ll be around for in that project, of course). Then again, I’m no sociologist - I can envisage how I would react in a given situation much more easily than attempting to figure out what someone else’s reaction would be.

To me, bugs are bugs - if some language fad extension was a big enough failure…I would like to think that the organisation in charge of the codebase would consider that a bug, and be interested in a long-term plan for its removal (but also see note regarding sociology).

1 Like

Then again, restricting oneself to basic language features can also lead to code that is more complicated and harder to refactor or change.

6 Likes

Hence the emphasis on small prototypes - if you find a useful-enough language feature, there’s less code to rewrite.

I think you nailed it - ecosystem unstable enough to prevent relying upon large enough packages: even one dependency in a tree makes the entire build irreparably fail. Forcing the programmer to either rewrite majority of dependencies by herself from scratch (at cost absolutely prohibitive in real world), or freeze the entire toolchain for the lifetime of the product (again, completely impossible in the real world because of bugs and security fixes in both dependency packages and the toolchain itself).

There’s less of it in Haskell now than a few years ago - but maybe enough to still keep it away from the industrial mainstream.

In my experience, C ecosystem doesn’t really have this problem: much fewer cases encountered, and fixes were trivial.

You may find a solution a Haskell. But often you’ll find too many solutions, you won’t know which one to choose.

I can feel your pain as I am often regretful of when started programing and I would be happy of any solution (vs THE solution) . Unfortunately, I think this problem is not specific to Haskell but eventually occurs with any languages.

I m looking forward to reading the next version of this blog once the honeymoon is over. I m not dismissing your post, there is lot of truth in it but it will interesting to see in a few years time on which point you changed you mind and why.

6 Likes

Thanks for this nice summary, and thanks for your eight years of contributions to Haskell. This part was my favourite:

Using Haskell tooling is like always being in the quantum superposition of “How do you even use other PLs without such wholesome Haskell tools???” and “How Haskellers can live like that without these usability essentials???”.

I find it hard to reconcile how excellent I believe our language is with the existence of so much flakiness and so many sharp edges in our ecosystem. The situation is improving though!

Regarding batteries included,

I’m a big proponent of the idea that a standard library should be batteries-included.

I’m sympathetic to the “batteries included” view of standard libraries, but it has its downsides too. For example, one reason that ByteString is not one of the batteries that’s included with base is because historically String was chosen to the battery fulfilling that role, and now it’s very hard to displace.

Regarding error messages, I take your point that “this is just one example (and most likely not the best one)” so here’s an attempt at an example that is fairer, because it doesn’t involve typeclasses or overloading of operators and numeric literals (that OCaml doesn’t support anyway). OCaml’s message is still better though.

ghci> True && [False, False, True]

<interactive>:1:9: error:
    • Couldn't match expected type ‘Bool’ with actual type ‘[Bool]’
    • In the second argument of ‘(&&)’, namely ‘[False, False, True]’
      In the expression: True && [False, False, True]
      In an equation for ‘it’: it = True && [False, False, True]
7 Likes

I’m not sure. I don’t see a general shift in perception. Some people here and there do the work. But that can easily regress once they’re gone.

HF is somewhat frozen in the dilemma between building trust through support and diplomacy and actually executing their true goals. So I don’t see them taking over the work of maintaining a specific perception.

Most key projects are still incredibly isolated, some from their end-users, some from their contributors, some from each other.

6 Likes

I’m not sure. I don’t see a general shift in perception. Some people here and there do the work. But that can easily regress once they’re gone.

I didn’t mention who’s doing the work or what happens when they’re gone, I just mentioned the situation of flakiness and sharp edges is improving. Would you say that cabal and HLS are more flaky than, say, three years ago, less or the same?

I’m afraid after my honeymoon with Haskell was over, I became immune to having honeymoons with other languages because Haskell set a high threshold. I see trade-offs of different languages clearly and just follow my preference at my current time.

The ideal language for me doesn’t exist yet (because I haven’t written it yet) but that’s okay. As long as I can use any language I enjoy, I’m fine :relieved:

4 Likes

Fair enough, I think your “concerns” about Haskell are totally valid and valuable feedbacks.

5 Likes

I’m familiar with the downsides of this approach too :disappointed:
Still, trade-offs are everywhere, and in the end, I tend to follow my preference while being aware of all the pitfalls. I guess, I got bitten more by the lean standard libraries approach :sweat_smile:

Regarding error messages, I take your point that “this is just one example (and most likely not the best one)” so here’s an attempt at an example that is fairer, because it doesn’t involve typeclasses or overloading of operators and numeric literals (that OCaml doesn’t support anyway). OCaml’s message is still better though.

True, I’m still a bit sour about this particularly confusing GHC behaviour :sweat_smile:

Actually, GHC error messages are even better now, as you get a stable unique error code which you can read more about online with examples and suggestions on how to fix them!

GHCi, version 9.8.1: https://www.haskell.org/ghc/  :? for help

ghci> True && [False, False, True]

<interactive>:1:9: error: [GHC-83865]
    • Couldn't match expected type ‘Bool’ with actual type ‘[Bool]’
    • In the second argument of ‘(&&)’, namely ‘[False, False, True]’
      In the expression: True && [False, False, True]
      In an equation for ‘it’: it = True && [False, False, True]
3 Likes

The ideal language for my would be a set of languages that are easily interoperable and where I can pick the complexity level I get exposed to based on the problem at hand.

C#, F# and F* somewhat go into that direction, but it’s still a long way.

Haskell seems to go the “let’s retrofit whatever we can” route. I’m not a fan of that approach.

3 Likes

Actually, in this case Haskell’s message is still better than OCaml’s:

# true && [false; false; true];;
Error: this variant expression is expected to have type bool
There is no constructor :: within type bool

We can thank OCaml’s ‘type-directed disambiguation’ of constructors for this ambiguous error.

9 Likes

In less nebulous terms? The first step to fixing problems is defining them clearly.

2 Likes