My Thoughts on OCaml vs Haskell/Rust in 2023

@dbuenzli,

Having a “blessed” runtime representation will also help with debugging OCaml code in ocamldebug I would guess? There are indeed lots of possibilities.

How would you go about building consensus around this? What is the best approach? An issue on the OCaml project on Github? A thread on discuss.ocaml.org initiating discussion?

I’d love for you to take this forward. I think it is a good idea.

3 Likes

There’s a thread in the Purescript Discourse forum in which people mention possible new features that they think would help the community grow. All of the proposals seem to be more or less oriented toward Javascript, but on some of them there’s pushback from people who argue that the proposed addition wouldn’t play well with the goal of supporting different backends." So there seems to be a core group who want to maintain the possibility of growing beyond the Javascript backend, even though at present the orientation of the community seems focused on Javascript.

I looked at a couple of these “runtime representation” libraries (the homepages) and it looks like these are fine for things like the standard type-derivers. But there are other uses for typeclasses:

  1. Rust uses typeclasses in its FFI mechanism
  2. extending standard operators to arrays/matrices/vectors/etc (and sparse versions of these)

and probably others that I could come up with, if it weren’t past midnight.

@Chet_Murthy I think the point is that we can help OCaml simply by having a standard runtime type representation. Type classes would be great but, we don’t need full blown type classes to improve things in OCaml around deriving related needs, for instance.

2 Likes

I would suggest that derivers already exist, are easy-to-program, and if that’s what’s driving the discussion around typeclasses, then that’s a mistake. To my mind, the strongest need for typeclasses is for things like numerical packages. I did a good bit of Rust programming with their numerical matrix (both dense and sparse) packages, and it’s wildly better than doing that work in OCaml.

P.S. Example: as a demonstration, I rewrote the “ppx_jsobject_conv” deriver without actually understanding its code (just looking at its output on various types, and using its tests as a guide) in a day-or-so. It was a breeze.

1 Like

Using ppx derivers on big, complex, recursive types seems very hit and miss in my experience. If there was a mandated runtime type representation then this problem would be simplified very much.

Got examples? I use 'em routiinely on the many versions of the OCaml AST, and use a deriver (pa_ppx_migrate) to automate nearly all of converting between different versions of the OCaml AST. The one thing that I think is nontrivial (mostly b/c I’ve never used 'em) is GADTs. But for the rest of OCaml’s data-types? I kinda doubt any claim that writing derivers for those is hard. I’ve written all the standard ones, plus a bunch of others, all pretty much as I have need of them.

Part of why I’m pushing back on this, is that I’ve seen how powerful typeclasses can be in allowing to succinctly program numerical matrix math, and I want that for OCaml. And also (btw) for programming FFIs. Neither of those can be addressed by things like this “reflection” representation.

1 Like

Of course you can always have “more”. Programmers always want more without seeing the tradeoffs.

The ratio between system complexity and ergonomic improvement to the experience of the language is rather large for adding typeclasses and touches the (already complex) language itself – and thus the way we program in OCaml.

It is much smaller for runtime types, which do not change the language as we know it but makes the eco-system much more ergonomic for a diversity of currently quite painful boilerplate tasks.

I won’t even start arguing with any ppx-based solution, ppx remains and will ever be as ridiculously ugly as ever. ML is a rich language, solve the problems therein, not into uncompositional meta layers.

6 Likes

I don’t remember all the details but sometime ago I was trying to debug why some things were not working in merlin for auto complete on object methods.

I was wanting to debug the typed tree generated by merlin. IIRC I tried to do various kinds of deriving on the big mutually recursive type defined in https://github.com/ocaml/merlin/blob/master/src/ocaml/typing/typedtree.ml in order to find out what is happening. The OCaml typed tree is quite complicated and mutually recursive and the derivers kept choking with weird incomprehensible (to me at least) errors.

I tried various approaches: trying to just use a deriving show, generate a runtime time representation to be able to get a show etc. I was unsuccessful.

(In the end, I found some approaches within OCaml itself that would allow me to print the typed AST without having to derive a pretty printer).

This was not the first time for me. Type with GADTs, types with parameters, mutually recursive types etc. all become tricky to do @@deriving for.

P.S.

You’re probably an OCaml veteran: if something doesn’t work you have many ways of getting it to work. Providing an easy, no hassle path for non-OCaml experts will always help OCaml.

