Enumerations, labels

Posted on April 26, 2016

More work in the ‘typed-languagebranch of Vicare, for both the expander and the built–in types infrastructure.

Everything I discuss here is relative to code in the head of the ‘typed-language’ branch. In this post I will take great pleasure in just copying and pasting chunks of text from the documentation’s Texinfo source files (life is hard, but sometimes less so).

Enumerations

Vicare already had the <symbol> type for symbol objects. It now also supports the type annotation:

(enumeration ?symbol0 ?symbol ...)

enumerations are used to match a symbol in a specified enumeration set:

(is-a? 'ciao (enumeration hello ciao salut ohayo))
⇒ #t

(is-a? 'blue (enumeration hello ciao salut ohayo))
⇒ #f

Enumeration type annotations are considered sub–types of <symbol>. We can combine enumerations in type annotations to filter out some symbols:

(define-type <my-symbols>
  (and (enumeration red blue green yellow purple)
       (enumeration blue yellow magenta)))

(is-a? 'blue <my-symbols>)      ⇒ #t
(is-a? 'red  <my-symbols>)      ⇒ #f

As special case, if we define an alias for an enumeration type annotation: we can use such identifier to validate symbols. Example:

(define-type greetings
  (enumeration hello ciao salut ohayo))

(is-a? 'ciao greetings) ⇒ #t
(greetings ciao)        ⇒ ciao
(greetings blue)        error→ symbol not in enumeration

The enumeration identifier is indeed used in the implementation of the define-enumeration built–in syntax:

(define-enumeration greetings
  (hello ciao salut ohayo)
  make-greetings)

(is-a? 'ciao greetings) ⇒ #t
(greetings ciao)        ⇒ ciao
(greetings blue)        error→ symbol not in enumeration

Label types

I needed to code something fun, so I implemented label types; these entities where already (in a similar form) in the Nausicaa libraries. Labels are types built on top of other types: we put type labels on values of a parent type to handle them locally in a special way. There are two kinds of labels:

Label types are defined using the syntax define-label, exported by the library (vicare language-extensions labels); labels are implemented half in the expander itself and half in this external library. A define-label use can contain any of the following clauses, much similar to define-record-type:

parent
type-predicate
equality-predicate
comparison-procedure
hash-function
method
case-method

where parent is mandatory; the parent of a label type can be any type annotation.

The following example defines a label <String> that is just a synonym for <string>:

(define-label <String>
  (parent <string>))

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

(.length O)     ⇒ 4
(.hash   O)     → (string-hash O)

now let’s define a custom hash function (we ignore the parent hash function that gets passed as parent-func argument):

(define-label <String>
  (parent <string>)
  (hash-function
    (lambda (parent-func)
      (lambda (S)
        (if (string-empty? S)
            0
          (char-hash (string-ref S 0)))))))

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

(.hash O)    ≡ (char-hash #\c)

now let’s define a method for appending prefixes and suffixes:

(define-label <String>
  (parent <string>)
  (case-method append
    (({_ <String>} {O <String>} {suff <String>})
     (string-append O suff))
    (({_ <String>} {O <String>} {pref <String>} {suff <String>})
     (string-append pref O suff))))

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

(.append O "-suff")             ⇒ "ciao-suff"
(.append O "pref-" "-suff")     ⇒ "pref-ciao-suff"

(.length (.append O "pref-" "-suff"))
⇒ 14

Now let’s define a label type to represent fixnums returned by comparison procedures (‘-1’, ‘0’, ‘+1’):

(define-label <comparison-fixnum>
  (parent (or <non-negative-fixnum> <negative-fixnum>))
  (type-predicate
    (lambda (parent-pred)
      (lambda (obj)
        (and (parent-pred obj)
             (fx<=? obj +1)
             (fx>=? obj -1))))))

(is-a? +1 <comparison-fixnum>)  ⇒ #t
(is-a? -1 <comparison-fixnum>)  ⇒ #t
(is-a?  0 <comparison-fixnum>)  ⇒ #t

(is-a? +2 <comparison-fixnum>)  ⇒ #f
(is-a? -2 <comparison-fixnum>)  ⇒ #f

On the sorry state of error messages

I am well aware that, whenever a type error is found by the expander, the displayed error message is hard to read by humans. The message is just a dump of the contents of a compound condition object:

vicare> (display 1 2)
Unhandled exception
 Condition components:
   1. &who: chi-application
   2. &message: "expand-time mismatch between closure object's arguments signatures and operands signature"
   3. &expand-time-type-signature-warning
   4. &syntax:
       form: #[syntax expr=(display 1 2)]
       subform: #[id display]
   5. &application-operator-expression: #[id display]
   6. &application-operands-expressions: #[syntax expr=(1 2)]
   7. &arguments-signatures: (#[signature (<top>)]
  #[signature (<top> <textual-output-port>)])
   8. &operands-signature: #[signature (<positive-fixnum> <positive-fixnum>)]

notice that this thing is for a very simple function; it gets much worse with functions having many clauses with many arguments.

In one of the possible futures of the Universe: I will implement a special exception handler in the expander, so that these condition objects are parsed and readable text is printed. It will not be easy because there is a lot of information to be shown…

The Vicare Manifesto

No there is no such thing, yet. I have read Racket’s one and I have seen Professor Felleisen explain it in a tech talk: it is great that people can state their philosophy in driving a project.

Ahem… at present, the only thing I can say about Vicare is: if you like the Scheme language and procedural macros, give it a try (it will have static typing, too!). Not much… huh?