14

I am a complete newbie with OCaml. I have recently stumbled into this page listing a good amount of criticism towards OCaml.

Seeing that the page it quite old (2007): which of the bullets points listed there are still true today? For instance: is it still true that it is impossible to print a generic object?

I want to make it clear that I am not looking for a discussion of the opinions expressed therein. I am asking whether the information listed, such as the fact that integers overflow without warnings, is still correct for more recent versions of OCaml

  • 5
    I'm voting to close this question as off-topic because meta.programmers.stackexchange.com/questions/6417/… – Philipp Feb 26 '15 at 11:45
  • 3
    There is a fork where those might've been fixed: tryfsharp.org – Den Feb 26 '15 at 11:58
  • 7
    Of course the opinion in that essay you linked is valid, but whether these criticisms are relevant for you are an entirely different matter. Note that the author comes from a Common Lisp background and therefore has Lispish values. If you take another approach to OCaml than comparing it to Lisp (e.g. “OCaml is Haskell for mere mortals” or “If C were a functional language, you'd have OCaml”) you'll find it far more satisfying. For all its flaws, OCaml is a great language and I'd encourage you to dabble in it nevertheless. – amon Feb 26 '15 at 12:03
  • 5
    As far as I know there's no way to detect integer overflow in hardware, so you'd need to insert runtime checks all over the place to detect it; but the author dismissed using "bignum" on the basis of performance! The complaint about static typing amounts to saying seat belts are bad because you might think you can't die in a car crash. The complaint about module immutability is saying he wants to monkeypatch things - an anti-modular and error-prone practice. The "small type zoo" has nothing to do with type inference. It's clear where his biases lie. – Doval Feb 26 '15 at 13:03
  • 2
    @Doval: Of course you can detect overflow in hardware. Dealing with it, however, is a software matter. – Mason Wheeler Feb 26 '15 at 14:23
13

This article is discussed at several places:

To summarize: yes, OCaml is not a Lisp, and no, it is not perfect (what does that mean?). I don't think the points mentioned in the blog post are relevant for day-to-day O'Caml programmers.

Having studied O'Caml, I think it is an interesting language which can help you build programs you would not even dare write in, say, C/C++/Java: for example, have a look at Frama-C.

For an up-to-date description of O'Caml, I encourage you to read about its features: the language promotes strong static type checking techniques which allows implementations to focus on producing performant, yet safe, runtimes.

Important: I am no OCaml expert: if you are one of them and see I wrote something horribly wrong, please correct me. I'll edit this post accordingly.

Static type checking

  • False Sense of Security

    This is true, but obvious.

    Static typing gives you proofs you can trust about a subset of your program's properties. Unless you accept to go all formal, an average (non-toy) program will be subject to programming error bugs which can be witnessed only at runtime.

    That's when dynamic checking techniques can be applied: the OCaml compiler has flags to generate executables with debugging information, and so on... Or, it can generate code that blindly trust the programmer and erase type information as much as possible. Programmers who wants robust programs should implement dynamic checks explicitly.

    The same thing applies to e.g. Common Lisp, but reversed: dynamic types first, with optional type declarations and compiler directives second.

  • Few Basic Types

    Still applies: the core language has not changed (or not dramatically).

  • Silent Integer Overflow

    This is the norm in most languages that integer overflow are checked by hands. I don't know of any library that would type-check operations to verify whether overflow can occur.

  • Module Immutability

    1. Author mentions Functors but I fail to see how his example cannot be implemented. Reading the First Class Modules chapter of https://realworldocaml.org, it seems that modules can be used to compose and build new modules. Of course, modifying an existing module requires source code modification, but again, this is not unusual among programming languages.

    2. "Semantically, functions are compiled INLINE"

    The reddit thread above disagrees, saying that binding are resolved at link time. However, this is an implementation detail and I think that the emphasized Semantically relates to the way functions are resolved. Example:

     let f x y = x + y ;;
     let g a b = f b a ;;
     let f x y = x * y ;;
     exit (g 2 3) ;;
    

    The above program compiles, and when executed, returns 5, because g is defined with the first version of f, just as-if the calling function g inlined the call to f. This is not "bad", by the way, it is just consistent with O'Caml's name shadowing rules.

    To summarize: yes, modules are immutable. But they are also composable.

  • Polymorphism Causes Run-time Type Errors

    I can't reproduce the mentioned error. I suspect it to be a compiler error.

No Macros

Indeed, there are no macros but preprocessors (OcamlP4, OcamlP5, ...).

Minor Language Suckiness

  • Record field naming hell

    True, but you should use modules:

    1. Two fields of two records have same label in OCaml
    2. Resolving field names
  • Syntax

    Still applies (but really, this is just syntax).

  • No Polymorphism

    Still applies, but somehow there are people who prefer that instead of Lisp's numerical tower (I don't know why). I suppose it helps with type inference.

  • Inconsistent function sets

    See the OCaml Batteries Included project. In particular, BatArray, for an example of map2 for arrays.

  • No dynamic variables

    Can be implemented:

    1. http://okmij.org/ftp/ML/dynvar.txt
    2. http://okmij.org/ftp/ML/index.html#dynvar
  • Optional ~ arguments suck

    By language restriction, you can't mix optional and keywords arguments in Common Lisp. Does it mean it sucks? (off course, this can be changed with macros (see e.g. my answer)). See O'Caml's documentation for optional and named arguments in O'Caml.

  • Partial argument application inconsistency

    I don't think it this is really annoying in practice.

  • Arithmetic's readability

    It holds, but you can use R or Python for numerical problems if you prefer.

  • Silent name conflict resolution

    Still applies, but note that this is well documented.

  • No object input/output

    Still applies.

