Documentation for MLsem

Basic usage
Syntax of the language
Caveat

Basic usage

The prototype consists of an online text editor (based on Monaco Editor).

Computed types are printed as lenses (above each definition). In case of error, the error message is printed instead.

Syntax

Definitions

A program is a sequence of toplevel statements:
prog ::= stmt* stmt ::= type id [(tvar, ..., tvar)] = t [and ...] | abstract type id [([+|-]tvar, ..., [+|-]tvar)] | let gid param* = e [and ...] | val gid [: t] param ::= pat | ( pat : t )

A statement can be either

Note that top-level definitions are considered recursive. A definition may refer to an identifier whose type signature is declared later. However, as top-level value definitions are typed sequentially, mutually-recursive definitions that have no explicit type signatures must be defined together using the and keyword.

Identifiers

Identifiers come in several flavours :

gid ::= id | ( op ) id ::= [a-z_][a-zA-Z0-9_']* cid ::= [A-Z][a-zA-Z0-9_']* tvar ::= '[a-zA-Z][a-zA-Z0-9_]*

Type expressions

t ::= simple_type | t -> t | t ,..., t | t :: t | [ tregex ] | { id :[?] t;...; id :[?] t [..]} | t | t | t & t | t \ t | ~ t | t where id tvar* = t [and ...] simple_type ::= b | id [(t, ..., t)] | cid [(t)] | tvar | (t) b ::= () | lit | (int..int) | (..int) | (int..) | (..) | any | empty | tuple | tuplen | true | false | bool | int | char | unit | string | list | nil

Types are the usual set-theoretic types with tuple (product), arrow and record constructors and union, intersection, difference and negation operators. A sequence type constructor is provided as well. The content of a sequence type can be a regular expression over types, using the usual operators (*, + and ?). For instance, the type [ 'a* (bool|int)? ] is equivalent to the type definition

t where t = 'a::t | s and s = [] | (bool | int)::[]

Record types are given by the list of their fields, that is labels associated with a type. A label may be absent (denoted by :?). Open record types end their field list with ... Polymorphic types can be instantiated by giving a list of type parameters. Basic types consists of literals (which denote their own singleton type), augmented with integer interval types, and set of builtin type identifiers.

Expressions

e ::= lit | gid | cid [(e)] | (e : t) | (e :> t) | (e :>> t) | (e :>>> t) | { [e with] id=e;...;id=e } | [ e; ... ;e ] | e ,..., e | fun param ... param -> e | let gid = e in e | let (pat) = e in e | if e [is t] then e else e | fst e | snd e | hd e | tl e | match e with [|] pat -> e | ... | pat -> e end | e ; e | if e [is t] do e [else e] end | while e [is t] do e end lit ::= [0-9]+ | 'char' | "char*" | float | () | false | true | []

Expressions can be

Patterns

pat ::= :simple_type | id | lit | cid [(pat)] | pat,...,pat | pat|pat | pat&pat | (pat) | [ pat; ... ;pat ] | { id [= pat];...; id [= pat] [..]} | id = lit

Patterns are essentially types with capture variables. For instance, the following expression

match y with | :[ int* ] -> false | ( x & :bool, :int ) | x = false -> x end

First checks whether y is a list of integers, in which case it returns false. Or, it tests whether either y is a pair of a Boolean and an integer, and captures the Boolean in x, or defines x to the constant false, and then returns x.

Caveat