Recall that WebL values have a value type that determines to a large extent what can be done with the value (i.e. what operators can be applied to it). The following paragraphs will explain the characteristics of each value type and give examples.
The keyword "nil" denotes a special value that indicates that a variable has no value. Note that we refer both to the value and the value type as nil. Variables that are declared without an initial value are initialized to the nil value.
The lexical constants true and false evaluate to a value of type bool (short for boolean). Expressions containing operators that compare values (for example equal or less than) also evaluate to a boolean. Boolean expressions can be combined with logical and and logical or operators, and are evaluated in a short-circuited fashion.
A lexical character constant evaluates to a value of type char (or character). Characters are enclosed with single quotes. The internal coding of characters is Unicode. Character expressions may contain escape sequences that denote special characters. See Escape Sequences lists the escape sequences used in WebL.
A lexical string constant enclosed in double quotes evaluates to a value of type string. A string consists of a sequence of characters. The number of characters in a string is called its size. The empty string, denoted by "", contains no characters and has a size of zero. There is no limit to the string size. Strings may be wrap across several lines in WebL programs. Strings may also contain the escape sequences defined in See Escape Sequences. Note that escape sequences are not expanded in strings that are written with the back-quote character.
A lexical integer constant evaluates to a value of type int (or integer). The internal representation of integers is 64-bit signed two's-complement. Overflows or undeflows during integer computations do not throw exceptions.
A lexical real constant evaluates to a value of type real. The internal representation of reals is 64-bit IEEE 754 floating-point. No exceptions are thrown in real math.
The list constructor [] constructs values of type lists. All expressions between the square brackets are evaluated from left to right and the values inserted into the list in that sequence. The parallel list constructor [| |] evaluates the expression between the brackets in parallel (using multiple threads) instead of left to right. There is no restriction on the size of the list or the value types that it can contain.
The set constructor { } constructs values of type set. All the expressions between the curly braces are evaluated and their values inserted in the set (if not already an element of the set). There is no ordering between the elements, no restrictions on the type of elements, and no restriction on the size of the set.
The fun statement constructs values of type fun (or function). The format of the fun statement is of the following form:
The identifiers in brackets are the formal arguments of the function. A function can be applied with the same number of actual arguments enclosed in parenthesis following the function constructor. The actual arguments are evaluated and assigned in a paired manner to the formal arguments. The resulting variable bindings form a new context in which the statement sequence of the function is executed. The value of the applied function is the value of the function statement sequence (executed in the new context). For example,
evaluates a function that sums two numbers with arguments x=3 and y=4. More typically, functions are constructed and then assigned to variables for later use. For example, the following program calculates the factorial of 10:
The function context created during function execution is nested in the context in which the function was initially created. This allows the construction of higher order functions and closures. As an example, we define a function that returns a function that adds a certain number to its argument:
WebL requires the introduction of a new variable before its first use. This creates a problem when functions need to mutually refer to each other, because one function has not been introduced yet at the place where it is called. Fortunately, as functions are first class citizens in WebL, the problem can be overcome by declaring mutually recursive functions by introducing the function variable names and afterwards assigning them values:
The object constructor [. .] constructs values of type object. Objects have fields, each field having a specific field value. Fields are typically used to store object variables, functions, and methods inside the object. The fields themselves may be of any value type, i.e. they are untyped.
Indexing an object with the field retrieves the value of that field. There are two common ways of indexing into object fields:
The o.x notation denotes a field called x of object o.
The o[e] notation evaluates e to a value x, and retrieves the field x of a. This effectively makes the object an associative array.
The expression o.x and o["x"] refers to the same field. Trying to access an object field that does not exist will throw an exception. A special assignment expression ":=" is used to insert new fields into an object. (If the field already existed, its previous value is overridden.) A builtin function called DeleteField allows the removal of a field from an object.
[. x = 1, y = 1 + 1 .] // Object with x & y field
o["x"] // The same field again
o.y := "hello" // Defines field y of o
o[1+2] := 42 // Defines field 3 of o
o[4-1] // Accesses field 3 of o
Size(ToList(o)) // # fields of o
DeleteField(o, "x") // Remove field "x"
The associative array behavior is so useful that it is used for other WebL types too. These object-like types are called special objects. Examples include types page and piece. To the programmer these types look very similar to objects, but they have "hidden" state attached to them (i.e. they function as opaque data types).
Object fields are ordered in the sequence of their definition (i.e. left-to-right, top-to-bottom in the object). The ordering of fields only has little impact in programs; it only defines the sequence in which fields are enumerated and how objects are printed. It does however play an important role for certain functions where parameter ordering is important. (See Retrieving Page Objects) Note that there is no way to remove a field from an object.
Combining objects and functions allows us to program in an object-based or object-oriented manner. For example, the following program implements a bank account object with "methods" to deposit and withdraw money. Note how we need to pass the bank account object as first actual argument to the deposit and withdraw methods. In both cases the self formal argument refers to the bank account object.
self.balance = self.balance + amount
self.balance = self.balance - amount
myaccount.deposit(myaccount, 100); // Deposit $100
The meth constructor constructs values of type meth (or method). Methods behave in all aspects, except for application (i.e. execution), in the same manner as functions. They are in fact used as a notational short-hand for method invocation without the need to pass a self parameter. We can recode the bank account program with methods in the following manner:
self.balance = self.balance + amount
self.balance = self.balance - amount
myaccount.deposit(100); // Deposit $100
myaccount.withdraw(50); // Withdraw $50
As can be seen, the only difference from the previous program is the use of the meth keyword, and a convenient way of invoking methods. In fact, the internal implementation of methods is equivalent to the bank account object programmed only with functions.
Value types page, piece, pieceset and tag are an essential part of the WebL markup algebra. We will not go into details yet about these value types -- they are discussed in more detail in Chapter 4.