16
votes

I'm looking at OCaml's functors. It looks to me pretty identical to the so called generic objects in C++/C#/Java. If you ignore Java's type erasion for now, and ignore the implementation details for C++ templates (I'm interested with the language feature), functors are quite indentical to generics. If I understand it correctly, functor gives you a new set of functions from a type you provide, so that for example

List<MyClass>.GetType() != List<MyOtherClass>.GetType()

But you could roughly rewrite OCaml's

#module Set =
   functor (Elt: ORDERED_TYPE) ->
     struct
       type element = Elt.t
       type set = element list
       let empty = []
       let rec add x s =
         match s with
           [] -> [x]
         | hd::tl ->
            match Elt.compare x hd with
              Equal   -> s         (* x is already in s *)
            | Less    -> x :: s    (* x is smaller than all elements of s *)
            | Greater -> hd :: add x tl
       let rec member x s =
         match s with
           [] -> false
         | hd::tl ->
             match Elt.compare x hd with
               Equal   -> true     (* x belongs to s *)
             | Less    -> false    (* x is smaller than all elements of s *)
             | Greater -> member x tl
     end;;

into C#

class Set<T> where T : ISortable
{
    private List<T> l = new List<T>();
    static public Set<T> empty = new Set<T>();
    public bool IsMember(T x) {return l.IndexOf(x) > -1;}
    public void Add(T x) {l.Add(x);}
}

Sure there's a slight different since a functor affects a Module (which is just a bunch of types function and values definitions, similar to C#'s namespace).

But is it just it? Are functors merely generics applied to namespaces? Or is there any signifcant different between functors and generics which I'm missing.

Even if functors are just generics-for-namespace, what's the significant advantage of that approach? Classes can also be used as ad-hoc namespaces using nested classes.

4

4 Answers

6
votes

But is it just it? Are functors merely generics applied to namespaces?

Yes, I think one can treat functors as "namespaces with generics", and that by itself would be very welcome in C++ where the only option left is to use classes with all static members which becomes pretty ugly soon. Comparing to C++ templates one huge advantage is the explicit signature on module parameters (this is what I believe C++0x concepts could become, but oops).

Also modules are quite different from namespaces (consider multiple structural signatures, abstract and private types).

Even if functors are just generics-for-namespace, what's the significant advantage of that approach? Classes can also be used as ad-hoc namespaces using nested classes.

Not sure whether it qualifies for significant, but namespaces can be opened, while class usage is explicitly qualified.

All in all - I think there is no obvious "significant advantage" of functors alone, it is just different approach to code modularization - ML style - and it fits nicely with the core language. Not sure whether comparing module system apart from the language makes much sense.

PS C++ templates and C# generics are also quite different so that comparing against them as a whole feels little strange.

5
votes

If I understand it correctly, functor gives you a new set of functions from a type you provide

More generally, functors map modules to modules. Your Set example maps a module adhering to the ORDERED_TYPE signature to a module implementing a set. The ORDERED_TYPE signature requires a type and a comparison function. Therefore, your C# is not equivalent because it parameterizes the set only over the type and not over the comparison function. So your C# can only implement one set type for each element type whereas the functor can implement many set modules for each element module, e.g. in ascending and descending order.

Even if functors are just generics-for-namespace, what's the significant advantage of that approach?

One major advantage of a higher-order module system is the ability to gradually refine interfaces. In OOP, everything is public or private (or sometimes protected or internal etc.). With modules, you can gradually refine module signatures at will giving more public access closer to the internals of a module and abstracting more and more of it away as you get further from that part of the code. I find that to be a considerable benefit.

Two examples where higher-order module systems shine compared to OOP are parameterizing data structure implementations over each other and building extensible graph libraries. See the section on "Structural abstraction" in Chris Okasaki's PhD thesis for examples of data structures parameterized over other data structures, e.g. a functor that converts a queue into a catenable list. See OCaml's excellent ocamlgraph library and the paper Designing a Generic Graph Library using ML Functors for an example of extensible and reusable graph algorithms using functors.

Classes can also be used as ad-hoc namespaces using nested classes.

In C#, you cannot parameterize classes over other classes. In C++, you can do some things like inheriting from a class passed in via a template.

Also, you can curry functors.

2
votes

Functors in SML are generative, so the abstract types produced by an application of a functor at one point in a program are not the same as the abstract types produced by the same application (i.e. same functor, same argument) at another point.

For example, in:

structure IntMap1 = MakeMap(Int)
(* ... some other file in some other persons code: *)
structure IntMap2 = MakeMap(Int)

You can't take a map produced by a function in IntMap1 and use it with a function from IntMap2, because IntMap1.t is a different abstract type to IntMap2.t.

In practice this means if your library has a function producing an IntMap.t then you must also supply the IntMap structure as part of your library, and if the user of your library wants to use his own (or another libraries) IntMap then he has to convert the values from your IntMap to his IntMap - even though they are already structurally equivalent.

The alternative is to make your library a functor itself, and require the user of the library to apply that functor with their choice of IntMap. This also requires the user of the library to do more work than ideal. Especially when your library not only uses IntMap, but also other kinds of Map, and various Sets, and others.

With generics, OTOH, it is quite easy to write a library producing a Map, and have that value work with other libraries functions that take Map.

1
votes

I just found a source that may help you with your problem - as OCaml has a different meaning for functors:

http://books.google.de/books?id=lfTv3iU0p8sC&pg=PA160&lpg=PA160&dq=ocaml+functor&source=bl&ots=vu0sdIB3ja&sig=OhGGcBdaIUR-3-UU05W1VoXQPKc&hl=de&ei=u2e8SqqCNI7R-Qa43IHSCw&sa=X&oi=book_result&ct=result&resnum=9#v=onepage&q=ocaml%20functor&f=false

still - I find it confusing if the same word is used for different concepts.


I don't know if OCaml has a different meaning - but normally a Functor is a "Function object" (see here: http://en.wikipedia.org/wiki/Function_object). This is totally different to generics (see here: http://en.wikipedia.org/wiki/Generic_programming)

A function object is an object that can be used as a function. Generics are a way to parametrize objects. Generics are kind of orthogonally to inheritance (which specializes objects). Generics introduce typesafety and reduce the need for casting. Functors are an improved function pointer.