Introduction

We want type checking for Clojurescript.

[Design] New Host, New Types

Javascript has a different primitive types to JVM. What are they? Are they important enough to include in the type system for CLJS?

Lots of things in Java are Classes. Does Javascript have classes? Does GClosure have a similar notion? What does CLJS use?

What is a deftype in CLJS? eg. Is it a Class with fields? Is it just a structural type?

[Design, Theory] Built on Protocols

The types in TC are fairly sophisticated for combining interfaces and protocols. We have an `Extends` constructor that takes positive and negative information on things it can extend.

eg. `(Extends [(Seqable t)] :without [Number])` is a type that implements `(Seqable t)` and explicitly does not implement `Number`.

ClojureScript brings an interesting challenge with its core protocols, because there is no explicit inheritance. In Clojure, the corresponding solution is to use interfaces with inheritance. eg. `(IPersistentMap t)` is a `(Seqable t)` and `(IPersistentCollection t)` explicitly because the interface extends both these types.

In CLJS, `IMap` is a protocol. There is no explicit inheritance of `Seqable` or `ICollection`. This seems to imply that we need quite large type aliases like this one:

(def-alias Vec
  (TFn [[x :variance :covariant]]
    (Extends [(Seqable x)
              (IVector x)
              (IStack x)
              (ICollection x)
              (IAssociative Int x)
              ...])))


 

This complicates even further a problem I have in Typed Clojure.

Consider code like this:

(let [x (ann-form [] (Vec Any))
      _ (assert (every? number? x))
      ...]
  ...)

 

What do we update the type of x to? `(Seqable Number)`? `(ICollection Number)`? Some combination? Ideally we'd want the equivalent of `(Vec Number)`, but this seems pretty hard to achieve.

This kind of updating of an expressive collection type seems like it might be useful, but maybe it's just enough to update the `Seqable` or `ICollection` portion of the type?

[Implementation] High-level, write in CLJ vs CLJS

For high-level implementation, we have two options:

  1. Rewrite TC in CLJS
  2. Reuse the type infrastructure of TC and provide "type-checking as a service", callable from a CLJS REPL

Option 1 is a lot more effort. We probably need a full CLJS compiler available anyway, which probably also implies Java is available at development time.

Option 2 is probably the better way to go.

Clojurescript patches

These things need to be improved in Clojurescript: