Home

Awesome

Cobalt

Cobalt is an acronym for COnstraint-BAsed Little Typechecker. In its core, it is an implementation of OutsideIn, the type checking and inference engine used in the GHC Haskell compiler since version 7, with extra support for higher-ranked types. Apart from the solver itself, it contains two implementations of constraint gathering:

Running

Cobalt comes with a web interface which you can use to typecheck a program, but also to inspect the gathered constraints and the solving process. The easiest way to get the interface is by running

stack run c -- serve p

where c can be either cobalt (for using traditional gathering) or cobalt-u (for using the extensible implementation). On the other hand p must be a number, which is the port in which the web interface will listed. Then, just point your browser to http://localhost:p :)

You might also load some predefined examples via the corresponding buttons. Most of them highlight either parts of the syntax or some of the Cobalt specific features, such as specialized type rules or higher-ranked types.

Syntax

In essence, Cobalt is just a typed lambda calculus with pattern matching, plus a language of constraints and axioms in the type level. Given that the main aim of Cobalt is studying type systems, the syntax for expressions those is quite rich:

polytype   := "{" tyvar "}" polytype
            | constraint* "=>" monotype
monotype   := tyvar
            | dataname monotype*
            | famname monotype*
            | monotype "->" monotype
            | "[" monotype "]"
            | "(" monotype "," monotype ")"
            | "(" monotype ")"
constraint := monotype ">" polytype
            | monotype "=" polytype
            | monotype "~" monotype
            | clsname monotype*

dataname := "'" identifier
famname  := "^" identifier
clsname  := "$" identifier

Note that, in order to keep the ideas as clear as possible, type constructors must be tagged with their sort by a one-symbol prefix. Data constructors such as 'Int or 'Bool have a quote at the beginning. Type families (which also include type synonyms) needs a caret in front of them ^F^. Finally, type classes are indicated by a dollar sign, such as $Eqor$Functor`.

Another change with respect to Haskell is that you must always annotate which variables are bound in a polymorphic data type. These bindings are indicated by {v}, where v is the variable you bind upon. For example, the type of == should be written as {a} $Eq a => a -> a -> 'Bool.

A program is made by a list of data, axiom, imports and definitions:

program := (data | axiom | import | defn)*

A definition is the specification of a value or function, and follows the syntax:

defn := termvar termvar* ("::" polytype)? "=" expr ("=>" okfail)? ";"

expr := intliteral
      | termvar
	  | "\" termvar "->" expr
	  | "\" termvar "::" polytype "->" expr
	  | expr expr
	  | "let" termvar "=" expr "in" expr
	  | "let" termvar "::" polytype "=" expr "in" expr
	  | "match" expr "with" dataname alt*
alt  := "|" termvar termvar* "->" expr

okfail := "ok" | "fail"

The main changes from Haskell syntax are:

When you want to give an environment to the type checker without actual expressions, you can import functions, which can later be used freely. Doing so is very easy, you just need to write import, the name and the type:

import := "import" termvar "::" polytype ";"

Data types in Cobalt are modelled in a special way. Instead of using an ADT-style declaration like in Haskell, their definition is split in two parts:

data := "data" dataname tyvar* ";"

Finally, we have axioms which define relations between constraints:

axiom := "axiom" ("{" tyvar "}")* monotype "~" monotype ";"
       | "axiom" ("{" tyvar "}")* constraint* "=>" clsname monotype* ";"
	   | "axiom" "injective" famname ";"
	   | "axiom" "defer" famname ";"
	   | "axiom" "synonym" ("{" tyvar "}")* monotype "~" monotype ";"

There are four main kinds of axioms in Cobalt:

From those basic blocks, we can describe two derived forms of axioms:

Specialized type rules

TODO