Why O’Caml is not my favorite programming language

A few hours ago the ICFP Programming Contest 2007 ended, and the team Funktion im Kopf der Mensch participated again, this year slightly demotivated by the fact that the organizer’s main goal seems to have been to show off how smart they are instead of letting the participant practice their creative skills.

As always we were using Objective Caml for most of our programming work. O’Caml has always had some annoying sides, but this year I ran into so many of them that I got seriously pissed off, and will now have to investigate other options.

For us, O’Caml’s main attraction are its type system, which, when used correctly, makes it surprisingly hard to write incorrect code, and its performance. O’Caml’s big weakness is its standard library. To be frank, it sucks, big time.

I’ll present the issues I ran into during the past three days, and I’ll compare the way O’Caml handles those things with Common Lisp, because in the areas I’ll discuss here, it is a good example of how to do it right. Note that I’m not saying that Common Lisp does everything else better. There is a reason why we’ve been using O’Caml, after all.

Let’s start with numbers. Say we need a function which adds the number 3 to a given number. Should be a trivial task, right? Indeed, in Lisp it is:

(defun add-3 (x)
  (+ x 3))

Let’s see if it works:

[2]> (add-3 0)
3
[3]> (add-3 4.5)
7.5
[4]> (add-3 3/2)
9/2
[6]> (add-3 (sqrt -4))
#C(3 2)

Great – we can give the function any kind of number, and it adds 3. How do we implement this in O’Caml? Well, first we’ll have to decide which kind of number we want to operate on, because, you see, you can’t mix different kinds of numbers. Ok, let’s concentrate on integers. There can’t be more than one kind of them, can there? Well, it turns out there are several. There’s “int”, “int32″, “int64″, “nativeint” and “big_int”. Here’s the code for all of them:

let add_3_int x =
  x + 3
let add_3_int32 x =
  Int32.add x (Int32.of_int 3)
let add_3_int64 x =
  Int64.add x (Int64.of_int 3)
let add_3_nativeint x =
  Nativeint.add x (Nativeint.of_int 3)
let add_3_big_int x =
  add_big_int x (big_int_of_int 3)

And those are just for the integer types! How ridiculous can it get?

Next issue: Arrays. Let’s say we need a function which returns an array with all the elements of the array it was given in reverse order, except for the last one. A triviality in Lisp:

(defun chop-rev (seq)
  (reverse (subseq seq 0 (1- (length seq)))))

Let’s see if it works:

