Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
OCaml for the Skeptical: OCaml in a Nutshell (2006) (uchicago.edu)
108 points by ColinWright on Aug 2, 2020 | hide | past | favorite | 69 comments


I like Ocaml and it would be a good choice of language for most kinds of projects, especially if good performance is desired. OCaml is dead simple to learn (I always recommend it as the first FP language they should learn) and eminently practical.

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.


> OCaml is dead simple to learn

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.


I would correct a couple of things here.

> 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 = ...


This is true, but Scala 3 solves this with enums. And with the new optional brace syntax, Scala is starting to look more like OCaml every day. In Scala 3:

   enum PaymentMethod
     case Cash
     case CreditCard(name: String, number: String, expiry: String)

   Seq(Cash, CreditCard("Bob", "1111111111111111", "10/21"))


Java is slowly getting good enough for those of us that have to deal with mob development while wanting to enjoy a little bit of FP programming in the process, if the optional brace syntax goes through, that might that be the final straw for those that haven't yet moved back to Java or started to find new loves like Kotlin.


That's unfortunately another one of Scala's cons, there will be a large breaking change in a few years and the ecosystem will be split again.


I don't know if I'd call OCaml dead simple. It's full of inconsistencies and bolted-on features that are practically useful but definitely not simple or elegantly designed. It's kind of like the C++ of ML derivatives in that sense.

I'd say Standard ML better fits the bill of "dead simple". The only issue is that tooling and learning resources are nearly nonexistent.


>full of inconsistencies and bolted-on features that are practically useful but definitely not simple or elegantly designed

Care to elaborate? What are you referring to?


Just some examples:

- 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.


Words like "clunky" and "hacky" are evaluation judgments, not objective criticism.


The main issue with Standard ML is that it's a dead language–it can't evolve beyond its current state. OCaml by comparison is steadily evolving.


That's effectively not true[1].

[1]: https://github.com/SMLFamily/Successor-ML


That repo is a bunch of TeX files (not even an implementation) that haven't been touched since 2018. I stand by my statement.

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/...


Let's be real - Successor ML is never happening. I just don't think there's enough interest.


SML/NJ, as I understand it, is implementing Successor ML features, so one of three things is true:

