Introducing Automatic Annotations

dissoc

Automatic annotations logo

Annotations in Typed Clojure are essential, but tedious to write. We need a tool to generate type annotations automatically, based on the specifications we already give via tests.

In this post, I set the stage for why this tool is necessary. We discuss how it

  • provides valuable generated documentation at any point in the development cycle,
  • assists in the effort to generate contracts for untyped code to participate in gradual typing, and
  • helps static type checking by reducing the annotation effort.

This work is part of a crowdfunding effort, please support the campaign here!

The Annotation Burden

Below is a function written in Clojure. It creates a point hash-map that acts as a record.

Defining a point in Clojure

Here is the same function in Typed Clojure.

Defining a point in Typed Clojure

The only difference between the typed and untyped code is annotations. Most effort in porting to Typed Clojure is spent manually creating these annotations.

Why Automatic Annotations?

There are many motivations for automatic type annotations. Here are three.

On demand documentation

The first motivation is automatic documentation for partial programs.

Based on the current tests, what are the inputs/outputs of a function?

The REPL-driven style of Clojure development often leaves our functions in unfinished states. Using TDD means we exercise the paths of code we are interested in, and summaries of these types in annotations at any time can help write consistent code.

Contract generation

The second motivation is runtime checking.

Assert types as contracts

Using the infrastructure developed for gradual typing</a>, we can use annotations to assert runtime contracts.

Import untyped boundary

Gradual typing allows us to treat untyped code as safe when passed to typed code, since a runtime contract violation will prevent ill-typed operations.

Static type checking

The third motivation is fuel for type checking. Type annotations are needed to help the type system use local, instead of global, reasoning to type check code. This is the main spark for this project.

Annotations needed for top-level and library bindings

Of the two main kinds of annotations needed, library annotations are the most annoying. Each library function used requires an annotation.

Interestingly, these annotations do not need to be 100% accurate to be useful.

Goal: Mostly correct annotations

We generate ‘mostly correct’ annotations, and fixing these types is part of the workflow. Simple functions will successfully type check with the generated annotations, and more complicated functions will present you with a mostly complete annotation.

Introducing Automatic Annotations

This work generates type annotations

Our automatic annotator generates types that are useful to document and verify (typed or untyped) code, using either runtime or compile-time techniques.

Next posts

That wraps up my introduction to automatic annotations for Typed Clojure. Upcoming posts will discuss implementation strategies, generating clojure.spec types (Write Unit Tests, Get Generative Tests!) and unique annotation problems related to how Clojure programmers use data.

If you enjoyed this post, please consider donating to the campaign!

07 Aug 2016