OCaml does have an okay LSP implementation though, and it's getting better; certainly more stable than F#'s in my experience, since that comparison is coming up a lot in this comment section.
This has the benefit of giving you the ability to refer to a case as its own type.
> the expression of sums verbose and, in my view, harder to reason about.
You declare the sum type once, and use it many times. Slightly more verbose sum type declaration is worth it when it makes using the cases cleaner.
Correct. This is not the case when you talk about Java/Kotlin. Just ugliness and typical boilerplate heavy approach of JVM languages.
I have provided a case how using inheritance to express sum types can help in the use site. You attacked without substantiating your claim.
> This has the benefit of giving you the ability to refer to a case as its own type.
means.
I can tell.
Thankfully the OCaml textbook has this explicitly called out.
https://dev.realworldocaml.org/variants.html#combining-recor...
> The main downside is the obvious one, which is that an inline record can’t be treated as its own free-standing object. And, as you can see below, OCaml will reject code that tries to do so.
- create a separate record type, which is no less verbose than Java's approach
- use positional destructuring, which is bug prone for business logic.
A case of a sum-type is an expression (of the variety so-called a type constructor), of course it has a type.
datatype shape =
Circle of real
| Rectangle of real * real
| Point
Circle : real -> shape
Rectangle : real * real -> shape
Point : () -> shape
A case itself isn't a type, though it has a type. Thanks to pattern matching, you're already unwrapping the parameter to the type-constructor when handling the case of a sum-type. It's all about declaration locality. (real * real) doesn't depend on the existence of shape.The moment you start ripping cases as distinct types out of the sum-type, you create the ability to side-step exhaustiveness and sum-types become useless in making invalid program states unrepresentable. They're also no longer sum-types. If you have a sum-type of nominally distinct types, the sum-type is contingent on the existence of those types. In a class hierarchy, this relationship is bizarrely reversed and there are knock-on effects to that.
> You declare the sum type once, and use it many times.
And you typically write many sum-types. They're disposable. And more to the point, you also have to read the code you write. The cost of verbosity here is underestimated.
> Slightly more verbose sum type declaration is worth it when it makes using the cases cleaner.
C#/Java don't actually have sum-types. It's an incompatible formalism with their type systems.
Anyways, let's look at these examples:
C#:
public abstract record Shape;
public sealed record Circle(double Radius) : Shape;
public sealed record Rectangle(double Width, double Height) : Shape;
public sealed record Point() : Shape;
double Area(Shape shape) => shape switch
{
Circle c => Math.PI * c.Radius * c.Radius,
Rectangle r => r.Width * r.Height,
Point => 0.0,
_ => throw new ArgumentException("Unknown shape", nameof(shape))
};
ML: datatype shape =
Circle of real
| Rectangle of real * real
| Point
val result =
case shape of
Circle r => Math.pi * r * r
| Rectangle (w, h) => w * h
| Point => 0.0
They're pretty much the same outside of C#'s OOP quirkiness getting in it's own way.One thing I am wondering about in the age of LLMs is if we should all take a harder look at functional languages again. My thought is that if FP languages like OCaml / Haskell / etc. let us compress a lot of information into a small amount of text, then that's better for the context window.
Possibly we might be able to put much denser programs into the model and one-shot larger changes than is achievable in languages like Java / C# / Ruby / etc?
Granted, this may just be an argument for being more comfortable reading/writing code in a particular style, but even without the advantages of LLMs adoption of functional paradigms and tools has been a struggle.
And then I didn't even make this "experiment" with Java or another managed, more imperative language which could have shed some weight due to not caring about manual memory management.
So not sure how much truth is in there - I think it differs based on the given program: some lend itself better for an imperative style, others prefer a more functional one.
Aside from the obvious problem that there's not enough FP in the training corpus, it seems like terser languages don't work all that well with LLMs.
My guess is that verbosity actually helps the generation self-correct... if it predicts some "bad" tokens it can pivot more easily and still produce working code.
I’d believe that, but I haven’t tried enough yet. It seems to be doing quite well with jq. I wonder how its APL fares.
When Claude generates Haskell code, I constantly want to reduce it. Doing that is a very mechanical process; I wonder if giving an agent a linter would give better results than overloading it all to the LLM.
Claude Code’s Haskell style is very verbose; if-then-elsey, lots of nested case-ofs, do-blocks at multiple levels of intension, very little naming things at top-level.
Given a sample of a simple API client, and a request to do the same but for another API, it did very well.
I concluded that I just have more opinions about Haskell than Java or Rust. If it doesn’t look nice, why even bother with Haskell.
I reckon that you could seed it with style examples that take up very little context space. Also, remind it to not enable language pragmas per file when they’re already in .cabal, and similar.
Wanting to use a functional language I pivoted to fsharp, which was not the expected choice for me as I use Linux exclusively. I have been happy with this choice, it has even become my preferred language. The biggest problem for me was the management of the fsharp community, the second class citizen position of fsharp in the DotNet ecosystem, and Microsoft's action screwing the goodwill of the dev community (eg hot reload episode). I feel this hampered the growth of the fsharp community.
I'm now starting to use rust, and the contrast on these points couldn't be bigger.
Edit: downvoters, caring to share why? I thought sharing my experience would have been appreciated. Would like to know why I was wrong.
Use opam: https://opam.ocaml.org or https://opam.ocaml.org/doc/Install.html.
Additionally, see: https://ocaml.org/install#linux_mac_bsd and https://ocaml.org/docs/set-up-editor.
It is easy to set up with Emacs, for example. VSCodium has OCaml extension as well.
All you need for the OCaml compiler is opam, it handles all the packages and the compiler.
For your project, use dune: https://dune.readthedocs.io/en/stable/quick-start.html.
Can you be more specific?
(Why the down-vote? He does need to be specific, right? Additionally, my experiences are somehow invalid?)
RUN eval $(opam env) && opam install --yes dune=3.7.0
One day the build just randomly broke. Had to replace it with this: RUN eval $(opam env) && opam install --yes dune=3.19.1
Not a big change, but the fact that this happens at all is just another part of building with OCaml feeling like building on a foundation of sand. Modern languages have at least learned how to make things reproducible with e.g. lockfiles, but OCaml has not.Additionally, see: https://dune.readthedocs.io/en/stable/tutorials/dune-package....
Anyways, what you could do is:
opam pin add dune 3.7.0
opam install dune
Alternatively, you can use the tarball directly: opam pin add dune https://github.com/ocaml/dune/releases/download/3.7.0/dune-3.7.0.tbz
Nevertheless, I have fond memories of OCaml and a great amount of respect for the language design. Haven't checked on it since, probably should. I hope part of the problems have been solved.
Do you have a ballpark value of how much faster Rust is? Also I wonder if OxCaml will be roughly as fast with less effort.
Strong stance on Modules. My ignorance, what do they do that provides that much benefit. ??
Modules are like structurally-typed records that can contain both abstract types and values/functions dependent on those types; every implementation file is itself a module. When passed to functors (module-level functions), they allow you to parameterize large pieces of code, depending on multiple types and functions, all at once quite cleanly. And simply including them or narrowing their signatures is how one exports library APIs.
(The closest equivalent I can imagine to module signatures is Scala traits with abstract type members, but structurally-typed and every package is an instance.)
However, they are a bit too verbose for finer-grained generics. For example, a map with string keys needs `module String_map = Map.Make(String)`. There is limited support for passing modules as first-class values with less ceremony, hopefully with more on the way.
[1]: Practically speaking, the 31-bit Ints are annoying if you're trying to do any bit bashing, but aesthetically the double semicolons are an abomination and irk me far more.
Come on guys, this was old five years ago and its not organic, the strike force is quieter about brigading than it used to be, but you can still set your watch by it.
When I see Rust topping the "most loved language" on Stack Overflow etc. what I think is really happening is that people are using a "modern" language for the first time. I consistently see people gushing about, e.g., pattern matching in Rust. I agree pattern matching is awesome, but it is also not at all novel if you are a PL nerd. It's just that most commercial languages are crap from a PL nerd point of view.
So I think "no gc but memory safe" is what got people to look at Rust, but it's 1990s ML (ADTs, pattern matching, etc.) that keeps them there.
[1]: https://github.com/oxcaml/oxcaml
[2]: https://docs.scala-lang.org/scala3/reference/experimental/cc...
No way OCaml could have stolen the Rust's thunder: we have a number of very decent and performant GC-based languages, from Go to Haskell; we only had one bare-metal-worthy expressive language in 2010, C++, and it was pretty terrible (still is, but before C++11 and C++17 it was even more terrible).
I’ve always been curious about OCaml, especially since some people call it “Go with types” and I’m not a fan of writing Rust. But I’m still not sold on OCaml as a whole, its evangelists just don’t win me over the way the Erlang, Ruby, Rust, or Zig folks do. I just cant see the vision
But OCaml sadly can't replace F# for all my use cases. F# does get access to many performance-oriented features that the CLR supports and OCaml simply can't, such as value-types. Maybe OxCaml can fix that long term, but I'm currently missing a performant ML-like with a simple toolchain.
moi2388•2h ago
nukifw•1h ago
Yes, F# is a very nice language, however, it seems to me that I am making a somewhat forced comparison between OCaml and F# in the following section: https://xvw.lol/en/articles/why-ocaml.html#ocaml-and-f
Smaug123•1h ago
nukifw•1h ago
debugnik•1h ago
moi2388•43m ago
jimbob45•1h ago