OCaml for the Skeptical: OCaml in a Nutshell (2006)
www2.lib.uchicago.edu
www2.lib.uchicago.edu
If you're coming from Haskell or Scala though, OCaml's type system is going to come off as pretty primitive. And in many ways it is primitive, the idea of "type-level" programming isn't really a thing in the OCaml community.
My favorite part of OCaml though is that the compiler generates fast, native binaries, which in my opinion is reason enough to choose it over Scala or F#. The language is on par with F#, but notably less expressive than Scala.
But if you want a simple language that anyone can pick up quickly, one with a well defined performance profile and a practical, concrete ecosystem, OCaml might be the perfect choice. Just don't expect a Haskell or Scala like experience.
I would say, the core of OCaml is dead simple to learn. But the language and the libraries/tools that make it usable for real-world projects have evolved a lot. Some projects can be quite intimidating. Just like in C++, I think it takes some discipline from the programmers to keep things simple.
> The language is on par with F#,
It's on par in some ways, and fairly they're different in other ways. E.g. F# has reified generics while OCaml erases all types at runtime.
> notably less expressive than Scala.
Personally I find it super difficult to express a lot of things in Scala that I can bang out in a few simple lines of OCaml. One of the simplest possible things for an FP language, try writing a sum type that type-checks properly when you put it in a list. Scala:
scala> sealed trait PaymentMethod
object PaymentMethod {
case object Cash extends PaymentMethod
case class CreditCard(name: String, number: String, expiry: String) extends PaymentMethod
}
scala> Seq(PaymentMethod.Cash, PaymentMethod.CreditCard(name = "Bob", number = "1111111111111111", expiry = "10/21"))
res0: Seq[Product with PaymentMethod with java.io.Serializable] = ...
OCaml: # module PaymentMethod = struct
type t =
| Cash
| CreditCard of { name : string; number : string; expiry : string }
end;;
# [Cash; PaymentMethod.CreditCard { name = "Bob"; number = "1111111111111111"; expiry = "10/21" }];;
- : PaymentMethod.t list = ... enum PaymentMethod
case Cash
case CreditCard(name: String, number: String, expiry: String)
Seq(Cash, CreditCard("Bob", "1111111111111111", "10/21"))I'd say Standard ML better fits the bill of "dead simple". The only issue is that tooling and learning resources are nearly nonexistent.
Care to elaborate? What are you referring to?
- First-class modules don't feel very first class, with how clunky it is to use modules at the value level
- Records are very clunky and hacky to use
- Generative functors are a hack
- Special language-level equality magic is awful (although SML also has this problem)
- Special `let` forms (whether via ppx or integrated into the language) feel really hacky
- GADTs require too much handholding of the typechecker
- All the "Objective" parts have very few redeeming qualities and should probably never have existed
- The way infix operators work is really half-assed
I don't think OCaml is a _bad_ language - it's very practically useful. It's just not simple or elegant.
By the way, the Successor ML wiki says[1]:
> Standard ML, being incapable of evolution, is dead. The purpose of successor ML, or sML for short, is to provide a vehicle for the continued evolution of ML, using Standard ML as a starting point. The intention is for successor ML to be a living, evolving dialect of ML that is responsive to community needs and advances in language design, implementation, and semantics.
Again, I stand by my statement that Standard ML is dead.
[1] https://smlfamily.github.io/successor-ml/OldSuccessorMLWiki/...
(1) Successor ML is happening,
(2) Standard ML is alive and evolving,
(3) SML/NJ isn’t just an implementation but a distinct language which is a successor to Standard ML and which incorporates what was described as “Successor ML”.
The reason why a lot of people write C++, for instance, is not because they like it or think it's a particularly good language, but it's because an amazing amount of (for instance) graphics and maths libraries are written with the C++ developer in mind.
It seems to me that the pain of working with a language you don't particularly like is way less than the pain of working with no libraries, or doing awkward FFI everywhere.
Is the edit/compile/run cycle faster than F#? If either OCaml or F# produce a sufficiently fast program which compiles faster?
Also how seamless is multithreaded operations compared to F#? I have some limited experience with F# (on Mac) and the biggest drawback was getting the environment correctly set up after which most everything seemed very natural.
There is a GIL (like Node/Python) that means only one thread runs at any moment in time, but multiple threads can still be started. Lwt's Promise abstraction is very similar to Node's, except it's at the library level, not at the runtime level.
Isn't F# an Ocaml derivative?
"The early conception of F# was simple: to bring the benefits of OCaml to .NET and .NET to OCaml: a marriage between strongly typed functional programming and .NET. Here 'OCaml' meant both the core of the language itself, and the pragmatic approach to strongly-typed functional programming it represented. The initial task was relatively well-defined: the author would re-implement the core of the OCaml language and a portion of its base library to target the .NET Common Language Runtime. The implementation would be fresh, i.e. not using any of the OCaml codebase, for legal clarity. The first lines of the F# implementation were written in December 2001, a front-end for a re-implementation of the core Caml syntax targeting ILX as a back end, and thus to .NET. The initial compiler was written using OCaml."
The initial version of F# was very much OCaml without functorial modules or object extensions.
[1] https://www.microsoft.com/en-us/research/project/f-at-micros...
https://discuss.ocaml.org/t/significant-performance-differen...
From my point of view, Ocaml is a lot nicer than Haskell (let's not talk about Scala).
Why would you assume that I'm not aware of a core feature of OCaml? Why would I even post about OCaml if I knew nothing (or hadn't written production code in it) about it?
But that's not why I care about functors. I use functors to package a bunch of types and values in a way that depends on a package of other types and values, which was their purpose from the beginning. In Haskell, I often want to do just this, but I find typeclasses (and associated types etc) to be a poor substitute for functors.
For this reason, I keep an eye on backpack.
> I'm very familar with the OCaml module system
These two statements don't seem compatible to me.
> functors are a poor substitute for typeclasses, atleast without modular implicits.
First, there are more than functors to Ocaml module system. First-class modules are a thing.
Then, how are functors a poor substitute to type class ? Modular implicits is mostly sugar to avoid explicitly mentioning modules. That doesn't directly add anything to Ocaml capabilities.
Like we can't have real monads in OCaml, because no HKT. And the module system isn't a solution. We have to be careful to import the right stuff, because >> and >>= exist for each separate monad. So now we have to write code that's not general, not polymorphic, and just ugly.
Haskell is a more powerful language than OCaml, full stop. OCaml programmers always talk about the module system, but it's honestly not very impressive. Typeclasses are a much more elegant solution.
I want to just be able to fmap things, and bind things, I don't want to have to think about the concrete types I'm working with. Also the lack of function polymorphism is super shitty.
And I think most agree that using different functions for arithmetic on floating points is stupid, inelegant, and a waste of time.
Modular implicit could solve this problem, and allow typeclasses like programming in OCaml.
First-class modules.
The points that you're bringing up are points that 99% of programmers don't care about. You're fighting about OCaml vs Haskell features but the really is that most people looking at alternatives in this niche are just going to go with Go or Rust (neither of which has higher-kinded types).
The reality is that OCaml offers a very simple and elegant base language, a powerful module system, a limited amount of type-level programming support (locally abstract types, GADTs, etc.), and probably the biggest draw of all: super-fast compile times. Seriously, no matter how impressive the Haskell compiler is–ocamlc and ocamlopt and BuckleScript all run rings around every variant of Haskell compiler.
You are criticizing OCaml on criteria that it honestly did not choose to optimize in. Would you criticize Haskell on its lack of a powerful module system, on its lack of strictness by default, on its complex runtime, on its confusing error messages that arise from ad-hoc polymorphism, and on its slow compiles? Sure you could, but it didn't choose to optimize for those. It doesn't make sense to fixate on things that were never a goal in the first place. If those things are your goals then you would simply find a compiler that offers them.
> First-class modules.
I think smabie might have meant "In the first place, module and higher-order modules ...".
[1] https://www.cs.cornell.edu/courses/cs3110/2019sp/textbook/in...
I'm interested in building web apps, web servers, and simple WebSocket servers. OCaml appeals to me because it has more escape hatches than Haskell and is higher level than Rust, but I've always found the ReasonML vs OCaml story confusing.
I don't think js_of_ocaml supports any kind of JSX. That's the only real reason to use Reason over OCaml that I know of, and it's a good one. But if I'm being frank, even if OCaml/Reason is a nicer language overall, TypeScript is a statically-typed frontend development story that has way more community buy-in and way fewer rough edges in the ecosystem. Just use it.
The recent syntax switch[1] does feel like it will further fragment the community.
And TypeScript has some pretty attractive functional projects:
https://github.com/gcanti/fp-ts
From what I have seen, there seems to be little buy-in for Reason in the Ocaml community which is not particularly surprising considering it's supposed to solve a pain point no one in the community actually has (Ocaml syntax is fine - it's obviously more Algol-like than C-like but clearly the existing community doesn't care).
Reason seems to be mostly pushed by Facebook towards Javascript developers. That might work. Then again, esy is nice.
For frontend development BuckleScript is probably better than js_of_ocaml in terms of integration with JS, bundle size and number of users. For backend development I personally think native OCaml (or Reason if you prefer it) is definitely usable. But you could also try BuckleScript bindings to node stuff if you really like node.
- JSX. OCaml has infamously never had a great UI lib, even though the language parallels with many of the functional paradigms popularized by React. JSX is a great medium to bridge that gap.
- The syntax itself. A lot of the contributors to Revery have no OCaml background, but rather JS since it's often billed as an Electron alternative. Reason's familiar syntax opens the door to many people who would be turned off by OCaml's "obscure" (not my actual thoughts) syntax.
- The tooling. Since it's just an alternative syntax for OCaml, you get all the OCaml tooling for free, plus the Reason-specific tooling. I'm a huge fan of refmt, which is a tool that parses and formats Reason source files. Sure other langs have close-ish equivalents (i.e. clang-format), but refmt is much more opinionated, meaning you never have to worry about intricacies with your formatting.
- It gets rid of syntax ambiguities like the 'dangling else' problem
- It introduces conveniences like line comments and JSX
- The tooling includes Esy, which offers an npm-like package management experience including lockfiles and build artifact caching for faster builds across projects
- The community is nice, close-knit, and helpful.
I finally just gave up.
The suggested tools are also out of date. If you want to try OCaml nowadays you should probably install it with opam and use dune as a build system.
The only reason I could think of was being able to compile to JS, but other than that Rust seems to be a superior choice for all the use cases I could have used practically used OCaml for.
Unless the project absolutely couldn't tolerate GC, why would I choose a low level language like Rust over a high level language like Ocaml?
Rust is a low level systems language, where you care about memory. You may not be malloc’ing, but you’re borrowing.
Conversely OCaml is an interpreted functional language. It has a much richer and more concise syntax than rust.
You can’t define operators in rust. That means no ‘|>’, no parser combinators. I doubt I’ll ever write a parser in a non-ML language, after my experiences with FParsec.
Rust can’t do partial application properly. I can’t compose functions in rust.
Don’t get me wrong though - Rust is a fantastic language and miles ahead of similar languages in terms of functional features. The only popular C-like language that’s catching up is C#, but discriminated unions aren’t due until next autumn.
Rust is better for manipulating a computer, OCaml is better for math-ey things such as parsers.
You should try OCaml, even if you don’t use it you’ll learn about programming from it.
Correction: OCaml is not an interpreted language. OCaml programmes are compiled down to native code (or bytecode, or Javascript).
In fact I would argue that rust is much more suited to write low level things like operating systems and unikernels than ocaml. Having said that mirage is very cool. It is a unikernel and it has garbage collection. So it’s easy to write robust and secure services! Yeah anything written in rust will probably be faster but then unless you’re a mega company and have mega size deployments this should not really matter to you. Mirage should serve your needs well if you need unikernels
2. Compilation times