[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Exposing monomorphic predicates/accessors



On Sat, 13 Nov 2004, Andre van Tonder wrote:

I am considering extending the syntax to expose monomorphic predicates and accessors in addition to the current polymorphic versions.

 (define-record-type point (make-point x y) (point? just-point?)
   (x (point.x just-point.x) (point.x-set! just-point.x-set!))
   (y (point.y just-point.y) (point.y-set! just-point.y-set!)))

Sorry for responding to myself, but I think the heaviness of this
syntax may be a sign that the way subtyping currently works might
not be the best, and that one can perhaps improve on it by orthogonalizing
the mechanism for the definition of new types and the mechanism for polymorphism.

Currently, the definition

  (define-record-type point (make-point x y) point? ......)

is in fact doing two unrelated things.  It introduces a new disjoint type,
instances of which can be constructed using (make-point ...), but then
goes on to introduce a polymorphic predicate and polymorphic accessors that are not specific to that type. For example, it is impossible to test whether a value has been constructed using the constructor (make-point ...) as opposed to some arbitrary future subtype of point. In other words, looking at the polymorphic predicate and the accessors, it appears as if the 'point' definition is declaring a whole class of types (in the sense of Haskell), so what is the constructor doing there?

Also, because the predicate point? has to be generic even in the absence of subtypes (unless we do a whole-program analysis), we lose performance even if we do not ever want to use the point type polymorphically. And since we have replaced SRFI-9 monomorphic records, we cannot use the latter for these types and SRFI-57 polymorphic records for some other types in the same program.

To overcome these objections, I propose the following:

    - Essentially keep the SRFI-9 semantics for define-record-type
      (with a small modification below).  All types introduced
      this way are disjoint and there is *no* subtyping.
    - Farm off the polymorphism to a new form, define-record-class
      (or perhaps define-record-interface, according to taste).

So we could, for example, define a class of types conforming to a <point interface as

   (define-record-class <point <point? (x <point.x) (y <point.y))

where <point? and <point.x, <point.y are polymorphic, and there is
*no* constructor declared. Here <point? can be read as "conforms to a subinterface of <point". Concrete types can then, if desired, be defined as belonging to a particular class via

   (define-record-type (point <point) (make-point x y) point?
      (x point.x)
      (y point.y))

The modification to SRFI-9 is in the type clause, which declares point as conforming to the <point interface. The procedures point?, point.x and point.y are monomorphic and can be implemented more efficiently than would have been possible for polymorphic versions.
We can then define, for example

  (define-record-type (color-point <point) (make-color-point x y hue)
     (x color-point.x)
     (y color-point.y)
     (hue color-point. hue))

The types point and color-point would be disjoint, in particular

  (point?   (make-color-point 1 2 3))   ==> #f
  (point.x  (make-color-point 1 2 3))   ==> error

but color-point conforms to the <point? interface

  (<point?  (make-color-point 1 2 3))  ==> #t
  (<point.x (make-color-point 1 2 3))  ==> 1

Unless there are strong objections, I will submit for review an implementation that uses SRFI-9 directly.

Andre