
OCaml Language Sucks (2007) - hellofunk
http://www.podval.org/~sds/ocaml-sucks.html
======
pcwalton
The rather idiosyncratic language preferences of the author show through at
the end:

> Make no mistake: Java/C/C++/C#/Perl are much worse than OCaml! There appears
> to be just one good tool, with OCaml coming a distant second.

where "one good tool" links to
[http://www.podval.org/~sds/tool.html](http://www.podval.org/~sds/tool.html),
an essay praising Common Lisp.

~~~
wlievens
Devout Lisp afficianados tend to be a little... opinionated.

~~~
DonaldFisk
This is true because

Devout ?x aficionados tend to be a little... opinionated.

I've met plenty of devout Prolog, C, C++, and Java aficionados, and yes,
they're opinionated.

Also, you're more likely to notice opinions that differ from your own than
ones you agree with, these are more likely to be held by groups you don't
belong to, and you're less likely to belong to minority groups, such as Lisp
programmers.

~~~
bubtubgub
> I've met plenty of devout Prolog aficionados

No you haven't.

~~~
DonaldFisk
I can understand your incredulity, but yes, I have. They even called
themselves "the Prolog mafia". This was in the early 1990s. I like Prolog, and
regret that it's hardly used nowadays.

------
wyager
Agree with many of these, but

>Cannot add an int to a float - need an explicit cast. This is an obvious
deficiency

is ridiculous. Implicit conversions cause huge classes of errors in languages
that support them. There's not even well-defined behavior for converting an
integer that won't fit in the mantissa to a float.

My experience with OCaml is that it made too many concessions to imperative
programming to be a particularly useful functional language and its type
system is sorely lacking. Things like 31/63-bit integers for pointer tagging,
binary "generic" comparison operators, ;-sequencing, etc. are terribly ugly
hacks.

~~~
ufo
I agree that the issues you mentioned are annoying warts for Ocaml but I'm not
so sure that they are all concessions to imperative programming. The 31-bit
ints have more to do with simplifying the garbage collector implementation.
The "generic" comparison operators have more to do with the lack of type
classes. This has a bit to do with the imperative bits of the language (which
make implementing type classes a bit harder) but also has to do with conflics
with the existing module system (ocaml Functors support type encapsulation and
some other things that make it harder to add type classes to the language)

------
iso-8859-1
Posted to Reddit eight years ago:
[https://www.reddit.com/r/programming/comments/20dh6/ocaml_la...](https://www.reddit.com/r/programming/comments/20dh6/ocaml_language_sucks/)

------
ufo
Some of these points are a bit outdated. Ocaml improved a lot recently.

> False Sense of Security

This is a strawman argument. And the static types really do help, specially if
you make good use of algebraic data types and the exhaustiveness checker.

The autocompletion and documentation suport is also super nice. Installing
Merlin is one of my top priorities when I set up an ocaml environment.

[https://github.com/the-lambda-church/merlin](https://github.com/the-lambda-
church/merlin)

> Few Basic Types

Nowadays you tend to see tons of different integer types in systems
programming languages (C, C++, Rust, etc). This is more the author saying he
loves Common Lisp than a downside of Ocaml.

> Module Immutability

Can't you define a new module that wraps around the old one and overrides the
`date_of_string` function? Changing the implementation of a module violates
all sorts of abstraction guarantees and would lead to many thorny issues.

> Polymorphism Causes Run-time Type Errors

Polymorphic equality and comparison can definitely be a source of confusion.
Hopefully in the future we will have module implicits and be able to solve
this issue similarly to how Haskell does it.

> No Macros

Again, this is more of a "my language is not LISP" complaint. Ocaml has
camlp4, which is not a full-blown macro system but allows for a large variety
of syntactic extensions to the language. However, camlp4 was tricky to
maintain in sync with the original language so nowadays the community is
moving towards simpler ppx transformers:

[http://whitequark.org/blog/2014/04/16/a-guide-to-
extension-p...](http://whitequark.org/blog/2014/04/16/a-guide-to-extension-
points-in-ocaml/)

> Record field naming hell

This got much better in more recent Ocaml versions, which let you have
multiple records with the same field name living in harmony.

> Syntax

I'll have to agree with this one. Ocaml definitely doesn't have the prettiest
syntax.

> No (ad-hoc) Polymorphism

This is a price to pay for keeping the type system sane. More flexible
arithmetic coercion rules would also get in the way of type inference, which
is a really nice thing about Ocaml.

> Inconsistent function sets

Its a bit less awful if you use one of the "replacement standard libraries",
such as Containers, Core or Batteries. But yes, the suckyness of the standard
library is a huge pain point.

> Arithmetic's readability

I don't think defining custom operators is as bad as the author thinks here.
Their precedence is defined by the symbols you use so a new multiplication
operators should behave similar to all the other ones.

But yes, the monomorphic operators really are annoying. As I said before, I'm
really looking forward to module implicits (sadly, it seems we'll have to wait
at least until the version after the next one though)

> Order of evaluation

This one is even worse than he says. Order of evaluation is undefined and when
compiling to bytecode its different from when its compiled to native code (one
is left-to-right and the other is right-to-left)

> No implicit forward declarations

Being able to shadow existing definitions is pretty nice sometimes. Haskell
does the "declarations are recursive by default" thing and it sometimes gets
in the way.

> Standard Library Sucks

Can't argue against that. But at least there are replacement libs you can use
to make it suck a little bit less.

> OMake

Nowadays I think people are moving towards ocamlbuild instead.

~~~
nv-vn
Also, generally if you're doing a lot of math with floats (or some other
numeric type) you can do:

    
    
        Float.(x + y * 2.0 - z / w)
    

or even just open the Float module locally

Also, the with-open-file problem can be solved pretty simply by using GADTs
(and this makes it simple to differentiate between the different modes you can
open a file in, as well).

------
chmaynard
A very enjoyable read! The author sounds like a curmudgeon of the first order.
Unfortunately, my only take-away after exploring his website is the joke about
the pessimist.

------
Ericson2314
Haskell -XStrict, problem solved :P.

~~~
catnaroek
-XStrict doesn't make Haskell's module system any less of a bad joke.

~~~
Ericson2314
Yes, yes. I wish we had [https://www.mpi-sws.org/~dreyer/papers/mtc/main-
long.pdf](https://www.mpi-sws.org/~dreyer/papers/mtc/main-long.pdf) too.
[http://plv.mpi-sws.org/backpack/](http://plv.mpi-sws.org/backpack/) and
[http://www.lpw25.net/ml2014.pdf](http://www.lpw25.net/ml2014.pdf) will bridge
the gap between OCaml and Haskell from both sides, but I know of no plan for
doing something about the rather embarrassing facepalms mentioned in the OP.

At least most of Haskell's facepalms are in the std library, not the language
(monad fail, applicative monad), which makes there after-the-fact remedy
slightly less herculean.

~~~
catnaroek
If I understand correctly, Backpack's design is primarily driven by
compatibility considerations (“how do we build upon GHC's existing
foundation?”), rather than elegance. In particular, Backpack doesn't eliminate
those ugly .hs-boot files, it just automates and hides their generation and
processing.

For all their faults, Standard ML and OCaml have pretty good support for
modular programming. And, as the Modular Type Classes paper you linked shows,
type classes can be built elegantly on top of a good modular foundation.

~~~
Ericson2314
I agree that backpack leaves Haskell with too many overlapping concepts. But
consider things could be unified internally, and compatibility-breaking
language extensions introduced down the road. That's my hope at least :).

Also, check out [https://www.mpi-sws.org/~rossberg/1ml/](https://www.mpi-
sws.org/~rossberg/1ml/) . I think it makes a pretty good argument that there
is a similar problem going on with the MLs versus the ideal too. (The
overlapping features are also not a subset of Haskell's overlapping features,
interestingly too.)

~~~
catnaroek
As far as I can tell, Standard ML doesn't have much of a feature overlap
problem. Off the top of my head, the only redundant feature is `abstype`,
which I think is a leftover from the 70's, when ML didn't have its module
system. If Standard ML had first-class modules, then modules and records could
also be considered redundant.

What Standard ML suffers from is lack of expressivity. The lack of (pattern)
guards sometimes forces pattern matching blocks to (nest more than necessary
and) contain redundant clauses. The lack of polymophic recursion forces non-
uniformly recursive data types (as well as the functions that operate on them)
to be rewritten in a way that makes the type checker a less effective bug-
finding tool. Without higher-rank types, you can't express the Day
convolution, free Applicatives, or Traversals (from the lens library). On the
other hand, I don't find myself missing higher-kinded types much, since they
are mostly useful in combination with type classes, which modules can
adequately replace.

What 1ML does is _unify_ features: (0) records and let expressions are now
particular uses of modules, (1) core language functions and type constructors
are now particular uses of functors.

~~~
Ericson2314
> On the other hand, I don't find myself missing higher-kinded types much...

I don't find myself missing modules that much for precisely the inverse
reason.

> If Standard ML had first-class modules, then modules and records could also
> be considered redundant.

If Haskell+Backpack had first class packages/modules, then packages/modules
and type classes could also be considered redundant.

This is the duality I am trying to get it :). Looking each language alone, the
features are "somewhat orthogonal". But in the context of the entire design
space, the _lack_ of unification comes off as redundancy.

~~~
catnaroek
> I don't find myself missing modules that much for precisely the inverse
> reason.

The “tiny” detail is that ML modules do much more than what type classes do:
they allow you to define abstract types whose representation is invisible to
the rest of the program, without ugly hacks like `newtype`-wrapping the
internal representation.

> If Haskell+Backpack had first class packages/modules

Packages and modules are completely different things. A package is a unit of
_distribution_ , whereas a module is a unit of _encapsulation_. Even within a
single program that's only distributed as a whole, you may want different
components to hide their internals from each other.

> This is the duality I am trying to get it

There is no duality between modules and type classes, because, while modules
can do everything type classes do (and more), the converse does not hold.

~~~
Ericson2314
Remember, it's the _combination_ of type classes and higher kinded types that
the MLs are up against.

[Not because there is an inherent trade-off (because there isn't one between
type classes vs modules either as the Dryer paper shows), but beause the MLs
have that weakness.]

~~~
catnaroek
Non-recursive higher-kinded types can be implemented in terms of functors. For
example, the free monad type constructor is very easy to implement in Standard
ML (OCaml is similar):

    
    
        signature FUNCTOR =
        sig
          type 'a t
          val map : ('a -> 'b) -> 'a t -> 'b t
        end
        
        functor FreeMonad (F : FUNCTOR) =
        struct
          datatype 'a t = Pure of 'a
                        | Free of 'a t F.t
          (* operations go here *)
        end
    

Modules can do everything type classes can do, _and more_. There is no
weakness.

The real reason why you need to define this as a higher-kinded type
constructor in Haskell (`data Free f a = ...`) is to overcome the limitation
of not having parameterized modules. The way it's done in Haskell also has the
defect that the type system will happily allow you to construct a value of
type `Free Foo a`, where `Foo` is _not_ a functor, whereas the ML version
prohibits this by construction.

~~~
Ericson2314
That's not the same however, because the modules language is a lot more
constrained than the term language. See the first paragraph the second page of
[https://www.mpi-sws.org/~rossberg/1ml/1ml-extended.pdf](https://www.mpi-
sws.org/~rossberg/1ml/1ml-extended.pdf).

~~~
catnaroek
> That's not the same however, because the modules language is a lot more
> constrained than the term language.

You don't need to tell me. That's obvious to anyone who has programmed in ML.
But Haskell's type class language is similarly a lot more constrained than its
term language. It's basically a pure (in particular, cut-free) compile-time
Prolog, with some restrictions to guarantee that no query will return more
than one result.

> See the first paragraph the second page of [https://www.mpi-
> sws.org/~rossberg/1ml/1ml-extended.pdf](https://www.mpi-
> sws.org/~rossberg/1ml/1ml-extended.pdf).

Rest assured, I've read the paper - not today, but several months ago. I'm
quite familiar with the ML module literature. Check my post history:

[https://news.ycombinator.com/item?id=10856201](https://news.ycombinator.com/item?id=10856201)

[https://news.ycombinator.com/item?id=11131265](https://news.ycombinator.com/item?id=11131265)

But, since you're arguing that _the combination of type classes and higher-
kinded types_ can do something that ML modules can't, I'm providing evidence
for the opposite: everything that can be done _with type classes and higher-
kinded types_ can be done with modules.

(0) You can't compute new modules at runtime, but you also can't define new
type class instances at runtime.

(1) An ML module can have both transparent and abstract type components,
controlled by the programmer. A type class always has abstract type components
at definition time, and transparent type components at instantiation time.
Modules are more expressive than type classes.

(2) The signature of an ML module is structural, and you can, _after the fact_
, selectively forget part of a module's contents. Type classes don't let you
do this. Try making `Monoid` a subclass of `Semigroup` without recompiling any
existing code. Again, modules are more expressive than type classes.

As for higher kinds, at least with OCaml's higher-order applicative functors,
you can encode arbitrary higher-kinded type constructors. And most of the
higher-kinded constructors that actually arise in practice can be encoded even
with Standard ML's first-order functors.

~~~
Ericson2314
> Rest assured, I've read the paper

Don't worry, I quote the paper not because I doubt your ML experience, but to
make sure that _I_ don't mess up the peculiars of the situation. I've spent
more time thinking about the design of SML/OCaml than using them, and wouldn't
want to undermine my argument with a mis-remembered surface syntax.

My point with this arguments is not that "polymorphic instances" are as
powerful as functors (they aren't!). Rather in this (and similar examples)
it's higher-kinded types vs modules, no type classes involved. The Haskell
equivalent is just a plane function, _not_ a polymorphic instance, and there
is things that can be done in the term language but not the module language.

SML/OCaml with higher-kinded types would annihilate this argument, no type
classes or implicits needed, but don't yet exist.

~~~
catnaroek
> My point with this arguments is not that "polymorphic instances" are as
> powerful as functors (they aren't!). Rather in this (and similar examples)
> it's higher-kinded types vs modules, no type classes involved.

This paper disagrees: [http://www.cse.unsw.edu.au/~chak/papers/modules-
classes.pdf](http://www.cse.unsw.edu.au/~chak/papers/modules-classes.pdf) .
See below.

> The Haskell equivalent is just a plane[sic] function, not a polymorphic
> instance, and there is things that can be done in the term language but not
> the module language.

A plain function can't return fresh new types. A functor can.

> SML/OCaml with higher-kinded types would annihilate this argument

As I said above, OCaml's higher-order modules let you encode arbitrarily
higher-kinded types. Even if OCaml didn't have parametric polymorphism in the
core language, you can express it in the module language with applicative
functors (although the result would be a lot more verbose). If you don't
believe me, give me an arbitrary higher-kinded type, and I'll tell you how to
encode it in OCaml.

~~~
Ericson2314
I don't have time to read the paper for a few days, but I meant while functors
can do things functions cannot do, functions can do things functors cannot do
too.

~~~
catnaroek
And records can do things functions can't do. And vice versa. That alone
doesn't mean records in one language are the counterpart of functions in
another.

~~~
Ericson2314
How is that relevant? I'm claiming that their exists functions in Haskell that
cannot be encoded either as functor or functions in ML. (If you want, I'll try
to come up with an example, but my guess would be some funny polymorphic
recursion + higher-kinded types)

