This is intended to be the first in a series of posts talking about
the design principles behind core, Jane Street's alternative to
OCaml's standard library.
It's worth noting that we haven't quite fully achieved any of our
design goals. Core is at the center of a complicated and evolving
software infrastructure, and it takes longer to force changes through
that infrastructure that it does to figure out what changes should be
made. So these principles serve as both a guide to how the library is
currently laid out as well as an indication of what kinds of changes
are likely to come over the next year or so.
We are proud to announce the first public release of core, Jane
Street's own alternative to OCaml's standard library. We use this
library as the base for our own development, and we hope people on the
outside will find some use for it as well.
Here's a type-checking problem I ran into today. I had a module with a variant type matching a signature that exposed the variant type.
module type S = sig type t = A | B end module M : S = struct type t = A | B end
I wanted to extend the module with some new functions, and match a new signature that extended the original signature. Easy, right?
Unlike let declarations, type declarations in OCaml are
automatically recursive. This seems harmless at first, but it
actually causes more trouble than it's worth. To see why, let's look
at an example.
I just got back from visiting Northeastern and Harvard where I yet again flogged a version of my POPL talk. Olin Shivers was my host at Northeastern and Greg Morrisett at Harvard. It was a bit of a rushed visit, but a lot of fun nonetheless.
Both Greg and Olin are very interested in making the next big jump in programming languages, and they both think that that next step will require better ways of reasoning statically about programs. I think they're dead-on in terms of what the right direction to go is, but I think they've got their work cut out for them. It will be hard to beat ML because ML sits in a kind of sweet spot; make it a little bit better in one aspect, and you give something up in another.
One of the annoyances of using monads in OCaml is that the syntax is
awkward. You can see why if you look at a simple example. Assume
that you're using the usual option monad. If we define
>>= to be the bind operator, you might end up with a
piece of code that looks like this:
let f x_opt y_opt z_opt = x_opt >>= (fun x -> y_opt >>= (fun y -> z_opt >>= (fun z -> return (x + y + z))))
This is awful for two reasons: the indentation is absurdly deep, and secondly, and there are too many closing parens at the end.
Here's another puzzle:
Is it possible in OCaml to define a variable-argument function? For
example, can one define a function f and values a and z such
that the following assertions hold:
assert (f z = 0); assert (f a z = 1); assert (f a a z = 2); assert (f a a a z = 3); ...
At Jane Street, we end up writing lots of messaging protocols, and many of these protocols end up being simple RPC-style protocols, i.e., protocols with a client and a server, where communication is done in a simple query/response style.
I've always found the writing of these protocols rather unsatisfying, because I could never find a clean way of writing down the types. In the following, I'd like to describe some nice tricks I've learned recently for specifying these protocols more cleanly.
In OCaml, referring to constructors defined in other modules can be somewhat awkward. Suppose we have a module like the following.
module Example = struct type t = Foo | Bar | Baz end
To write a function that pattern matches on values of type
Example.t we could directly refer to the variants as follows.
let f e = match e with | Example.Foo -> ... | Example.Bar -> ... | Example.Baz -> ...
That is pretty verbose.
The Unix module defines the Unix_error exception constructor.
module Unix : sig exception Unix_error of error * string * string ... end
Suppose you want to create your own My_unix module that defines some
Unix utility functions and exports the same Unix_error. How would
you do it?