Brool brool (n.) : a low roar; a deep murmur or humming

The Genius of Python, The Agony of Ocaml

 |  coding

Having been using Python for a while, I am fully enamored of the language. It is elegant, easy to read, and wonderfully descriptive. The only drawback is a serious case of the slows, but I am hoping that this will be fixed eventually. When I think of all the time I spent writing Java, it is enough to make me cry.

Writing code in Python flows well because of the little touches: help(module/function), python -i, and others. When writing I actually don’t have a reference manual or anything on the screen: instead I just have a Python interpreter running in a editor window, and test little snippets of code there. It flows well. As an example, for the past two months we’ve been working on a middleware layer in Python for a soon-to-be-released website. Total time spent coding? Typical Silicon Valley hours. Total time spent in the debugger over the same time period? Mmmm, probably 30 minutes. That counts both pdb.set_trace calls and Emacs pdb mode.

Unbelievable? If you’re used to C++ or Java, it might be, a little. But being able to test code routines as soon as they are written, in conjunction with unit tests, means that I hardly spend any time in the debugger.

The Agony of Ocaml

I want to like Ocaml, I really do. Syntactically the language is a little quirky, with a mix of a bunch of different styles, but, hey, I programmed in Perl, so what’s a little heterogeneous style-mixing between friends? But even better, I can code something really quickly in Ocaml imperative style while I would still be figuring out how to elegantly do it in Haskell.

And that language is fast. I mean, really astoundingly fast, which is why it is so evil. It’s like that psychotic girlfriend that was amazing in bed. You keep trying to break up with her, but she seduces you right back into that abusive relationship.

Ocaml has that seductive speed, it’s got an interpretive toplevel, so it makes you think that it’s a really cool language. But then it punishes you, again and again. If Python is an honest and open relationship where everything is on the table, Ocaml is a sulking, pouting one that makes you go through pain and punishment to figure out what you did wrong.

Oh, you want an example? Try Python’s

>> a = {} >> a['x'] = 10 >> a['y'] = 20 >> print a {'y': 20, 'x':10}

vs. Ocaml’s

# let a = Hashtbl.create 100;; # Hashtbl.add a "x" 10;; # Hashtbl.add a "y" 20;; # ... how do I print!? ... # let hashstrint_print = Hashtbl.iter (fun key data -> Printf.printf "%s: %d; " key data);; # hashstrint_print a;;

Ignore the trailing semicolons and spaces, please don’t make me fix that. I have to write one routine like this for every combination of key/data hash table that I have. There isn’t even a printing facility for lists, you have to use List.iter and the right print for your data types. Traces show <poly> for anything more complex than a simple type. The debugger continues the brain dead approach to values, giving you the same (lack of) facilities as the toplevel. It makes Ocaml so frustrating: it’s wonderfully expressive, it’s faster than greased rockets, but it’s impossible to see your data while debugging!

Maybe it’s a requirement that functional languages make it painful to get output, something that is built into the universe at a basic level. Ocaml has their strongly-typed and non-overloaded print routines, while Haskell has monads. (Whoops, forgot about Lisp/Scheme. Once again Lisp is ahead of the curve).

There are some options, kinda. Metaocaml allegedly has a polymorphic print, but I haven’t used it. You can use higher level functions to make printing stuff easier, as in let hash_print format = Hash.iter (fun key data -> Printf.printf format key data), but that only works for simple types that can be handled by a printf format specifier. There is the extended library for Ocaml, which, if you ignore the tar file errors, has a dump/print routine that works okay.

I would like to propose my Eighth Law Of Coding, which should be:

If your language doesn’t have a polymorphic print, there is something wrong with the implementation or the language, or both.

Yes, yes, I know — you can write your own printers and attach them to the toplevel/debugger. To that, I say: Why, thank you, Ocaml, thank you for allowing me to code my own routines to print a hash table.


Comments are moderated whenever I remember that I have a blog.

