Musings on the tagged language (part 1) (2015 February 13 bis)

At present, Vicare’s master branch implements the embryo of a language with type annotations; it is called tagged language because calling it typed language seems presumptuous at this stage.

We can have the following repl session:

vicare> (enable-tagged-language)
vicare> (import (vicare expander tags))
vicare> (define-struct alpha ({a <string>} {b <number>} {c <pair>}))
vicare> (define-struct beta ({d alpha}))
vicare> (define {O beta} (make-beta (make-alpha "ciao" 123 '(1 . 2))))
vicare> O
$1 = #[struct type=beta]
vicare> (O d)
$1 = #[struct type=alpha]
vicare> ((O d) a)
$1 = "ciao"
vicare> (((O d) a) length)
$1 = 4
vicare> (((O d) c) car)
$1 = 1
vicare> (((O d) c) cdr)
$1 = 2
vicare> ("hello" length)
$1 = 5
vicare>(set! O 99)
Unhandled exception
 Condition components:
   1. &who: tag-assert-and-return
   2. &message: "expand-time return values signature mismatch"
   3. &expand-time-retvals-signature-violation:
       expected-signature: #["retvals-signature" (#<syntax expr=beta mark*=(top)>)]
       returned-signature: #["retvals-signature" (#<syntax expr=<fixnum> mark*=(top)>)]
   4. &syntax:
       form: (#<syntax expr=tag-assert-and-return mark*=(top)>
  (#<syntax expr=beta mark*=(top)>)
  #<syntax expr=99 mark*=(top)>)
       subform: #<syntax expr=99 mark*=(top)>
vicare>

The syntax:

(?object-reference ?field-name)
(?object-reference ?method-name)

resembles the dot–notation that traditionally allows to access fields and call class–specific methods in object–oriented languages; for this reason I say the tagged language implements object–oriented perfumed programming.

This syntax does not adhere to the Lisp style. The form:

("hello" length)

especially violates the primitive syntax as defined by r6rs, because the first subform is a constant rather than an identifier or a subexpression evaluating to a procedure. Should a string be an applicable object?

A more Lispy style would be:

(?field-name ?object-reference)
(?method-name ?object-reference)

which is actually what we have when using multimethods as defined by Common Lisp’s clos and all the Scheme extensions inspired by clos.

The Lispy style is problematic in Scheme because, as mandated by r6rs: all the identifiers bound to field accessors and method procedures must be exported by imported libraries. This means that (if we write by hand library forms, which is usually the case), we have to write all the names of fields and methods in the export list; there can be many of them.

The non–Lispy syntax solves this problem by having the expander consider the string in ("hello" length) an object of type <string> and searching for a method or field named ‘length’ in the definition of the built–in class <string>; with this mechanism, if we import the class identifier alone, we automatically have access to all its methods and fields. But it is not Lispy.

I like clos’s multimethods. Ideally I would implement the tagged language on top of expand–time multimethods whose definition is separated from the class definitions of the arguments; there would be some true freedom with this. The syntax of method calls would be:

(length "hello")

which is truly Lispy. In my ideal world, there would be no need to explicitly export and import the identifier length; this is not simple to achieve in a sound manner.