(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”.


Yup. Realistically, the successor of ML is in fact OCaml.


I liked Ocaml last time I had a go, but after getting to the point I could fumble my way around (can write a toy ray-tracer level proficiency) I couldn't really work out what I was supposed to do with it.

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.


What libraries did you feel were missing? I would say that OCaml has a pretty respectable ecosystem.


OCaml has a nice amount of system programming libraries, thanks to mirage project and jane street. It's a breeze to make system daemons with it for the most part, and compilers of course.


> compiler generates fast [...] reason enough to choose it over Scala or F#

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.


OCaml's compile speed is very very fast. I think it's actually quite a bit faster than Go's compile speed.


I've never used F# in any serious capacity, but OCaml compilation time is really fast. Ocaml multicore is going to drop soon, so mainline OCaml should finally have a real concurrency story soon. The effects system looks pretty interesting.


It has concurrency allready, LWT and a solution from Jane Street. Parallelism is dropping soon.


OCaml has really good libraries for concurrency–e.g. check out https://github.com/ocsigen/lwt

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.


> The language is on par with F#,

Isn't F# an Ocaml derivative?


I wouldn't call F# an OCaml derivative, but rather they are both ML derivatives.


It's more fair to say that F# originated as a heavily OCaml-influenced Caml implementation. It was originally called Caml.net [1], after all:

"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://fsharp.org/history/hopl-draft-1.pdf


F# literally started as a Caml port to .Net by Don Syme. It's hard to be more derivative than that. It's right there in the historical acknowledgements part of the F# website [1].

[1] https://www.microsoft.com/en-us/research/project/f-at-micros...


The performance of F# and OCaml would be interesting to compare in depth. I imagine F# has a higher performance ceiling these days, with .NET Core 3.1 and higher, but what about idiomatic code? Not so sure, since F# has some overhead in it's sequences and a few other things.


There is was post about this not so long ago

https://discuss.ocaml.org/t/significant-performance-differen...


From your post, I gather you are not very familiar with Ocaml module system. It's both where type-level programming happens and what F# doesn't have. You might be confusing your lack of knowledge of the languages with lack of the language.

From my point of view, Ocaml is a lot nicer than Haskell (let's not talk about Scala).


I'm very familar with the OCaml module system. If modular implicits were a thing, I'd say OCaml would be great. But functors are a poor substitute for typeclasses, atleast without modular implicits.

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?


Functors are indeed a poor substitute for typeclasses if I want ad-hoc polymorphism. If I want ad-hoc polymorphism, I want typeclasses. I find Scala's implicits to be a poor substitute (though not as poor as functors), and I'm sceptical that modular implicits are going to compare to typeclasses.

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.


> the idea of "type-level" programming isn't really a thing in the OCaml community.

> 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.


First place modules and higher order modules are a hack to get around the limited polymorphism that OCaml provides. Like, just compare OCaml code to Haskell code. Haskell is way more concise, way more powerful, and less messy.

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 place modules

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 place modules

> First-class modules.

I think smabie might have meant "In the first place, module and higher-order modules ...".


And as much as I like both, at the end of the day I will be delivering projects in Java, C# or C++, because costumers don't care how their pizza selling app looks like inside.


This [1] is a a much more detailed course that gives a great introduction to OCaml.

[1] https://www.cs.cornell.edu/courses/cs3110/2019sp/textbook/in...


There is also https://www.cs.cornell.edu/courses/cs3110/2019fa/textbook/. Yours is 2019 Spring, this is 2019 Fall. This version covers many more topics.


Is there a good reason to learn ReasonML instead of OCaml these days, or should I just learn OCaml + js_of_ocaml?

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'm one of the weirdos who likes OCaml syntax, but in my eyes ReasonML is unfortunate. It fractures the community for (ironically) no real reason - not only is it a new syntax, they're now writing a new standard library for it (Belt), as though the three existing ones (plus the JS one!) weren't enough! I don't think the new syntax is particularly nice either.

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.


Belt isn't a standard library for Reason, it's a standard library for Bucklescript that has specific optimizations since it knows it's going to be compiled to JS. Reason uses the same Pervasives/Stdlib as OCaml.


That's helpful, thanks.

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

https://github.com/Matechs-Garage/matechs-effect

[1]: https://reasonml.org/blog/bucklescript-8-1-new-syntax


There isn't much to fragment to be honest.

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.


The situation with Reason is (arguably needlessly) confusing. Reason is just an alternative syntax for OCaml, despite being often advertised in combination with BuckleScript (the more recent OCaml to JS compiler). You can use either Reason or OCaml syntax with either BuckleScript, js_of_ocaml or natively (very confusingly there's also a new slightly different version of Reason for BuckleScript only which you can ignore). The advantages of Reason syntax over OCaml are pretty much limited to a nice JSX syntax.

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.


I don't really think ReasonML provides much value, and would personally stay away from it.


If you think of ReasonML as a separate language (as I find it all too often is described), then it doesn't provide any extra value. However, as someone who is a contributor to Revery, a fairly large Reason project, the benefits of using Reason over OCaml are:

- 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.


ReasonML is a syntax and a set of tools. If you prefer the OCaml syntax, by all means use that. If you prefer the standard OCaml tooling, by all means use those. A few reasons to look at Reason might be:

- 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.


Wake me up when OCaml get multi-core support.



They're definitely making progress lately. For a few years there it seemed like it would never happen, but now I'm starting to have some hope.


What will you use multicore support for?


I tried to use OCaml once. My main difficulty was the compiler error messages were very primitive, and I had an awful time figuring out what was wrong with the code I was writing.

I finally just gave up.


Ah that's too bad, it really does offer an elegant and efficient programming stack. Admittedly the syntax errors are historically bad but the editor support tool, Merlin, offers more help with those, with messages like "Syntax error after ... expecting `=' "


Last updated in 2006.


Yes, it has a very 2006-era "OCaml as an alternative to C/C++" attitude, hence the claims that OCaml has an extensive library ecosystem and developer tooling: compared to C this is true but not compared to Python/Java in 2020.

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.


Curious question: If you are picking OCaml over Rust in 2020 for a new project, what is your reason for doing so (other than being already familiar with OCaml through college courses, or wanting to try out something new for fun) ?

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.


I've never really seen them as comparable. But I would choose Ocaml over Rust because OCaml is easier, less complex, and more expressive. Also, Rust doesn't support functional programming. OCaml's performance is really quite good and I've never had problems with the GC.

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 offers a better alternative to functional programming: you can mix and match imperative and functional programming without the pitfalls of arbitrary mutable state (which is where most attempts to mix and match run into problems)


OCaml can do the same


They’re quite different languages in almost every way.

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.


> Conversely OCaml is an interpreted functional language.

Correction: OCaml is not an interpreted language. OCaml programmes are compiled down to native code (or bytecode, or Javascript).


Ahh, I didn’t know that. I just assumed that (smooth) REPL == interpreter. Thanks :)


OCaml supports functional programming more idiomatically: for example, it has implicit currying, and it has a single function type instead of several function traits. OCaml is garbage collected, if you don't want to think about ownership. OCaml has the ML module system, which has tradeoffs when compared with typeclasses or traits.


There is some cool stuff for OCaml, that is not (yet) possible in Rust, e.g. building a unikernel using MirageOS (https://mirage.io/)


It’s very much possible to build a unikernel in rust. Google ‘rust unikernel’ and you’ll get results.

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


The community has the vibe of sedate unix greybeards, rather than hypebeasts.


1. No borrow checker

2. Compilation times




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: