Haskell has a function fmap which can map over a number of different datatypes. For example, fmap can map a function over both a List and a Maybe (the equivalent of an option in OCaml):

Prelude> fmap (+ 1) [1,2] [2,3] Prelude> fmap (+ 1) (Just 3) Just 4

Unfortunately, the equivalent is impossible in OCaml. That is, there’s no way to define an OCaml value fmap so that the two expressions:

# fmap [1;2] ~f:((+) 1) # fmap (Some 3) ~f:((+) 1)

both typecheck and evaluate to the right value.

Even if we eliminate the complexity of type inference by specifying the type explicitly, we can’t define fmap so that the two expressions:

# fmap ([1;2] : _ list) ~f:((+) 1) # fmap (Some 3 : _ option) ~f:((+) 1)

typecheck and evaluate to the right value.

However, the Generic module in Jane Street’s Core_extended library will let us do exactly that with just a trivial syntactic change. But before continuing, I’ll warn you that the Generic module is not necessarily something you’d want to use in real world code; it falls much more in the “cute trick” category. But with that caveat, let’s look at our example using Generic :

# open Core.Std;; # open Core_extended.Generic;; # map ([1;2] >: __ list) ~f:((+) 1);; - : int list = [2; 3] # map (Some 3 >: __ option) ~f:((+) 1);; - : int option = Some 4

Note that, after opening the Generic module, all we did to the previous example was change : to >: and _ to __ . (Also, the Generic module calls the mapping function map instead of fmap , but that’s inconsequential.)

Of course, the trick is that >: , __ , list , and option are actually values defined by the Generic module in such a way that their intended usage looks like a type annotation.

Note that these “types” are nestable as you would expect real types to be:

# map ([None; Some 3] >: __ option list) ~f:((+) 1);; - : int option list = [None; Some 4]

This means that you can change what map does just by changing the “type” you assign to its argument:

# map ([None; Some 3] >: __ option list) ~f:(fun _ -> ());; - : unit option list = [None; Some ()] # map ([None; Some 3] >: __ list) ~f:(fun _ -> ());; - : unit list = [(); ()]

The Generic module also defines a generic fold function so that you can accumulate values at any “depth” in your value:

# fold ([[Some 3; None]; [Some 5; Some 2]] >: __ option list list) ~init:0 ~f:(+);; - : int = 10

Not every “type” formable is __ followed by some sequence of option s and list s: for example, Generic also provides string (considered as a container of characters):

# map ([Some "foo"; None; Some "bar"] >: string option list) ~f:Char.uppercase;; - : string option list = [Some "FOO"; None; Some "BAR"]

Note that the fact that the “types” are nestable means that these values must have unusual definitions: in particular, __ (and string ) are functions which must be able to take a variable number of arguments. Indeed, these values are defined using a technique sweeks wrote about in a blog post on variable argument functions: the f and z in sweeks’s post are analogous here to __ and >: respectively.

Here’s the definition of the primitive values we’ve used so far ( Generic actually defines a few more):

let __ k = k (fun f x -> f x) let ( >: ) x t y = t (fun x -> x) y x let map x ~f = x f let string k = k (fun f -> String.map ~f) let list map k = k (fun f -> List.map ~f:(map f)) let option map k = k (fun f -> Option.map ~f:(map f))

The types of these turn out to be extremely unhelpful, and you can’t really use them to figure out how to use these values. For example, here is the type of >: (and this isn’t just the inferred type of the above definition, this is the type which must actually be exposed to use >: ):

val ( >: ) : 'a -> (('b -> 'b) -> 'c -> 'a -> 'd) -> 'c -> 'd

Finally, is this module actually used? The answer is no. As far as I know, it’s used nowhere in Jane Street’s codebase. But it’s still kind of cute.