alice
manual.


Alice Project

syntactic
enhancements


________ Overview ____________________________________________________

The extensions listed here are mostly syntactic sugar that is also expressible by other, less convenient means:


________ Lexical syntax extensions ___________________________________

Literals

Numeric literals may contain underscores to group digits:

val pi = 3.141_592_653_596
val billion = 1_000_000_000
val nibbles = 0wx_f300_4588

Moreover, binary integer and word literals are supported:

val ten  = 0b1010
val bits = 0wb1101_0010_1111_0010

Long identifiers

Long identifiers are not part of the lexical syntax, but of the context-free grammar. Consequently, there may be arbitrary white space separating the dots from the identifiers:

mod . submod (*Here!*) . subsub
    . f

________ Vectors _____________________________________________________

Following SML/NJ, Alice provides vector expressions and patterns:

val v = #[1, 2, 4, 1, 2]

fun f #[]  = 0
  | f #[n] = n
  | f  v   = 1

Vector syntax

atexp ::= ...
#[ exp1 , ... , expn ] vector (n≥0)
atpat ::= ...
#[ pat1 , ... , patn ] vector (n≥0)

________ Records _____________________________________________________

Record punning

While SML allows punning in record patterns (so that the left hand side of the former example is legal), it does not allow punning in record expressions. In Alice, the latter is also available as a simple derived form, dualing the derived form for patterns. For example, the declaration

fun f {a,b,c} = {a,b}

is understood as an abbreviation for

fun f {a = a, b = b, c = c} = {a = a, b = b}

The labels may have type annotations, i.e. {a : int, b} abbreviates {a = a : int, b = b}.

Functional record update

There also is syntax for functional record update. For example,

let
    val r = {a = 1, b = true, c = "hello"}
in
    {r where a = 3, c = "bye"}
end

evaluates to

{a = 3, b = true, c = "bye"}

Record syntax

atexp ::= ...
{ atexp where exprow } record update
exprow ::= ...
vid <: ty> <, exprow> label as expression

The expression atexp in a record update must have a record type that includes all fields contained in exprow. The types of the fields must match. The result of evaluating a record update is a record of the same type but with the fields occuring in exprow replacing the corresponding values of the original record.

Labels as expressions are a derived form:

vid <: ty> <, exprow> ==> vid = vid <: ty> <, exprow>

________ Patterns ____________________________________________________

Alternative patterns

Alternative patterns (also called or patterns) are present in SML/NJ as well and allow more compact case analysis:

fun f(1 | 2 | 3) = 0
  | f n          = n

The patterns nested inside an alternative pattern may bind variables, but all patterns must bind exactly the same set of variables with the same type.

Layered patterns

Layered patterns (also called as patterns) have been generalized to allow arbitrary patterns on both sides (in contrast to just an identifier on the left hand side as in SML). This is useful as it allows to put the identifier on either side:

fun f(xs as x::xr) = bla
fun g(x::xr as xs) = blo

Negated patterns

Negated patterns are a more special feature. A negated pattern matches exactly when the operand pattern does not match. This sometimes allows specifying cases in a more natural order, in particular in combination with or patterns:

fun f(non(1 | 2 | 3)) = 0
  | f n               = n

The nested pattern may bind variables, but these are not visible outside. (They may be useful for a local guard, for example.)

Guard patterns

The most important extension are pattern guards. These allow decorating patterns with boolean conditions. A guarded pattern matches, if the pattern matches and the guard expression evaluates to true. The guard expression can refer to variables bound by the nested pattern:

fun f(x,y) where (x = y) = g x
  | f(x,y)               = h y

Guards can be nested into other patterns. Any side effect produced by the guard expression occurs whenever the pattern is tried to be matched.

Pattern syntax

atpat ::= ...
( pat1 | ... | patn ) alternative (n≥2)
pat ::= ...
pat as pat layered (R)
non pat negated
pat where atexp guarded (L)

________ Recursive definitions _______________________________________

Recursive declarations

SML only allows function expressions on the right hand side of val rec. Alice is a bit more permissive. For example, one can construct cyclic lists:

val rec xs = 1::2::xs

Or regular trees:

datatype tree = LEAF | BRANCH of tree * tree

val rec tree = BRANCH(BRANCH(tree,LEAF), tree)

The right-hand sides of recursive bindings may be any expressions that are non-expansive (i.e. syntactic values) and match the corresponding left-hand side patterns (statically). Unfounded recursion is legal, but evaluates to futures that cannot be eliminated:

val rec (x,y) = (y,x)

Note that the same data structures are constructable by explicit use of promises.

Recursive expressions

Recursive values may be constructed directly without resorting to recursive declarations:

val l = rec xs => 1::2::xs

Recursive expression syntax

exp ::= ...
rec pat => exp recursion

Such rec expressions are expanded as a derived form:

rec pat => exp ==> let val rec vid as pat = exp in vid end

where vid is a new identifier.



last modified 2003/02/18 09:30