The module system in OCaml sounds very nice (and we all know what the "O" for!). But there's still a bias towards a sort of static-ness in FP. For example, the use of abstract data types where a Java programmer may use a class hierarchy. Clients cannot extend an ADT: I can't make my own List in Haskell and pass it off to a function.
Regarding the OO "excess baggage," I would respond that what is "excess" depends on the nature of the system. I can understand dismissing that stuff when your program is self-contained. When the only code at play is your own, when you can statically enumerate every type, function call site, etc, it may be hard to see the value in those features.
My project is a shared library, and so is dynamically linked with code written by other teams, perhaps years ago, or even yet-to-written. The system is thus not my program in isolation, but an intimate collaboration between my component and client components. Runtime dispatch, inheritance, reflection, and even occasional mucking with meta-objects are the tools we use to cooperate. This is a type of extensibility that Haskell doesn't even try to support. I don't know about OCaml here.
(Alan Kay called this the "negotiation and strategy from the object’s point of view.")
In the same way that many recommend programming to interfaces rather than concrete classes in static OO languages, programming to typeclasses rather than concrete types (when you can't avoid that kind of dependency and write completely generic code) is an important recommendation in Haskell.
> The module system in OCaml sounds very nice (and we all know what the "O" for!). But there's still a bias towards a sort of static-ness in FP. For example, the use of abstract data types where a Java programmer may use a class hierarchy. Clients cannot extend an ADT: I can't make my own List in Haskell and pass it off to a function.
Depends on what your function accepts. If it takes explicitly a list, you're screwed, but it clearly was never intended to be generic. If it accepts something Foldable or Traversable, just make sure your data structure has an instance for these type classes.
In OCaml, you can have objects and inheritance if you absolutely want to, but you can get a lot out of structural typing before going there. If you want extensible ADTs, you can, but you need to plan for it by using polymorphic variants [1] at the expense of some safety.
> My project is a shared library, and so is dynamically linked with code written by other teams, perhaps years ago, or even yet-to-written.
Constructing a component architecture is the goal of many approaches, and shared libraries is one expression of that ideal. When a library is compatible with the calling application and the OS, a library can closely approximate an ideal component.
However, components, applications and OSs are not static but constantly changing. In order for a library to be a component used by many other entities, the library must be continually (at least frequently) curated to remain compatible with all the other components it cooperates with.
While the point of a library is to abstract an API so users of the library don't have to think about how it's implemented, the creator of the library must consider those details very deeply.
Whatever techniques or languages are used to create a library, OO, FP, both or neither, the most important consideration is that its source code is clear, concise, logical, and understandable. The library I create today will decay if not maintained, and if I'm not around, how easily can someone pick up where I left off?
The inevitable tricks employed making procedures or methods work in real code will not be obvious to our successors. Good ideas, even embodied in obsolete code can be useful if clearly expressed and adequately explained. Thorough documentation transforms the work into lasting value.
Regarding the OO "excess baggage," I would respond that what is "excess" depends on the nature of the system. I can understand dismissing that stuff when your program is self-contained. When the only code at play is your own, when you can statically enumerate every type, function call site, etc, it may be hard to see the value in those features.
My project is a shared library, and so is dynamically linked with code written by other teams, perhaps years ago, or even yet-to-written. The system is thus not my program in isolation, but an intimate collaboration between my component and client components. Runtime dispatch, inheritance, reflection, and even occasional mucking with meta-objects are the tools we use to cooperate. This is a type of extensibility that Haskell doesn't even try to support. I don't know about OCaml here.
(Alan Kay called this the "negotiation and strategy from the object’s point of view.")