[10]> (chop-rev #("abc" "123" "xyz"))
#("123" "abc")
[9]> (chop-rev "abcdef")
"edcba"

Notice that in Lisp string are arrays, too. As an added bonus, our function even works on lists:

[8]> (chop-rev '(1 2 3 4))
(3 2 1)

What can O’Caml do for us? Again, we first need to ask: Which kind of arrays are we talking about? There’s the built-in array type “array”. Then there’s strings, which O’Caml doesn’t treat as arrays, for no apparent reason. There’s another type called “Buffer”, which is really just a string that can grow, but is, of course, completely incompatible with “real” strings. There’s also something called “bigarray”, which is an array that can be big. In any other language such a data structure would be obsolete, because standard arrays wouldn’t have any size limits. Not so in O’Caml! You can enjoy all the comforts of the built in array type, but only if you don’t need to put in more than about 4 million elements (on 32 bit systems), unless you work with floats, in which case you’re limited to 2 million elements. Perhaps I should also mention that strings are limited to 16 million characters, but they forgot to implement big strings. What a joy!

About these ads

25 thoughts on “Why O’Caml is not my favorite programming language

  1. I don’t want to defend OCaml too much, but your examples are bad ones: there are easy ways of expressing Lisp-like numbers and lists in OCaml. But, in the end, both OCaml and Lisp are obsolete. If you want an easy-to-use dynamic programming language, there are plenty better choices than Lisp, and for static type systems, people have made a lot of progress over the last decade.

  2. Have you tried Haskell yet? I (unfortunately) don’t use enough functional languages day to day, but I’ve heard a lot of good things about Haskell…

  3. Wow. I have no idea what that means.

    I couldn’t resist stopping by when your blog title is so intriguing.

    Enjoy your day.

  4. Mike: There is no easy way (indeed I claim that there is no way) to express Lisp-like integers (Lisp also provides rational, floating point and complex arithmetic) in O’Caml so that they behave like standard integers, i.e. without conversion to and from standard integers when interacting with standard O’Caml functions.

    Also, I wasn’t talking about lists (you can’t express Lisp-like lists in O’Caml because they can’t be statically typed), but about arrays. And yes, you can use big arrays, and I suppose they work, but then why provide the “small” array type at all? Why can’t all arrays be big?

    My point isn’t that can’t implement those basic features in O’Caml, my point is that they are not implemented in the standard library. Why not?

    As for dynamic languages better than Lisp: Could you give some examples?

  5. My friends and I (3 of us) did the ICFP contest this weekend and decided to learn Ocaml as part of it. We hit up against many similar issues, and didn’t even get the dna to rna converter working completely, but we did learn a lot. Ocaml’s standard libraries suck, but if you augment them with Extlib and Bigarray, you can get a lot of benefit. We ran into the limits on the string sizes, and have had to move the code over to using a Bigarray of chars…

    Eventually, I’ll post about the experience =)

    -Dave

  6. add3 :: (Num a) => a -> a
    add3 x = x + 3
    chop_rev seq = drop 1 (reverse seq)

    Implements the same functions in Haskell.
    It seems much less annoying than O’Caml, but probably has its own quirks.

    *Main> add3 0
    3
    *Main> add3 4.5
    7.5
    *Main> chop_rev [1, 2, 3, 4]
    [3,2,1]
    *Main> chop_rev “abcdef”
    “edcba”
    *Main> chop_rev ["abc", "123", "xyz"]
    ["123","abc"]

  7. There was a lot of success on the part of the Haskell people this year — we had implementations topping 40k iterations per second. A lot of this was down to library support, especially the recently became available ByteString library. As you probably are aware, type classes in Haskell pretty neatly solve the issues that you mention (though many people wish that the standard numeric hierarchy conforms to the mathematical one a bit more).

  8. I’m aware that Haskell is much better in the areas I’ve had issued with in O’Caml, and I’ll certainly give it a try in the near future.

    Thanks for the example, Anders!

  9. And the thing with Haskell is that there’s often an even terser way to do it.

    add3 :: Num a => a -> a
    add3 = (+) 3

    chop_rev = drop 1 . reverse

  10. I concur with your criticism (both with ocaml and this year’s contest).

    Unfortunately, the show-off attitude of the contest organizers got me really disheartened. As a member of your team I feel this is the right time to say “I’m sorry. I didn’t want to let you down.” By-gone.

    These ocaml issues you listed (plus a few more, like generic parsers/printers, 31-bit integers, and missing stdlib functionality in general) really bother me.

    For some of these problems there is a quick, decent fix. Generic parsers/printers can be done by using camlp4, like Markus Mottl did with his sexplib.

    Other issues have no simple solution. The ocaml stdlib remains very stable (and feature-poor) over the years, so developers are forced to write (test, and debug) their own stdlib-replacement/addendum.

    The most serious problem I see with ocaml, however, is that the compiler discourages you to write compact, elegant code (that uses separate modules and higher-order functions). If you are heading for good performance, you end up writing lots of code that does not use these features.

    People that use ocaml in real life seem to bump into these problems regularly, as can be seen in the article “Caml trading: Experiences in Functional Programming on Wall Street” in “The Monad Reader” by Yaron Minsky.

    Maybe Haskell is a better overall choice than ocaml indeed. Thanks to the negative experiences of this weekend, I am now seriously considering moving to Haskell, too.

  11. performace is no excuse for a bad language. and a bad language is no excuse for performance.
    unless its a performance critical application (and REAL critical), i can’t see how O’Caml can be better than Common Lisp.

  12. I used the recently released Vec library (http://www.dealfaro.com/home/vec.html) for the contest and it worked perfectly. Not having type classes or some other way to have polymorphic operators is definitely annoying… at one point I understood their motivation but lately I’ve been eyeing Haskell like many others :)

  13. I have used F# (OCaml’s dialect from Don Syme at MS Research) for ICFP 2007. It was my first F# program also :) and i had just Fri and part of Sat for fun :(. My entire dna decoder and rna->image translator is about 450 LOC and i’ve image self test passed. From my Lisper/Schemer point, F# is nice mental practice and it’s definitly not a dark side of power :)

  14. F#’s pretty cool. It’s like Ocaml, but with a much bigger class library. Performance is similar to Ocaml, and you can use it on Mac or Linux if you install the “Mono” version of the CLR.

    And there’s no annoying “arrays can only be this big” limits, because the CLR memory system doesn’t store type tags in pointers.

    …I think this year’s IFCP was very difficult. They should have had more conformance tests to help out teams with buggy implementations.

  15. The language your looking for is Haskell. Haskells type classes make writing the kind of polymorphic functions your looking for easy and unlike lisp your not giving up on type safety.

  16. There are many different numeric types, such as modulo ints, integers, arbitrary-precision rationals, floating point numbers, arbitrary-precision floats, intervals and so on. I notice that all of your examples happen to use only the numeric types that are bundled with Lisp.

    I wonder how your examples would compare if you looked at numeric types that don’t happen to be bundled with Lisp? Interval arithmetic is far more useful than arbitrary-precision rationals, for example…

  17. I see the well known caml (and F#) troll harrop
    found his way here. It sure doesn’t feel right that
    all these people became disillusioned with ocaml
    does it John ? Less potential vcustomers (= suckers) for you.
    Jumping ship to Haskell must hurt through.
    Oh the irony ;-)

  18. Functional languages confuse the hell out of me. I’d much rather stick with ym high level C++ and Java then get down and dirty with things like Caml, Haskell and Scheme..

  19. >>Functional languages confuse the hell out of me. I’d much rather stick with ym high level C++ and Java then get down and dirty with things like Caml, Haskell and Scheme.
    I really hope you realize what you just wrote.
    C++ and Java Hight-Level Language…riiight..
    To Original Poster:Your Whole post doesn’t make the least bit of sense to be honest.And stop being a speed kiddie for god sake,learn how to think and solve problems.

  20. I have a bunch of open source source code written in Ocaml.

    I would like to know if there is a way to convert these Ocaml source code files to C/C++ or PHP.

    Why?

    Well, I don’t want to learn a new language program. I have enough with the fact that I have to program in C/C++, C#, Visual Basic, PHP, ASP, Javascript, ActionScript. Learning a new language is tedious.

    Please, If you know a program that would make me some nice C files from the Ocaml files (.ml) let me know. Send me an email to axllaruse@yahoo.com

    Thanks

Comments are closed.