Mixins, late binding for overloaded functions, leftover problems

Posted on Sat Jun 18, 2016

More work in the ‘typed-languagebranch of Vicare, for both the expander and the built–in types infrastructure. The matching machinery that determines if two type specifications are super–type and sub–type has seen further development, but it is still unfinished.

Everything I discuss here is relative to code in the head of the ‘typed-language’ branch.

Mixins

Mixins are a way to add definition clauses to record–types and labels. Let’s consider this situation:

(define-record-type <alpha>
  (fields a))

(define-record-type <beta>
  (fields b))

(define-record-type <delta>
  (parent <alpha>)
  (fields v)
  (method (doit {O <delta>})
    (+ 1 (.v O))))

(define-record-type <gamma>
  (parent <beta>)
  (fields v)
  (method (doit {O <gamma>})
    (+ 1 (.v O))))

the definitions of <delta> and <gamma> share some clauses; both of them already have a parent type and multiple inheritance is not supported by Vicare. Is it possible to write the shared clauses only once and attach them to the record–type definitions? Yes, with mixins. The example above is equivalent to the following:

(define-record-type <alpha>
  (field a))

(define-record-type <beta>
  (field b))

(define-mixin <stuff>
  (field v)
  (method (doit {O <stuff>})
    (+ 1 (.v O))))

(define-record-type <delta>
  (parent <alpha>)
  (mixins <stuff>))

(define-record-type <gamma>
  (parent <beta>)
  (mixins <stuff>))

the syntax use of define-mixin associates a set of clauses to the identifier <stuff>; when the mixins clause is used in the body of a record–type definition:

  1. The clauses associated to the selected mixin identifier are retrieved as syntax object.
  2. All the instances of the identifier <stuff> are substituted with the identifier of the enclosing record–type; <delta> and <gamma> in the example.
  3. The resulting clauses are added to the enclosing definition.

Late binding for overloaded functions

I have implemented late binding for overloaded functions:

(define/overload (doit {O <string>})
  (list 'string O))

(define/overload (doit {O <fixnum>})
  (list 'fixnum O))

(doit (cast-signature (<top>) "ciao"))  ⇒ (string "ciao")
(doit (cast-signature (<top>) 123))     ⇒ (fixnum 123)

Before this feature: whenever, at expand–time, a matching specialised function was not found, a syntax violation was raised. With this feature: late binding code is inserted and we get an error only at run–time when the function is actually called.

I do not like this very much, so I have added a new warning enabled with the command line option -Woverloaded-function-late-binding: whenever a matching function is not found at expand–time, a warning condition is raised. This warning is also enabled when using the new command line option -Wextra.

Problem: recursive types

At present recursive type definitions are not supported. I want them. It is not possible to say this:

(define-type <list-syntax-object>
  (list-of <syntax-object>))

(define-type <vector-syntax-object>
  (vector-of <syntax-object>))

(define-type <syntax-object>
  (or <wrapped-syntax-object>
         <list-syntax-object>
       <vector-syntax-object>))

and it is not possible to say this:

(define-type <syntax-object>
  (or <wrapped-syntax-object>
      (list-of <syntax-object>)
      (vector-of <syntax-object>)))

Whenever the right–hand side of a define-type use is processed: the left–hand side syntactic identifier is bound (meaning that it is associated to a label in the lexical environment), but the syntactic binding is still “displaced” (meaning that the label is not yet associated to a syntactic binding descriptor).

Until now, I have been unable to devise an implementation that satisfies me. I am inclined towards using “forward declarations”, like the C language does; so the code above would look something like:

(define-type <syntax-object>
  (forward-definition))

(define-type <list-syntax-object>
  (list-of <syntax-object>))

(define-type <vector-syntax-object>
  (vector-of <syntax-object>))

(define-type <syntax-object>
  (or <wrapped-syntax-object>
         <list-syntax-object>
       <vector-syntax-object>))

but I do not know how it could be implemented internally: I would like an implementation with a small footprint.

Problem: type signatures for record constructors

At present, the definition of a record–type with constructor protocol looks like this:

(define-record-type <duo>
  (fields one two)
  (protocol
    (lambda (make-record)
      (lambda ({A <fixnum>} {B <fixnum>})
        (make-record A B)))))

the type information of the constructor function’s arguments is not propagated to the constructor syntax (new <duo> 1 2).

This is because the expression in the protocol clause can be anything that returns a protocol function and it goes through the machinery of record–constructor descriptors. Now, I want to keep the protocol stuff because it is standard and very flexible; but I also want to specify the constructor’s type signature in the record–type definition.

I could set–up a constructor-signature clause as follows:

(define-record-type <duo>
  (fields one two)
  (protocol
    (lambda (make-record)
      (lambda ({A <fixnum>} {B <fixnum>})
        (make-record A B))))
  (constructor-signature
    (lambda (<fixnum> <fixnum>) => (<duo>))))

but it would be an ugly duplicate. Should I accept the ugliness?