1 Like

Another take on the current state of OCaml from Two Years of OCaml. There are many points I do and don’t agree with from both, but I think it is important to reflect on the language and tooling. These fresh takes on things are useful to read. Thanks @sid for taking the time to write your version up.

For me personally I would like a version of modular implicits to land and to have a typed effect system to go along with my EIO/Multicore. I should writeup a longer version given my background with Haskell and OCaml.

3 Likes

I do feel that that it’s not a coincidence that we’re seeing some survey articles about OCaml lately. My unscientific gut feel is that there has been a reaction to overly complex languages like Scala and Haskell in recent years and people are searching for simpler alternatives. OCaml (and Kotlin as another example) are simpler, more grokkable languages that have been getting more attention as people reevaluate Haskell/Scala.

If OCaml can provide production level modular implicits in a timeframe that is less than a couple of years, I think it has the chance to increase its developer usage drastically. Otherwise time may pass us by.

@lambda_foo I always have been interested in your take on Haskell ! Look forward to it!

3 Likes

I’m talking widely hypothetically here, but I think there is room for a
new ML language that is not OCaml and learns from the trends in recent
languages.

In particular, a new ML that aims at being successful could drop a ton
of features from OCaml (objects, functors, global type inference, poly
variants, etc.) and focus on:

  • “classic” syntax, like Reason/Rust (it helps with adoption, whether we
    like it or not)
  • typeclasses/traits from day one. No more magic equality and juggling
    printers.
  • deriving from day one. Serde from day two.
  • type inference inside a function, but annotations for function
    arguments and return type.

I’m a bit on the fence on currying because it’s sometimes very nice, but
it’s also, like the article suggests, a source of bugs (e.g. I always
annotate the type in ignore to not accidentally ignore a partially
applied function).

All that sounds a lot like Rust-with-GC, and I think it’d be quite
successful if it existed. I don’t think OCaml can evolve towards this
because it clashes with a lot of fundamental features like the powerful
module system.

7 Likes

Yes, there is a lot of space for a language which is like a Rust-with-GC. Ironically Rust was Rust-with-GC before it dropped GC.

Purescript is an interesting language in its own right and matches some of the points you listed. It essentially a strict Haskell. It concentrates on the frontend and on generating JavaScript but other backends are available (see some discussion above).

2 Likes

A strict, impure Haskell with bracket syntax, so
really quite different than Haskell :slight_smile:

This is often repeated, but IMO without much to support the assertion. Parens-and-braces being so important doesn’t account for python or ruby at all (for example), and ISTM that the real-life Reason experiment demonstrated that syntactic changes don’t actually drive adoption much compared to systemic questions (e.g. library ecosystems and retargeting to desirable/in-demand platforms).

7 Likes

To varying degrees, all of Swift, D, Nim, Crystal, and maybe even C# fit this description.

But really, the closest thing to “Rust + GC” is…Rust! There’s a constant churn of GC experiments in that community, with various localized automatic memory management facilities cropping up all the time (crossbeam’s epochs, all sorts of arena implementations, etc). Multiple tracing GCs have been written for Rust, but none of them have really attracted much usage AFAIK.

To me, this is a pretty good sign that there’s just not much demand for “Rust + GC”. :person_shrugging:

3 Likes

Just noting that with (Jane St’s) Base and associated PPXs you get all of that:

  • no polymorphic compare/equal (unless explicitly calling Poly.equal)
  • deriving sexp, fields, equal, etc. that just works (while remaining hackable when needed)
  • warnings (as errors) on dangerous uses of let _ = or ignore

(+ a well designed API for most common modules)

I myself hope I’ll never again have to dig into somebody else’s codebase that uses type-classes :slight_smile:

11 Likes

There are a lot of correct assessments in your comment. But I would disagree with the above one.

The design of Rust by now does not facilitate the construction of a very ergonomic GC. GC cannot be bolted on later on like some of the libraries in Rust you refer to. The outcome will always be lacking or hamstrung in some way.

So if GCs have not taken off in Rust it does not mean there is no space for a Rust-like language in which GC support provided is by the core language facilities.

P.S. Sorry this is a reply to @cemerick and not c-cube

1 Like

What @sid said. A rust-like language designed with a GC in mind would be simpler in many respects (e.g. no need for the borrow checker at all, and probably no need for references; no distinction between String and str; no distinction between closures and functions).