If you have programmed with algebraic datatypes (ADTs), then you will know that they are an expressive means to model data. ADTs are also good at representing a language’s abstract syntax tree.

Consider the following expression language mimicking Ola’s core expressions but introducing let-bindings as expressions:

t := bool | int      -- types
c := b | i           -- constants
e := (add e e)
   | (and e e)
   | x               -- Vars
   | let x = e in e  -- Let bindings

We can model both types and terms using ADTs in Idris, and reuse the Idris’ own represention of booleans and integers as constants.

data Ty = BOOL | INT -- Types

data Expr = B Bool -- Idris' boolean
          | I Int  -- Idris' integers
          | Add Expr Expr
          | And Expr Expr
          | Var String
          | Let String Expr Expr

You will notice, if you squint your eyes, that both ASTs and ADTs look similar. Great!

In both representations, however, you can write ill-typed and ill-scoped terms.

For example, compare the following expressions and their encoding with Idris:

AST ADT
(add 1 true) (Add (I 1) (B True))
(and 1 3) (And (I 1) (I 3))
(and x true) (And (Var "x") (B True))
let x = 1 in y (Let "x" (I 1) (Var "y"))

These expression’s are ill-typed!

Can you write a few more ill-typed and well-typed instances of Expr?

Can you write a few more ill-scoped and well-scoped instances of Expr?

We know from Olaf/Ola’s static semantics the typing rules that help us check if our syntax is well-typed. Using these rules we can write a type checker, in Idris, to decide if an instance of Expr is well-typed or not. The type signature could be:

check : (ctxt : List (String, Ty)) -> (expr : Expr) -> Bool

Can you complete the definition of check, and use Idris’ REPL to check terms?

A problem with this approach is that the checks are extrinsic to the definition of Expr itself. Thus, after checking is expr is well-typed we have no way of guaranteeing that it will remain well-typed. Further, when evaluating instances of Expr we have to catch all cases of where instances may be ill-typed.

In the next section we will show how dependent types enable us to embed typing rules directly into our term representation.