Ari | 2012-04-30 23:36:35
I'd actually disagree about polymorphic print. Often it's a blessing, but not always. Just like the presence or lack of polymorphic "+". Every serious programmer I've met has introduced a bug into a program by dividing a float by an int, or comparing equality on floats without remembering to define a level of precision. When you're printing to the screen and you expect the output to fit nicely in a few characters and it turns out to be a large hashtable, that's just as bad. Forcing you to think about what you're dealing with is not a bad thing. And having the compiler automatically make sure you really are dealing with what you think you're dealing with is a very good thing. But yes, if you're writing some 20 line program I would totally recommend using a scripting language like python. It's delightful in that realm of programming. But once you're into serious development, not having a print routine for a hashtable is below the threshold for even a trivial problem; you make a helper function once and you're done. Besides -- in a real application, how often would you want to output the contents of a hash table in a random order anyway? Ever?
tim | 2012-05-02 20:55:24
@Ari: Haskell does this right with typeclasses -- strong typechecking w/polymorphic operators. Ocaml makes quick-and-dirty debugging more painful than it needs to be because you have to stop and write some code to print out even basic types. What's more, you have to write new print statements for every <i>type</i> of hash map; specifically, an int -> string hashmap requires different code than an int -> int hashmap or an int -> (int -> string) hashmap.
dmitry | 2012-05-15 05:35:18
@Ari Trouble with the absence of polymorphic print (and operators) is that the rule of a minimalistic approach is broken. Which increases code size. Which is bad. Because time required to read the code is at least proportional to its size. To give you an example, consider: &gt;&gt; print {'x':10, 'y':20} How long does it take to read? About 0.5 second? Less? Now back to the Ocaml example: # let a = Hashtbl.create 100;; # Hashtbl.add a "x" 10;; # Hashtbl.add a "y" 20;; # let hashstrint_print = Hashtbl.iter (fun key data -&gt; Printf.printf "%s: %d; " key data);; # hashstrint_print a;; Just how long would it take one to read it?
Associat0r | 2012-08-30 20:00:15
# let hashstrint_print = Hashtbl.iter (fun key data -&gt; Printf.printf “%s: %d; ” key data) This can be shortened to at least. # let hashstrint_print = Hashtbl.iter (Printf.printf “%s: %d")
Victor | 2007-07-25 06:08:05
Well, I do not find lack of polymorphism in the OCaml 'print' function a significant drawback. It seems to me, that the static typing system of OCaml is much more pleasant to work with than Python's weak and dynamic duck typing.
Olle | 2012-11-29 19:35:44
...or use a record instead of a hashtable.
dmitry | 2013-02-21 23:07:18
@Associat0r Yes, with that change Ocaml code would get 1/5 shorter sizewise, 0% shorter linewise. Arguably it is less readable. Example in Python is 5 times shorter (both size and lines).
George | 2014-02-11 16:48:28
For people coming to these page via the search engine: there are two alternatives to standard libraries, "Batteries" and "Core" which both offer rather useful approach for polymorphic printing. Batteries provides polymorphic "dump" function which can print any value thrown at it (rather badly for types, as the type name is lost during the runtime). Core provides slightly-harder-to-use but arguably more useful s-expression serialization framework (a good reference is, which make printing very easy: <code> utop # let a = String.Set.of_list ["hello"; "scary"; "world"];; utop # String.Set.sexp_of_t a;; - : Sexp.t = (hello scary world) </code> It's even better for your own types: <code> type a = A of (string, int) Map.Poly.t with sexp;; let b = A (Map.Poly.of_alist_exn ["hello", 1; "world", 2;]);; utop # sexp_of_a b;; (* auto-generated function *) - : Sexp.t = (A ((hello 1) (world 2))) </code>
Jack Lost | 2014-07-31 22:17:41
Obviously, the author of this article only learnt OCaml on surface. One of the true beautiful sides of OCaml is its super strongly type system (so it doesn't allow so-called polymorphic arguments in some cases). This kind of type system naturally reduced many possible bugs. And the code will be much more robust. Also that's why there is a saying for OCaml that if the code can compile then it is bug free.
tim | 2014-08-01 00:31:19
Mmm, I used Ocaml for quite a few years, but I guess you could claim that is just surface level knowledge. I'm an admirer of the Ocaml language, but I'd argue that strong typing is orthogonal to easy development, as shown with Haskell's polymorphic classes.
Jon Harrop | 2006-11-12 07:42:07
OCaml was designed from the ground up to be fast. Consequently, it removes as much run-time type information as possible. The downside is that you can't print values because you don't know their types at run-time. This could be fixed by having two completely separate run-times but that's quite a lot of work (don't forget it is a research project!). You may be interested in F#. It has all the benefits that you're touting. It will almost certainly become open-source and already runs under Mono. Best yet, you can ask the author to add specific features and he does! :-)
jonathan | 2007-12-06 13:50:04
Lisp is functional and has a powerful print routine. (format t "~A" foo) Lisp is SO much nicer than ocaml. Ocaml is a pain in the ass.
luke | 2009-08-24 23:04:20
@Victor: Python's strongly typed but dynamic. Weak typing is something else.
Daniil Baturin | 2015-04-06 08:21:06
For people coming here from search engines, there's also
tim | 2015-06-10 22:05:13
Oh, that's pretty neat!
Add a comment