1. 3
  1.  

  2. 3

    We can do ADTs with objects trivially, too. With our running Coord example, some languages allow “sealed interfaces” where the only implementations can be those in the same package. But otherwise just say “don’t implement this yourself, you’ve been warned.” Good enough.

    No, it’s not good enough. Try providing two implementations of the following abstract data type specification using objects:

    (* Purely functional, guaranteed non-empty heaps.
     * No exceptions are thrown under any circumstances. *)
    signature HEAP =
    sig
      type key
      type heap
      val pure : key -> heap (* singleton constructor *)
      val head : heap -> key
      val tail : heap -> heap option
      val ++ : heap * heap -> heap
    end
    

    You can’t.

    I call sealed (i.e. “no subclassing”) classes “abstract data types.”

    Again, no. The whole point to abstract data types is that users don’t need to know or care how they are implemented.

    I think ADTs are best represented as classes not intended for subclassing.

    Abstract data types are best internally represented as ordinary data structures. Even the intermediate states of manipulating complex data structures are best represented as ordinary data structures.

    With objects and ADTs, you can think in terms of properties: preconditions, postconditions, invariants, and the like for simple cases.

    This is true for abstract data types, but not objects. Type abstraction is precisely the mechanism that guarantees that an established invariant cannot be destroyed by third parties.

    With a functional language, we can:

    • For ADTs, just don’t export a data type’s constructors from its module.

    That’s what Haskell does, and it’s pretty terrible. Other languages provide more graceful type abstraction facilities.