More for the typed language (mostly)

Posted on April 21, 2016

Sheesh! This is the first post of year 2016… and it is April already! I worked on the ‘typed-languagebranch of Vicare, mostly on the expander. The typed language is moving forwards, step by step; there is still a lot to do.

Despite being called ‘typed-language’: I am using it as if it is the master branch. This is bad. There have been so many changes that I hesitate to merge it into the actual master; I keep telling to myself: I have to do this first, I have to do that first. Well… I will not merge it tomorrow, because I have to do stuff first.

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

Import sets for modules

When importing a module: it is now possible to use the full syntactic binding name modifiers. Example:

(module it
  (red blue)
  (define red 1)
  (define blue 2))

(define (doit)
  (import (prefix it private::))
  (fprintf (current-error-port)
           "~a, ~a\n" private::red private::blue))

this should make modules usable as full namespace things, like in other languages. Indeed, I have removed the ugly library (vicare language-extensions namespaces) (still there in the master branch) because it is now useless.

Type annotations

The biggest development in the last four months is about type annotations: forms that specify which type a variable has and a value is expected to have, when the typed language is enabled. Simple type annotations where already present (I called them tags before); in this example, the type annotation is the identifier <string>:

(define {O <string>}
  "ciao")

(.length O)     ⇒ 4

There are more now:

(is-a? "123" (or <false> <string>))     ⇒ #t
(is-a? #f    (or <false> <string>))     ⇒ #t
(is-a? 99    (or <false> <string>))     ⇒ #f

(is-a? 123   (and <fixnum> <positive>)) ⇒ #t

(is-a? 123     (not <vector>))          ⇒ #t
(is-a? '#(123) (not <vector>))          ⇒ #f

(is-a? '(1 2 3) (list-of <positive-fixnum>))    ⇒ #t
(is-a? '(1 "2") (list <fixnum> <string>))       ⇒ #t

(is-a? '(1 "b" c)
        (pair <fixnum>
          (pair <string>
            (pair <symbol> <null>))))
⇒ #t

there is also support for closure object’s signatures:

(type-annotation-super-and-sub?
   (lambda (<fixnum>)              => (<fixnum>))
   (lambda (<non-negative-fixnum>) => (<positive-fixnum>)))
⇒ #t

which will be useful in some future when I will attempt an implementation of interfaces. Unfortunately there is no way to validate the type of a closure object at run–time; this is problematic:

(is-a? display
       (case-lambda
         ((<top>)                       => (<void>))
         ((<top> <textual-output-port>) => (<void>))))
⇒ #t

(is-a? display
       (lambda (<fixnum>) => (<fixnum>)))
⇒ #f

(is-a? (unsafe-cast-signature (<procedure>) display)
       (lambda (<fixnum>) => (<fixnum>)))
⇒ #t  ;;wrong!!!

the run–time predicate for all the type annotations describing closure objects is procedure?. What can I do?

It is possible to define aliases for type annotations:

(define-type <string-maybe>
  (or <false> <string>))

In the documentation file vicare-scheme there is a node ‘types’ which describes the typed language; it is WIP3 but enough up–to–date to be useful. Readers will notice that some type annotations’ syntaxes are similar (but not equal) to Typed Racket’s ones; this is inevitable: they chose good names.

Type informations propagation

Some built–in syntaxes (if, and, or) propagate type informations correctly:

(type-of (and 1 "ciao" 'blue))
⇒ #[signature (<symbol>)]

(type-of (and (read) 123))
⇒ #[signature ((or <positive-fixnum> <false>))]

(type-of
  (or (cast-signature ((or <false> <string>)) (read))
      (cast-signature ((or <false> <fixnum>)) (read))))
⇒ #[signature ((or <string> <false> <fixnum>))]

cond is built upon if:

(type-of
  (cond ((read) 123)
        ((read) "ciao")
        (else   'blue)))
⇒ #[signature ((or <positive-fixnum> <string> <symbol>))]

(type-of
  (cond ((read) 123)
        ((read) => (lambda ({_ <string>} x) "ciao"))
        (else   'blue)))
⇒ #[signature ((or <positive-fixnum> <string> <symbol>))]

other syntaxes (case) do not do it yet:

(type-of
  (case (read)
    ((a)   123)
    ((b)   "ciao")
    (else  'blue)))
⇒ #[signature <list>]

a standalone <list> identifier means: unspecified number of values, of unspecified type. This is what I am working on these days, mostly. For correct type propagation: non–core macros must be reimplemented as core macros.

Right–hand side type propagation

There was once support for RHS type propagation. It means this:

(define O
  (new <vector> 1 2 3))

(type-of O)     ⇒ #[signature (<vector>)]

it is not needed to explicitly specify the type annotation for the variable O because the expander figures it out. At some point, I removed such feature because it was problematic with the type infrastructure implemented back then.

It is now time to reconsider this feature. I will try something about it.


Footnotes

(3)

Work–in–progress. I went crazy the first time I saw this acronym in source code: what is this design pattern I never heard about?!?