Syndicate content Syndicate content

Core Principles: uniformity of interface

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.

Core has landed

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.

using with type on a variant type

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?

OCaml Annoyance #23: type declarations are implicitly recursive

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.

The ML sweet spot

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.

Bind without tears

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.

Variable-argument functions

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);
...

Typing RPCs

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.

Using let module for matching

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.

Extracting an exception from a module

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?

Syndicate content Syndicate content