Crosstwine Labs

Types are behind some of the highly-productive features of the Veda intelligent editor/IDE:

Note: you can try these by opening demo.il and following the instructions!

The Veda toolchain allows users to add type annotations to SKILL/SKILL++ language forms. This document details the syntax understood by the upcoming beta version and proposes a few extensions.

These annotations are optional, and type inference allows a few of them to make a big difference in how Veda understands your code.

Types and documentation comments also contribute to the output of the condense tool, which generates static definitions for “library IP,” enabling seamless plugging into customer development workflows:

("com.crosstwine.veda.condense.il.defs"
 library "my-library"
 symbols ((MyIpFunction1
           description (("<documentation elided>"))
           params ((filename
                    summary "<summary elided>"
                    type (or string symbol)))
           returns (nil
                    summary "<summary elided>"
                    type list))))

The type specifier syntax is heavily inspired by Common Lisp; some of the extensions proposed below have been “tuned” to work well with infix syntax.

Annotation Syntax

Currently Supported

The following type specifiers are currently fully supported:

foo
Single symbols denote named types, either built-in, such as fixnum or string, or user-defined, such as MyClass or MyStruct. As a special case, null is a synonym for list;
(or typespec*)
Analogous to CL:OR. As in CL, (or) denotes a type which has no members, and is used to annotate emptiness. E.g.: the return type of non-returning functions.
(lambda arg-typespec value-typespec)

Somewhat analogous to CL:FUNCTION, with arg-typespec having the following form:

arg-typespec::= (typespec*
                 [@optional typespec*]
                 [@key typespec*]
                 [@rest typespec])

Will be replaced by something closer to CL:FUNCTION at some point.

Proposed Extensions

We currently envision supporting the following type specifiers, but their syntax has not yet been set in stone (feel free to chime in!):

(member object*)
Expands to (or (eqv object)*);
(eqv object)
Analogous to CL:EQL;
(range list-or-array typespec)

Denotes a subtype of list or array which holds objects of type typespec. E.g.:

(range array string)

is equivalent to the following JSDoc/Closure type expression:

{Array.<string>}

Note that with range available, the null type specifier could expand to (range list (or));

(range table typespec)
Denotes a subtype of table which holds values of type typespec and keys of any type;
(range (arrayref table typespec1) typespec2)
Denotes a subtype of table which holds keys of type typespec1 and values of type typespec2;
(null typespec)
Expands to (or typespec null). This is a bit peculiar, but meshes nicely with infix syntax.

Infix Syntax

SKILL supports infix syntax, and we intend to enable it for type specifiers:

string||fixnum
Reads as (or string fixnum);
list:string
Reads as (range list string);
list:string||fixnum, list:(string||fixnum)
Reads as (range list (or string fixnum));
(list:string)||fixnum
Reads as (or (range list string) fixnum);
member(t nil)
Reads as (member t nil);
table[string]:number
Reads as (range (arrayref table string) number);
!symbol

Reads as (null symbol).

Note that this is a clear departure from the JSDoc/Closure notation, in which ! denotes not null. Our types are never “nullable” by default, however, so ! may double as a mnemonic for “attention!”

Note: “Unnatural” Alternative to !

The following has been considered a possible alternative to !—at the cost of using a bizarre compound type specifier:

~foo
Would expand to (or foo null) after being read as (bnot foo);

We do not plan to support it.

In Function Documentation

Type annotations can be used in JSDoc-inspired documentation comments. E.g. with [@type][@type] for structure and class slots:

;; A struct.
defstruct(MyStruct
  ;; Slot A.
  ;;
  ;; @type {string}
  a
  ;; @type (or fixnum flonum)
  b
  c
)

or with [@param][@param] and [@return][@return] for complex functions where argument type templates won’t do:

;; @param {MyStruct}          a - A!
;; @param (or fixnum null)    b B?
;; @param (or
;;         (lambda () string)
;;         port)              c
;; @return {symbol} Always t!
procedure( MyVeryComplexProc(a b c @rest rest)
  Delegate(b c a)
)

Note that in these contexts, a type specification must start with either { or (. This means that atomic types and some infix syntax expressions must be wrapped in braces, as in {MyStruct} above. Extra braces are currently disallowed.

Note that these braces are delimiters, and are not considered to be part of the specifier.

Conclusion

Types enable SKILL/SKILL++ developers to use highly-productive development tools and practices.

Documentation comments and a few type annotations go a long way toward delivering fully-documented “IP” which offers the same benefits to downstream users.

Feedback is welcome; we can be reached via:

Thank you for your attention!

Regards,
Damien Diederen.