I'm a big fan of F# as it has a huge company and ecosystem behind it. That said OCaml is where most of the language ideas came from. Much of what makes F# is owed to OCaml.
> F# as it has a huge company and ecosystem behind it
Indeed, F# has the might of Microsoft behind it. Yet, to me, that carries some risk in itself. In the future, Microsoft might decide for some reason that F# is not aligned to its goals and say, it wants to invest in C# exclusively or decrease emphasis on F#. Then what happens?
OCaml is more resilient in a sense it is more "community" owned and run. Companies (contributing to OCaml) keep coming to (or leaving) OCaml. F# has all its eggs with Microsoft.
True. I really appreciate Don Syme for not adding too much to F#. It's a beautiful precise language as it is.
I really don't like what has happened to C# is the past few years where features are just added blindly it seems. Current C# looks nothing like C# code from a few years ago and that's bad IMO.
Example? The biggest addition to C# recently is non-nullable references, which was long overdue even though I haven't had a proper opportunity to use yet, but they don't even change the syntax of the language.
You missed the pattern matching, default interface methods, records (class and struct based), and all the low level features to be able to write C++ like code in C#, code generators, global usings, implicit main class, static lambdas, abstract interface methods for operators,....
On the horizon, discriminated unions are still being talked about, and more C++ performance like features.
I don't work in C#, but I must say, these seem like great features to me, and not too crufty/bloaty at all.
Scrolling through the other recent feature additions, they all look like things that would make a language feel smoother rather than more noisy. But again, IANAC#D.
Might be just me, but in larger teams it's annoying because not everyone knows the most recent features.
I wrote C# daily before I switched to F# ~3 years ago and I sometimes struggle reading "modern" C# code. In C# you now often have multiple ways of doing the same thing.
Pattern Matching was 5 years ago (and I use it all the time, it's great).
I have to admit I've not come across many C# 9.0 features in the wild yet, and I might double take if I saw them used liberally throughout a codebase. The null-coalescing assignment I'm a bit skeptical about the value of, or at least, it doesn't fit with my preferred style of coding (sticking to non-nullable types where possible, and generally avoiding variable reassignment).
I think the community plays a more important role in what they want for F#.
First there was a focus for Fable and targeting frontend, and now you can target Python and potentially use F# there instead of Python.
But the community is interested in a lot of things, so F# doesn't have a real "singular" focus, like Go(backend systems/api's).
F#'s ties with Microsoft is a big productivity boon, but also seems to work against it sometimes...still the community really gets stuff done instead, because it's such a practical language!
Sometimes I get the feeling management is not so sure that accepting the F# integration into VS 2010 was such a good idea, and every now and then, tries to figure out between C#, VB, C++/CLI, what are they supposed to do with F# as well.
MS management seems like a wild place to me, but I only have experience with the OSS side. Between X amount of GUI frameworks, adding every feature under the sun, OSS controversies, quite unpredictable at times...
Let suppose someone wants to port a OCaml project to F# in a linux environment. How much work would it take in term of code rewriting? can it compile to native code? how good is the language support experience in vscode? any reason not to do it?
They are very close. I think a rewrite wouldn't be too crazy. But there might be libraries available only in one or the other.
Also F# is .NET based, which has the clear benefit of adding a huge standard library, but at the same time the OOP-based design of .NET makes for uglier than necessary code in F#.
Oh so they're finally releasing a "true" AOT. It was advertised to have been released several years ago but they have continuously pushed back the date. Good to see they're finally delivering it.
Also great to see a fellow HNer who uses the `service-name@personal-domain` email scheme, as per your profile!
It pains me to admit it because I really like F# but, with due respect to the developers, Ionide and its related projects are the most unstable toolchain I've ever used.
Spend half a day reloading the editor because the extension keeps hanging on non-trivial MSBuild only to discover that the formatter has truncated in half one of the files you worked on due to a soundness bug. (OCaml's editor support, in contrast, is quite stable.)
Rider is the best editing experience I've had with F#, by far.
I was recently excited to try F# and prototype a web service within falco. Ionide worked until it didn’t in between switching from .net 6 to 7. Built fine but ide errors everywhere about missing modules
> How much work would it take in term of code rewriting?
Probably a lot. Most of the operations on basic types like strings rely on C# methods rather than being implemented in the corresponding module like in OCaml (e.g. `String.split_on_char ' ' "a b c"` in OCaml vs `"a b c".Split(' ')` in F#). Type annotations on function parameters are required much more often because the compiler can't infer object types from a method call. Obviously you'll have very different libraries for doing the same things, and you'll be interacting with C# libraries a lot. Some language features like objects or binding operators exist in both languages but have very different syntax and differing semantics, and others like local opens and functors just don't exist in F#. F# also defaults to a "light" indentation-based syntax and I think the old OCaml-like syntax is either unsupported or hidden behind a compiler directive, and you'll probably want to rename identifiers from snake_case to camelCase.
> how good is the language support experience in vscode?
There's two competing LSP implementations for F#. Ionide works, but it's slow and it crashes and reports old errors for code you've just rewritten very often in my experience. fsharp-language-server seems to be a bit better in some aspects, but I haven't ever gotten it to work in VS Code.
It probably took longer than it had to: 30% of that time was devops, I wanted it to be bug-for-bug compatible so I spent a lot of time writing tests, and we were using some stuff which wasn't mature yet (new JSON serialization lib, compile to webassembly, binary serializers)
F#/.NET certainly has everything needed, and I find it a much simpler language than OCaml (eg no module games) - really it's what I hoped OCaml would be.
OCaml is one of those languages that has it's own compiler backend. I might have forgotten but I think it even uses it's own linker. It is also backed by organisations, for example people from INRIA university and Jane Street company constantly improve it. I remember it also compiled small programs in a few milliseconds, something LLVM based languages cannot do.
OCaml is an impressive language on many fronts: compile time, speed, memory usage, but then is almost crippled beyond usage by a few unfortunate decisions / (lack of) features:
- Dependency management, despite progress `opam` is hopeless brittle because no one uses lock files, good luck building a project that's more than 3 months old. Additionally, by default everything is installed into a global 'switch' and only one version of each package can exist in it.
- Build tooling: things are finally improving with `dune`, but the amount of packages that are running hyper-complex Makefile based build systems is incredibly frustrating. It can be very difficult to add a dependency or change the layout of these projects if you aren't an expert in arcane Make sigils.
- Error messages: again, finally improving, but the number of times I've gotten errors like "line 78 Syntax Error"... I know that parser generators are appealing academically / intellectually but there's a reason major industrial compilers use recursive descent.
- Documentation: Seemingly non-existent for most packages and the tooling is quite poor compared to peer languages (Haskell, Rust).
- More controversially, I think that modules are a worse solution in practice than typeclasses / traits. The automatic instance resolution coherence brings is a huge ergonomics win, and it also allows for more ecosystem wide collaboration in practice. I know that modular implicits are supposed to bring the 'best of both' but that doesn't even exist yet.
None of the problems above are unfixable but they collaborate to make working in OCaml a very frustrating experience for me, even worse because through them I can see what is actually a quite nice language.
Re: dependency management and build tooling: these days I'm using opam-monorepo (https://github.com/tarides/opam-monorepo, previously known as duniverse) to manage dependencies and it's working out pretty well for me. It vendors all dependencies into a node_modules-like directory inside the projects and writes an opam-compatible lockfile, and it builds all the dependencies using dune. For the most widely used packages that don't already use dune it uses an overlay containing dune ports at https://github.com/dune-universe/opam-overlays.
I have been an Ocaml user for a decade at this point and using Ocaml in the tech stack for the business I'm building. In my opinion:
- Dependency management is not great in opam. I find it especially frustrating that the tried to be really clever with a SAT solver and it rarely does what I want it to do. That being said, I rarely run into issues with old builds of something failing. I only have a few projects on opam and I will get PRs for fixing my project to support later builds. The bigger issue, IMO, is there is no great way to mirror all the deps your project needs locally so you don't have to worry about them disappearing.
- I have never run into the issue you've described here. In general, a package on opam just builds. I rarely have to modify an existing package, but when I do it has not been an issue.
- Error messages aren't great, but I have never really hit this Syntax Error problem you run into (maybe you are one of those people that uses ";;"?). Error messages have also improved a lot over the last few versions, even around polymorphic variants, which has been a much welcomed improvement.
- Documentation of third party libraries is unequally distributed. I find the language and std lib well documented, but for a library it depends. There is lots of room for improvement here. But on the flip side: I've run into plenty of libraries in Rust with bad documentation as well.
- I think this comment on modules is a big unfair. Modules were never designed to solve the problem of typeclasses or traits. As you mentioned, module implicits might allow modules to be used as a solution, though. But on the flip side, type classes and traits struggle to express what modules can express, and I dearly miss modules when I work in any other language.
As someone who has dabbled in Ocaml but has more experience with Haskell typeclasses and Rust traits, is there a good primer on what Ocaml modules are and how they're supposed to be used? Just porting my previous assumptions on traits/typeclasses over to Ocaml hasn't led me to very satisfying results.
I'm not aware of any particular document, know. I have just read the language spec for modules. I know that sounds scary but, IMO, it's very approachable. And then, I think you just need to play with it to get a feel for me. But maybe someone else has a better introduction. But I think typeclasses and traits are about polymorphism, and that isn't what modules are about, they are about encapsulating code.
> More controversially, I think that modules are a worse solution in practice than typeclasses / traits.
If by controversial, you mean wrong we are in agreement. Modules are both conceptually simpler and easier to use in practice. They also produce far better error messages.
I personally find Ocaml tooling far better than Haskell. The debugger is a lot nicer. The compiler is considerably faster, doesn’t rely as much on extension and generally gives better error messages. VSCode integration does everything I want and is easy to install.
For what it’s worth you are the first person I meet complaining about syntax error. What people complain about are type checking errors which are indeed hard to understand sometimes. Syntax errors are caught well by the parser which tells you exactly where they are. I don’t really understand what you are complaining about.
I'll try to find an example of the syntax error I was mentioning, but I regularly trip up the parser with dumb mistakes which cause it to give completely useless errors.
> Syntax errors are caught well by the parser which tells you exactly where they are.
They aren't necessarily caused where the actual error is, just wherever the parser gets stuck, and the error it produces is usually not reflective of the human thought process that went in, but rather the automata's state.
Would be interesting to see such an error, but also what's interesting to me is that you are regularly making 'dumb' syntax mistakes, which suggests to me that you're still in the learning process and that you'll internalize the syntax more fully and stop making those mistakes some time in the future.
Not really, it's more that I regularly make typos in all forms of writing (even handwritten). It's not usually misspellings but rather whole lexemes that go missing or are swapped.
fwiw, I TA a course taught (partially) in OCaml and work regularly with a large OCaml project as part of my thesis.
Parser generator errors are just a personal pet peeve of mine, I grew up with (mega)parsec (Haskell), where decent errors are out-of-the-box and great ones are easy.
Partially agree here with the syntax error thread in that it’s mildly annoying to lose intellisense when you’re declaring a new variable before the “in” keyword is in place. But that’s about the only time I commonly see syntax errors.
While you're not wrong about docs, the new package documentation for every package in the ecosystem is pretty fantastic! https://www.ocaml.org/packages
50 comments
[ 5.3 ms ] story [ 107 ms ] threadFor us the tooling + library ecosystem are more important to get sh* done.
https://v2.ocaml.org/manual/polyvariant.html
Indeed, F# has the might of Microsoft behind it. Yet, to me, that carries some risk in itself. In the future, Microsoft might decide for some reason that F# is not aligned to its goals and say, it wants to invest in C# exclusively or decrease emphasis on F#. Then what happens?
OCaml is more resilient in a sense it is more "community" owned and run. Companies (contributing to OCaml) keep coming to (or leaving) OCaml. F# has all its eggs with Microsoft.
Latest pivot is to try go heads up against Julia into Python's domain.
You already know which one data scientists will pick up.
I really don't like what has happened to C# is the past few years where features are just added blindly it seems. Current C# looks nothing like C# code from a few years ago and that's bad IMO.
On the horizon, discriminated unions are still being talked about, and more C++ performance like features.
Pattern Matching
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/cs...
Non destructive mutation
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/cs...
Null-coalescing assignment
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/cs...
I don't work in C#, but I must say, these seem like great features to me, and not too crufty/bloaty at all.
Scrolling through the other recent feature additions, they all look like things that would make a language feel smoother rather than more noisy. But again, IANAC#D.
I wrote C# daily before I switched to F# ~3 years ago and I sometimes struggle reading "modern" C# code. In C# you now often have multiple ways of doing the same thing.
But the community is interested in a lot of things, so F# doesn't have a real "singular" focus, like Go(backend systems/api's).
F#'s ties with Microsoft is a big productivity boon, but also seems to work against it sometimes...still the community really gets stuff done instead, because it's such a practical language!
Also F# is .NET based, which has the clear benefit of adding a huge standard library, but at the same time the OOP-based design of .NET makes for uglier than necessary code in F#.
> How much work would it take in term of code rewriting?
There are definitely code changes required, but I think those are quite manageable as concepts mostly map 1:1 from OCaml to F#.
> can it compile to native code?
Yup, https://learn.microsoft.com/en-us/dotnet/core/deploying/nati...
> how good is the language support experience in vscode?
Pretty good, https://ionide.io
(but I personally prefer JetBrains Rider)
> any reason not to do it?
Compilation speed, some OCaml language features?
Also great to see a fellow HNer who uses the `service-name@personal-domain` email scheme, as per your profile!
It pains me to admit it because I really like F# but, with due respect to the developers, Ionide and its related projects are the most unstable toolchain I've ever used.
Spend half a day reloading the editor because the extension keeps hanging on non-trivial MSBuild only to discover that the formatter has truncated in half one of the files you worked on due to a soundness bug. (OCaml's editor support, in contrast, is quite stable.)
Rider is the best editing experience I've had with F#, by far.
I recommend Rider.
I was recently excited to try F# and prototype a web service within falco. Ionide worked until it didn’t in between switching from .net 6 to 7. Built fine but ide errors everywhere about missing modules
Probably a lot. Most of the operations on basic types like strings rely on C# methods rather than being implemented in the corresponding module like in OCaml (e.g. `String.split_on_char ' ' "a b c"` in OCaml vs `"a b c".Split(' ')` in F#). Type annotations on function parameters are required much more often because the compiler can't infer object types from a method call. Obviously you'll have very different libraries for doing the same things, and you'll be interacting with C# libraries a lot. Some language features like objects or binding operators exist in both languages but have very different syntax and differing semantics, and others like local opens and functors just don't exist in F#. F# also defaults to a "light" indentation-based syntax and I think the old OCaml-like syntax is either unsupported or hidden behind a compiler directive, and you'll probably want to rename identifiers from snake_case to camelCase.
> how good is the language support experience in vscode?
There's two competing LSP implementations for F#. Ionide works, but it's slow and it crashes and reports old errors for code you've just rewritten very often in my experience. fsharp-language-server seems to be a bit better in some aspects, but I haven't ever gotten it to work in VS Code.
It probably took longer than it had to: 30% of that time was devops, I wanted it to be bug-for-bug compatible so I spent a lot of time writing tests, and we were using some stuff which wasn't mature yet (new JSON serialization lib, compile to webassembly, binary serializers)
F#/.NET certainly has everything needed, and I find it a much simpler language than OCaml (eg no module games) - really it's what I hoped OCaml would be.
However LLVM contribution level is at the same range as Linux kernel contributions, so it will be quite hard to change course.
tiny nit, but INRIA is not a university but rather a national network of labs.
https://github.com/ocaml-multicore/eio#fibers
- Dependency management, despite progress `opam` is hopeless brittle because no one uses lock files, good luck building a project that's more than 3 months old. Additionally, by default everything is installed into a global 'switch' and only one version of each package can exist in it.
- Build tooling: things are finally improving with `dune`, but the amount of packages that are running hyper-complex Makefile based build systems is incredibly frustrating. It can be very difficult to add a dependency or change the layout of these projects if you aren't an expert in arcane Make sigils.
- Error messages: again, finally improving, but the number of times I've gotten errors like "line 78 Syntax Error"... I know that parser generators are appealing academically / intellectually but there's a reason major industrial compilers use recursive descent.
- Documentation: Seemingly non-existent for most packages and the tooling is quite poor compared to peer languages (Haskell, Rust).
- More controversially, I think that modules are a worse solution in practice than typeclasses / traits. The automatic instance resolution coherence brings is a huge ergonomics win, and it also allows for more ecosystem wide collaboration in practice. I know that modular implicits are supposed to bring the 'best of both' but that doesn't even exist yet.
None of the problems above are unfixable but they collaborate to make working in OCaml a very frustrating experience for me, even worse because through them I can see what is actually a quite nice language.
- Dependency management is not great in opam. I find it especially frustrating that the tried to be really clever with a SAT solver and it rarely does what I want it to do. That being said, I rarely run into issues with old builds of something failing. I only have a few projects on opam and I will get PRs for fixing my project to support later builds. The bigger issue, IMO, is there is no great way to mirror all the deps your project needs locally so you don't have to worry about them disappearing.
- I have never run into the issue you've described here. In general, a package on opam just builds. I rarely have to modify an existing package, but when I do it has not been an issue.
- Error messages aren't great, but I have never really hit this Syntax Error problem you run into (maybe you are one of those people that uses ";;"?). Error messages have also improved a lot over the last few versions, even around polymorphic variants, which has been a much welcomed improvement.
- Documentation of third party libraries is unequally distributed. I find the language and std lib well documented, but for a library it depends. There is lots of room for improvement here. But on the flip side: I've run into plenty of libraries in Rust with bad documentation as well.
- I think this comment on modules is a big unfair. Modules were never designed to solve the problem of typeclasses or traits. As you mentioned, module implicits might allow modules to be used as a solution, though. But on the flip side, type classes and traits struggle to express what modules can express, and I dearly miss modules when I work in any other language.
I agree: Ocaml is a very nice language.
https://v2.ocaml.org/releases/4.14/htmlman/modtypes.html
https://v2.ocaml.org/releases/4.14/htmlman/modules.html
https://v2.ocaml.org/releases/4.14/htmlman/firstclassmodules...
If by controversial, you mean wrong we are in agreement. Modules are both conceptually simpler and easier to use in practice. They also produce far better error messages.
I personally find Ocaml tooling far better than Haskell. The debugger is a lot nicer. The compiler is considerably faster, doesn’t rely as much on extension and generally gives better error messages. VSCode integration does everything I want and is easy to install.
For what it’s worth you are the first person I meet complaining about syntax error. What people complain about are type checking errors which are indeed hard to understand sometimes. Syntax errors are caught well by the parser which tells you exactly where they are. I don’t really understand what you are complaining about.
> Syntax errors are caught well by the parser which tells you exactly where they are.
They aren't necessarily caused where the actual error is, just wherever the parser gets stuck, and the error it produces is usually not reflective of the human thought process that went in, but rather the automata's state.
fwiw, I TA a course taught (partially) in OCaml and work regularly with a large OCaml project as part of my thesis.
Parser generator errors are just a personal pet peeve of mine, I grew up with (mega)parsec (Haskell), where decent errors are out-of-the-box and great ones are easy.