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.