Musings on the tagged language (part 4) (2015 February 18)

In a previous entry (see Musings on the tagged language (part 3) (2015 February 15)) I discussed the idea of allowing function overloading in the tagged language as the way of defining type tag methods and field accessors; function overloading is clos–like multimethods with dispatch at expand–time. It consists of having a global table of multimethod names that is queried every time we need to expand a form like:

(?identifier ?arg ...)

the expander searches for the name of ?identifier in the table; so the following expansions can take place:

(length '(1 2))         → (list-length       '(1 2))
(length '#(1 2))        → (vector-length     '#(1 2))
(length '#vu8(1 2))     → (bytevector-length '#vu8(1 2))
(length "1 2")          → (list-length       "1 2")

because the expander searches for the string "length" in the table (searching for the symbol length is more efficient but equivalent, because symbols are interned by using their string name as key; so what matters here, conceptually, is the string name).

Function overloading and their namespace

In the previous entry (see Musings on the tagged language (part 3) (2015 February 15)) I concluded that the cleanest way to select a multimethod from the namespace of multimethods would be to introduce a specific built–in syntax:

(method-call length ?obj)

and then think of some reader syntax to save typing a lot of characters; for example, “quoting” with a colon:

:(length ?obj)

Is this actually good? The more I think of it, the more transparent multimethod call is preferable; even if it breaks normal identifier binding resolution.

Function overloading vs. clos multimethods

clos’s multimethods have a number of features; some of them can be used with function overloading, too; other features cannot.

Function overloading vs. macro expansion

Function overloading should, really really, not interfere with macro expansion. If we do:

(import (vicare))

(add-method length (<string>) string-length)

(define (frob obj)
  ---)

(let-syntax ((length (syntax-rules ()
                       ((_ ?expr)
                        (frob ?expr))
                       )))
  (length "ciao"))

or:

(import (vicare))

(add-method length (<string>) string-length)

(define (frob obj)
  ---)

(define-syntax length
  (syntax-rules ()
    ((_ ?expr)
     (frob ?expr))
    ))

(length "ciao"))

the following expansion must take place:

(length "ciao") → (frob "ciao")

When processing the form:

(?identifier ?arg ...)

the expander must (in this order):

  1. Verify if ?identifier is bound to a macro in the local lexical context and if so: apply its macro transformer.
  2. Verify if the name of ?identifier is present in the global table of multimethods and if so: process the multimethod application.
  3. Verify if ?identifier is bound to a variable in the local lexical context and if so: process the binding.
  4. Raise an expand–time “unbound identifier” exception.

Local overloading

Vicare has standard local syntax definitions and fluid syntaxes (special identifiers that can be rebound without breaking the result of free-identifier=?). It goes like this:

#!r6rs
(import (vicare))

(define-fluid-syntax ciao  (identifier-syntax "ciao"))
(define-syntax       hello (identifier-syntax "hello"))

(define-syntax do-ciao  (identifier-syntax ciao))
(define-syntax do-hello (identifier-syntax hello))

(fluid-let-syntax ((ciao  (identifier-syntax "ohayo")))
  (display do-ciao)
  (newline))

(let-syntax       ((hello (identifier-syntax "ohayo")))
  (display do-hello)
  (newline))

(flush-output-port (current-output-port))
-| ohayo
-| hello

Similarly, it makes sense to allow local function overloading. It should go like this:

(import (vicare))

(add-method length (<string>) string-length)

(length "ciao") → (string-length "ciao")

(let-method ((length (<string>) frob))
  (length "ciao")) → (frob "ciao")

Missing methods

What should happen if a name is present in the global table of multimethods, but no specialised method matches a given arguments signature? Example:

(import (vicare))

(define (frob obj)
  (display str)
  (newline))

(define (frob-string str)
  (display str)
  (newline))

(add-method frob (<string>) frob-string)

(frob '(1 2))

there is no frob method for <list>, so:

Conclusions

Maybe, maybe, maybe things are getting into shape. At least in my mind.