Implementation, libraries

These keep changing every day: there is no definitive answer.

Finally,

"You should try OCaml (or, better yet, Haskell) even if you think it sucks and you are not planning to use it. Without it, your Computer Science education is incomplete, just like it is incomplete without some Lisp and C (or, better yet, Assembly) exposure."

... still applies.

  • Thank you, I will have a look at the links, but I am not really asking whether those opinions are justified. I am asking whether the facts still hold. For instance: is there a way to print out any algebraic data type? Is it still accurate that integers overflow without warnings? Is there today a way to operate on file and dispose them without having to write every time the boilerplate to deal with closing files on errors? – Andrea Feb 26 '15 at 17:51
  • @Andrea I edited my answer. – coredump Feb 27 '15 at 10:53
  • 1
    "...there are people who prefer that instead of Lisp's numerical tower (I don't know why). I suppose it helps with type inference." Bingo. SML and OCaml's type system requires that every expression have only one type. SML's overloading of math operators is an exception built into the language. This is true of Haskell as well; enabling that kind of overloading was the motivation behind type classes. The catch is that you can only have one type class instance per type. You also can't blindly convert an integer to a float of equal size - a 64-bit float only has 54 bits of precision. – Doval Feb 27 '15 at 12:52
  • @Doval What about something like Typing the numeric tower for Racket? Can we imagine an OCaml library that would exploit type classes or Generalized Algebraic Data Types (GADT) in order to provide polymorphic math operators? Regarding conversion: not all operations are possible, but some are and could be typed. – coredump Feb 27 '15 at 13:04
  • 2
    @Doval: "Re: polymorphic math operators, I don't think there's any way without implementing Haskell's type classes". F# has polymorphic math operators without type classes. – Jon Harrop Aug 11 '15 at 18:00
7

