The mLite Type System

Types in mLite are bound to values rather than variables, and they are checked dynamically at run time.

Primitive lists are heterogenous:

[ 1, "hello", #"x", true, 1.61, fn x=x ]

Algebraic Types

Algebraic compound data types can be defined by the user:

type :tree = :leaf (x) | :node (:tree, :tree)

Constructors are used to build complex data structures:

:node (:node (:leaf 1, :leaf 2)     ;;      o
       :node (:leaf 3, :leaf 4))    ;;     / \
                                    ;;    /   \
                                    ;;   o     o
                                    ;;  / \   / \
                                    ;; 1   2 3   4

Infix declaration turn constructors, like :node, into binary operators (here :node gets the same precedence as ::):

infix :node = ::

Infix operators simplify expressions syntax a lot:

(:leaf 1 :node :leaf 2) :node (:leaf 3 :node :leaf 4)

Even algebraic types are heterogenous:

(:leaf 1 :node "foo") :node (:leaf 3 :node :leaf [1,2,3])

However, they check their consistency, e.g. a :node accepts only a tuple of :tree's as its argument:

:node (:leaf 1, "oops")  ;; THIS WON'T WORK, because "oops" is not a :tree


Functions are polymorphic, with a much greater flexibility than in statically typed languages (at the expense of safety, of course).

For example, the q function, below, computes the quotient of two numbers when passed a tuple, and the reciprocal value when passed a singular argument:

fun q (x, y) = x / y
    | x      = 1 / x

The revnum function reverses a number:

fun revnum (0, b) = b
         | (a, b) = revnum (a div 10, b * 10 + a mod 10)
         | a      = revnum (a, 0)

When applied to a single argument, it calls itself with a tuple containing an accumulator (b). However, this is a technique that is only useful for ad-hoc hacking, because it allows you to do things like this (giving the unspecified result 98123):

revnum (123, 98)

So in production code, revnum would look like this:

  fun revn (0, b) = b
         | (a, b) = revn (a div 10, b * 10 + a mod 10)
  fun revnum a = revn (a, 0)

Pattern matching can be combined with guard expressions and type predicates. The following function can be used to compare any combination of characters, strings, or numbers, where a type mismatch results in a false result:

fun equals (a, b) where char a
                  = char b also a = b
         | (a, b) where str a
                  = str b also a = b
         | (a, b) where real a
                  = real b also a = b
         | _ = false

However, the built-in eql function should be used to do this, because it covers all types of the mLite language.