views:

878

answers:

4

In Python map() works on any data that follows the sequence protocol. It does The Right Thing^TM whether I feed it a string or a list or even a tuple.

Can't I have my cake in OCaml too? Do I really have no other choice but to look at the collection type I'm using and find a corresponding List.map or an Array.map or a Buffer.map or a String.map? Some of these don't even exist! Is what I'm asking for unusual? I must be missing something.

+6  A: 

The closest you will get to this is the module Enum in OCaml Batteries Included (formerly of Extlib). Enum defines maps and folds over Enum.t; you just have to use a conversion to/from Enum.t for your datatype. The conversions can be fairly light-weight, because Enum.t is lazy.

What you really want is Haskell-style type classes, like Foldable and Functor (which generalizes "maps"). The Haskell libraries define instances of Foldable and Functor for lists, arrays, and trees. Another relevant technique is the "Scrap Your Boilerplate" approach to generic programming. Since OCaml doesn't support type classes or higher-kinded polymorphism, I don't think you'd be able to express patterns like these in its type system.

Chris Conway
In ML, the higher-order module system solves this problem.
Jon Harrop
I disagree that wrapping everything in a functor is a "solution" to this problem.
Chris Conway
You are disagreeing with a fact. Look at Okasaki's book for lots of examples and a comparison that explains why OCaml's solution is preferable to type classes in this context.
Jon Harrop
No, I am disagreeing with what you consider a solution to the OP's problem. Functors, whatever their advantages, do not provide the kind of syntactically lightweight overloading you get in Python and Haskell.
Chris Conway
If you want brevity then use objects: xs#map f. Either way you can easily express this in OCaml's type system.
Jon Harrop
+1  A: 

The problem is that each container has a different representation and requires different code for map/reduce to iterate over it. This is why there are separate functions. Most languages provide some sort of general interface for containers (such as the sequence protocol you mentioned) so functions like map/reduce can be implemented abstractly, but this is not done for the types you mentioned.

Jay Conrod
A: 

As long as you define a type t and val compare (: t->t->int) in your module, Map.Make will give you the map you want.

drhorrible
+1  A: 

There are two main solutions in OCaml:

  1. Jacques Garrigue already implemented a syntactically-light but inefficient approach for many data structures several years ago. You just wrap the collections in objects that provide a map method. Then you can do collection#map to use the map function for any kind of collection. This is more general than your requirements because it allows different kinds of data structures to be substituted at run time. However, this is not very useful in practice so the approach was never widely adopted.

  2. A syntactically-heavier but efficient, robust and static solution is to use functors to parameterize your code over the data structure you are using. This makes it trivial to reuse your code with different data structures. See Markus Mottl's OCaml translations of Okasaki's book "Purely Functional Data Structures" for some great examples.

If you aren't looking for that kind of power and just want brevity then, of course, you can just create a module alias with a shorter name (e.g. module S = String).

Jon Harrop