Seeing that the page it quite old (2007): which of the bullets points listed there are still true today?

  • False Sense of Security. This is nonsense.

  • Few Basic Types. OCaml now has bytes and byte arrays but no built-in unicode strings, 16-bit integers, unsigned integers, 32-bit floats, vectors or matrices. Third-party libraries provide some of these.

  • Silent Integer Overflow. Unchanged but it was never a problem.

  • Module Immutability. His recommendation that functions and modules should be mutable is a grim throwback to Lisp and a really bad idea. You can supercede modules using include if you want to but you cannot mutate them, of course.

  • Polymorphism Causes Run-time Type Errors. This is a big problem with OCaml and it has not been fixed. As your types evolve polymorphic equality, comparison and hashing will start to fail when they encounter types like functions and debugging the problem is very hard. F# has a great solution to this problem.

  • No Macros. Ironically, when he wrote this OCaml actually had full support for macros but they have now decided to pull the feature out.

  • Wrappers. This was a real problem and it has not been fixed. There is still no try ... finally construct in the OCaml language and no wrapper implementing it in the stdlib.

  • Places. Unchanged but a non-problem.

  • Record field naming hell. Structure your code properly using modules.

  • Syntax. Unchanged but a non-problem.

  • No Polymorphism. This was mostly nonsense when he wrote it and nothing has changed.

  • Inconsistent function sets. OCaml still doesn't have a cons function. That's fine. I don't want Lisp stuff in my language, thank you.

  • No dynamic variables. Was a good thing about OCaml. Is still a good thing about OCaml.

  • Optional ~ arguments suck. Optional arguments rock. I badgered Microsoft to get them to add optional arguments to F#.

  • Partial argument application inconsistency. Eh?

  • Arithmetic's readability. This has changed since I stopped using OCaml ~8 years ago. Apparently now you can do Int64.((q * n - s * s) / (n - 1L)).

  • Silent name conflict resolution. He was trying to do full-blown software development in the REPL as you would in Lisp. Don't do that in OCaml. Use files and batch compilation resorting to the REPL only for testing, running disposable code and interactive technical computing.

  • Order of evaluation. This was wrong when he wrote it. Order of evaluation is undefined in OCaml.

  • No object input/output. He cited a third-party library that already solved this "problem".

  • Compiler stops after the first error. Eh?

  • No stack trace for natively compiled executables. Fixed.

  • Debugger sucks. I never used the debugger. Static type checking catches almost all of my bugs.

  • GC sucks. I found OCaml's GC to be superb except for one major problem: the global lock prevents parallel programming.

  • No implicit forward declarations. Mutual recursion is explicit by design in all MLs. The only wierdness is that type definitions are recursive by default whereas let bindings are non-recursive by default.

  • Function round is absent. OCaml still has a bare bones stdlib but third party libraries like Jane St's Core provide round and friends.

  • Lists. List.map is still not tail recursive. I submitted patches to fix serious bugs like this and had to wait years before they appeared in releases. Lists are still immutable, of course. And so they should be.

  • Speed. I believe the compile times for big polymorphic variants have been fixed.

  • Pattern matching. A triumph of hope over reality. The Lisp community have failed to do this. Hence my 10th rule: any sufficiently complicated Lisp program contains an ad-hoc, informally-specified and bug-ridden implementation of half of OCaml's pattern match compiler.

For instance: is it still true that it is impossible to print a generic object?

When he wrote that you could not simply do:

print value

but you could invoke the pretty printer from the top-level as a library call, giving it the necessary type information. And there was a macro that you can use to annotate data structures in order to have pretty printers autogenerated.

  • Pattern matching: OCaml source code and optima both references the same paper: "Optimizing Pattern Matching". I would say that neither "ad-hoc", "bug-ridden" nor "informally-specified" can realistically be applied here. "F# has a great solution to this problem": I would genuinely like to see a little more detail about this, if possible. The answer is good but cursing when talking about cons gives a bad tone (the original article is a rant, but you don't need to copy that from it). – coredump Aug 11 '15 at 19:39
  • 2
    @coredump: "I would genuinely like to see a little more detail about this, if possible". F# has user defined ad-hoc polymorphism on certain operators and functions. So you can use + on ints, floats and complexes but you can also define your own types and add an overload for + to work on your type. This provides the brevity and readability of Lisp or Haskell with the predictably-good performance of SML or OCaml, achieving something that no other language does. – Jon Harrop Aug 11 '15 at 23:45
  • @coredump: "OCaml source code and optima both references the same paper". The techniques described in that paper are built entirely upon the ML type system. A type system that Lisp does not have. Optima does not and cannot do much of what is described in that paper. Just look at section 4.2 "Using exhaustiveness information". There is no exhaustiveness information in Lisp because there are no variant types. For example, OCaml chooses between nested jumps or a dispatch table based upon the number of leaves but that information is unknown in Lisp. – Jon Harrop Jan 12 '16 at 3:16
  • Lisp and OCaml are designed for different things (e.g. dynamism, image-based programming). But Lisp has a type system, different from Hindley-Milner, and implementations do exploit it during compilation. Look at this SBCL session with examples from the 4.2 section of the paper. Exhaustiveness is already checked by the compiler from type inference and declarations. Optima could add implementation-specific code for compile-time macroexpansion (or SBCL VOPs), to have other strategies, but there is not enough incentives to do it. – coredump Feb 23 '16 at 14:46
  • How does that apply to user-defined algebraic datatype? – Jon Harrop Feb 23 '16 at 18:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.