Detailed Overview of Templet 3.0
A Scripting Language for the Web
18 January 1999

Copyright 1997, 1998, 1999 Ellis S. Cohen, All Rights Reserved

1. Introduction

The Templet language was developed initially as a server-side scripting language for generating HTML. However, it is suitable for client-side scripting as well, although availability of the client-side plug-in will follow availability of the rest of the system.

As a standalone system, Templet is suitable for many other text generation and string processing tasks, including XML processing. It is, in fact, a complete general-purpose programming language.

1.1 Relation to HTML

Templet was designed to integrate well with HTML and derives its main syntactic features from HTML. The principal driving force for Templet 1.0 was to make it as easy as possible for HTML developers to initially use a small set of Templet features that mimicked HTML features. As Templet has evolved, it has grown somewhat more complex, but the essential principle of HTML similarity remains. In particular

1.2 Power and Simplicity

Templet 3.0 is a programming language that has great power, yet can support frequently used idioms with great simplicity. For example, by combining its loop, string list, and substitution features, one can write
  [foreach Sandi|Andee|Kathleen]
    I loved [$].<br>
  [/foreach]
which will display

A local variable could have been used to store the list of names, so the display above could also have been generated by

  [do &names = Sandi|Andee|Kathleen]
  [foreach &names]
    I loved [$].<br>
  [/foreach]
Adding optional clauses provides even more power:
  [foreach Sandi|Andee|Kathleen][:trim][:separate ", and "]
    I loved [$]
  [/foreach]!
generates

Dynamic generation of input controls is just as easy. For example
  I loved
  <SELECT>
    [foreach Sandi|Andee|Kathleen]
      <OPTION VALUE="[$]">[$]
    [/foreach]
  </SELECT>
generates

Database extraction is almost as easy. The DB.SELECT function uses standard SQL, except the SQL itself can be dynamically generated in the same way that other text is. To display all marriages recorded in the Marriages table in the past year, write:

  [do
    &cutoff = [date];
    &cutoff.year--;
  ]
  [DB.SELECT husband, wife, marrydate
    FROM Marriages
    WHERE marrydate > [&cutoff]
  ]
      [$husband] was married to [$wife] on [$marrydate].<br>
  [/SELECT]
which could generate Including the appropriate HTML will generate a table instead:
  <TABLE BORDER=1 CELLSPACING=1>
    <TR>
    [foreach Husband|Wife|Date]
      <TD><B>[$]</B></TD>
    [/foreach]
    </TR>
  [do
    &cutoff = [date];
    &cutoff.year--;
  ]
  [DB.SELECT husband, wife, marrydate
    FROM Marriages
    WHERE marrydate > [&cutoff]
  ]
    <TR>
    [foreach [$husband]|[$wife]|[$marrydate]]
      <TD>[$]</TD>
    [/foreach]
    </TR>
  [/SELECT]
  </TABLE>
could generate

1.3 Features

Templet's syntactic and semantic model borrows features from a somewhat eclectic set of programming languages including (in alphabetical order) Alphard, APL, Java, Javascript, Perl, Scheme (and other LISPs), and Smalltalk.

A history of Templet can be found at the end of this document.

The Templet 3.0 design is distinguished by the following features:

Templet comes with a wide variety of built-in and library classes, including Class, Object, String, List, Dictionary, SortedList, SortedDictionary, Date, Time, Timestamp, Function, Environment, Conductor, Promise, LazyList, Enumeration, Stream, PatternMatcher, Thread, Lock, Database and Query.

Templet also includes classes that provide significant server-side functionality and web support, including transparent construction of URLs for links, (frame & image) sources, and forms. Exercising these URLs invokes specified server-side Templet functions, passing along request-specific values and/or automatically generated session-id's, which are either encoded in the URL or provided via cookies. These classes also provided functions that simplify construction of input elements, especially those with dynamically-determined initial values.

As a web-driven back-end, Templet can either run as a Java servlet or as a separate Templet server, depending on a servlet, other server plug-in or CGI program to pass appropriate HTTP requests on to the Templet server; other configurations can be readily supported.

Templet's built-in functions are implemented in Java; user-defined functions can be implemented in Java as well. Public Java API's for all Templet object are available for use within user-defined functions.

There is no present support for a security model. Currently we assume that all code is server-side and open. Any access controls (e.g. private fields) are to support programming safety rather than security.

Contact Ellis S. Cohen (e.cohen@acm.org) for more information on availability or on how you can participate in the implementation.

2.0 Features

This section covers the following topics:
  1. System Entities
  2. Environments
  3. Application and Parameters
  4. Constructing Values and Variables
  5. Fields and Aliases
  6. Infix Mode
  7. Advanced Parameter Topics
  8. Conductors
  9. Defining Functions
  10. Defining Classes
  11. Enumerations, Streams, Promises and LazyLists
  12. Compilation
  13. User-Defined Aliases and Mediators
  14. Web and Database Support

2.1 System Entities

2.1.1 Objects & Classes

All values are represented by objects. All objects, at least potentially, have fields, identified by a (1-based) index and/or by name. These fields can reference other objects. The indexed fields and named fields are (at least by default) separate, and do not overlap in any way. Consequently, objects can act (simultaneously) as both lists (which lookup by index) and dictionaries (which lookup by name). A large number of built-in functions operate on the fields of objects.

Every object has an associated class object. A class may specify that the indexed and/or named fields of its instances are missing, private or public to functions not associated with the class.

Most objects may be shared, that is, referenced from more than one field (in one or more objects). A class can specify whether its instances are shared; if they are not shared, then passing such an instance as a parameter, or storing it in a field makes a copy of it (although the system internally uses a copy-on-write mechanism, delaying the actual replication until required). Strings may not be shared. All other built-in objects may.

Every function is associated with some class. There are a number of kinds of functions, including ststaic and instance functions.

There is support for new classes defined using delegation, derivation, and extension of other classes. It is possible to test whether an object is of a class that extends (possibly transitively) some specified class, thereby guaranteeing that the object supports the instance functions defined by that class.

2.1.2 Aliases

Instead of holding an object, a field can hold an alias, a non-object entity, which only indirectly references an object. Aliases can be user-defined (via an underlying object), however, the standard built-in alias simply identifies a field in some other object -- that is, the alias holds a reference to an object plus a name/index, and represents the named/indexed field within that object.

If a field holds an alias, then ordinarily retrieving an object from that field actually (and transparently) retrieves the object it indirectly references. In the case of a standard alias, it transparently retrieves the object from the field the alias identifies (and if that field itself holds an alias, then from the field it identifies, etc.).

If a field holds a standard alias, then ordinarily storing an object in that field actually stores the object in the field the alias identifies (unless that field itself currently holds an alias, in which case the object is stored in the field it identifies, etc.)

Aliases for strings can be used to achieve the effect of passing strings (or other objects which are automatically copied) without copying them.

A field can hold a standard alias that references another field in the same object. In fact, an object is said to have bound names if all named fields hold aliases to indexed fields in the same object. an object is said to have bound indices if all indexed fields hold aliases to named fields in the same object.

2.1.3 Strings, Dictionaries & Lists

There are a wide variety of functions that operate on Strings, including search and replace functions that support pattern matching using nested regular expressions, including specific support for XML-elements. Strings also represent numbers, and the usual suite of functions on them are available.

Dictionaries are objects whose indexed fields are missing, and whose named fields are public. Consequently, they provide mappings from arbitrary strings (including from ones that would represent integers, since the indexed fields are missing) to arbitrary objects. There are built-in static functions that operate on any object with public named fields. Dictionaries have instance functions that correspond to each of these static functions.

Lists are objects whose named fields are missing, and whose indexed fields are public. There are built-in static functions that operate on any object with public indexed fields. Lists have instance functions that correspond to each of these static functions.

Strings themselves can be used to represent lists of strings, with the | character used to separate list elements. For example, the string hello|there|bub can either be treated as a single string (with the usual string functions applicable to it) or as a list of three strings (with list-like functions applicable to it). In fact, almost every List instance function has a corresponding String instance function that applies to strings treated as string lists. A string that does not contain any | character can be treated as a string list with a single element.

In APL-like manner, many operations are extended to work on lists. For example the sum function takes an arbitrary number of parameters. If these are all (strings representing) numbers, then the result is the sum of those numbers. But if any of the parameters is a list or string list, then all the parameters are converted to lists which are the length of the longest list (a single value is turned into a list, all elements having that value; a shorter list repeats its elements as needed), and the result is a list whose i'th element is the sum of the i'th elements of each of the parameter lists).

2.2 Environments

2.2.1 Environments and Contexts

Functions execute with an Environment object many of whose fields (at least conceptually) include objects that act as naming contexts for the executing function. The naming contexts are generally created automatically by Templet, and are generally of class Object. The names and roles of these fields are:

In a server-side environment, other fields hold context objects for

The environment has other non-context fields as well (discussed in more detail later), including

2.2.2 Variables

Local and parameter variable names are prefixed by special prefix characters which identify the context. Templet has four prefix characters:

Local variables can be either named or indexed; &ndx identifies the field named ndx in the local context, while &3 names the 3rd unnamed local variable. Named and indexed local variables are different. For convenience, &1 can be abbreviated as &.

Parameter variables are always indexed. That is, $1 (or just $) names the first parameter, $2 names the second parameter, etc. Parameter variables can be named as well, but (unlike local variables), a named parameter variable (e.g. $knt) always corresponds to some indexed parameter variable. In other words, parameter contexts always have bound names (if they have any named fields at all) -- all named fields in a parameter context contain aliases to indexed fields in the same context.

Instance variables are indexed and/or named depending on the class of the instance object. % by itself stands for the entire instance object; it is equivalent to ^this.

Variables without a special prefix (i.e. ones that start with a letter or an underscore) identify fields in the global context. In particular, the built-in and library class objects are available in the global context, as are many built-in (and some library) functions.

Note that any string can be used as a field name, however, not all fields can be directly named as variables -- in particular, field names that contains blanks, periods, colons, at signs, or hash marks cannot be used, since blanks are used as separators, and the other characters (as we shall see below) are used as naming operators. However, they can be enclosed in quotes as described below.

There are no variable declarations in Templet. Storing a value in an undeclared variable declares it. More generally, storing a value in an undefined field of an object defines it.

Accessing an undefined field just returns the special value null. In many cases, a null acts like an empty string, and both of them (as well as strings containing only blanks -- i.e. spaces, tabs, newlines, returns and formfeeds) act in many cases like (a string representing) the number zero. In fact, these are exactly the values that play the role of false in boolean operations; built-in operations that return booleans return a null for false and a 1 for true.

2.2.3 Functions and Environments

Functions, like other objects, are created at run-time. A function contains a reference to the environment in which it was defined. The class of that environment is the class associated with the function. Instance functions are invoked with an instance of their associated class.

When a function is invoked, the environment newly created for that invocation has fields for both its calling environment (^caller), and the environment in which it was defined (^definer). An existing function can be used to generate a new function with the same code, but with a different defining environment.

Various fields in a new invocation's environment may be inherited from either the calling or defining environment, depending upon attributes of the function. For example

Because an environment contains a reference to the calling environment, ordinary functions can act as interpreters (though an eval function is built-in). More generally, because functions, contexts and environments are ordinary objects, many reflection operations simply "fall out" rather than requiring a separate reflection API.

2.3 Application and Parameters

2.3.1 The Application Operation

The primary operation in Templet is application, which applies a target object to zero or more parameters, and which encompasses both accessing values and invoking functions.

If &s (i.e. the local variable named s) holds a string, then [&s] returns the string named by the variable, while if &s holds a function, then [&s] returns the result of invoking the function with no parameters. Similarly, if &v holds a list, then [&v 3] returns the 3rd element of the list, while if &v holds a function, then [&v 3] holds the results of invoking the function with 3 as its parameter. Each object's class determines how it interprets application, based on an optional apply instance function in the class object.

Parameters themselves can be evaluated, so, if &v holds a list, and if evaluating [&s] generates 3, then evaluating [&v [&s]] generates the 3rd element of &v. In fact, if &s is a string that holds 3, evaluating [&v &s] generates the 3rd element of &v as well, since List's apply function uses the VAR parameter mechanism that treats parameters beginning with one of the three variable prefix characters as a variable, and passes the object it references.

Functions can take multiple parameters, and both fixed and variable numbers of parameters are supported. Multiple parameters are separated by blanks (and are trimmed) so [sum 3 4] evaluates to 7. If a function is defined as having exactly a single parameter, then blanks do not act as parameter separators, and (by default) the parameter is not trimmed.

2.3.2 Parameter Mechanisms

Templet has a number of parameter mechanisms that vary along a number of dimensions --

The parameter mechanisms available are:

For QUOTE, DEF and VALDEF parameters, the actual parameters must be well-formed. That is, included square brackets, curly braces, or parentheses must be balanced and properly nested, or else null is passed instead.

For each parameter mechanism, Templet has a corresponding built-in function that takes a single parameter using the specified parameter mechanism and which returns the argument ultimately passed. For example, [ref sum] returns the object with the global name sum -- i.e. the built-in sum function.

2.3.3 Special Values and Applicators

Some special applicators relating to null are

  [null]  returns null
  [null! $x]  sets $x (an ALIAS parameter) to null
  [null? $x]  returns 1 if $x references a null, null otherwise
  [nonnull? $x]  return null if $x references a null, 1 otherwise
Similar applicators are available for space, newline, tab, zero, one, true, and false. The space, newline and tab functions can take an integer parameter, and the specified number of spaces, newlines, or tabs are generated.

Note that function which return booleans end (by convention) with a question mark, while functions that set variables or update the object held in a variable end (by convention) with an exclamation mark. For example [null! $x] is equivalent to [set! $x [null]].

Functions that set or update variables, but which also return some value (usually associated with the original state) end (by convention) with an at-sign, so, for example [set@ $x [null]] sets $x to null, but returns the object previously referenced by $x.

2.3.4 Quoted Strings as Parameters

A string literal can be surrounded by either single or double quotes. The standard Java backslash quoting conventions are used within strings to represent special characters. Inside a double-quoted string, \" represents a double-quote. Inside a single-quoted string, \' represents a single-quote. Of course, \\ represents a single backslash.

Quote marks only quote string literals when used as a non-QUOTE parameter or when used following the prefix character of a variable name. A quoted string cannot be used as the target of an application. Quoted strings have three uses:

If &d holds a dictionary, then either [&d john] or [&d "john"] returns the object indexed under the string john. However, to obtain an entry indexed under either the strings John Henry or $nam, the unquoted form cannot be used.

Similarly, &"my var" refers to the field named my var in the local context of the executing environment.

The quote function also provides quoting. It quotes its parameter and returns it, without treating any characters, including quotes or backslash, as special. However, the string must be well-formed.

2.4 Constructing Values and Variables

2.4.1 Concatenation

Concatenation is an implicit operation in Templet. If evaluating [foo 3] returns hello and [foo 5] returns there, then evaluating [foo 3] [foo 5]! results in hello there!.

However, when a parameter consists of multiple concatenated terms with top-level blanks, the blanks will not be treated as characters, but as parameter separators if the function can take multiple parameters. For example, if $fn can take a variable number of VAR (or VAL) parameters, then evaluating [$fn [foo 3] [foo 5]!] will call $fn with two arguments, hello and there!.

There are two functions that can be used to avoid this problem and pass the single string hello there! to $fn -- cat and val.

The concatenation function cat, which concatenates an arbitrary number of arguments could be used, with the example written as [$fn [cat [foo 3] " " [foo 5] "!"]]

The val function evaluates its single VAL argument. So the example above could have been written as [$fn [val [foo 3] [foo 5]!]].

The val function can also be used to quote string literals, except that backslash is not treated specially, and brackets do trigger application. So, If &d holds a dictionary, then [&d [val John Henry]] returns the object indexed under the string John Henry.

The result of implicitly concatenating an alias or non-string object along with blanks (in either order) is the original object or alias -- the blanks are ignored. However, concatenating a non-blank string with an alias or non-string object converts the object or alias to a string which is then concatenated with the non-blank string.

Implicit string conversion uses the built-in toString function. This first tries to use the object class's toString instance function if one is provided. The toString instance function of all the built-in or library class evaluates the detailed field of its thread-specific context object (via ^threadclass). If that evaluates to true, toString generates a more detailed representation of the instance.

2.4.2 Constructing Variable Names Dynamically

Variable names can be constructed dynamically. For example, suppose that $from holds the string a and that $to holds the string b. Then [set! $[$to] $[$from]] has the same effect as [set! $b $a] which sets the field named by $b (corresponding to an ALIAS parameter) to hold the value held by $a (corresponding to a VAR parameter).

However, if $from holds the string $a and $to holds the string $b. Then [set! [$to] [$from]] will set the field named by $b to just hold the string $a (not the object held by $a). Since the second parameter is a VAR parameter, the object named by $a is passed only if the unevaluated actual parameter itself begins with a prefix character. It doesn't; [$from] begins with a left bracket.

To obtain a reference to the object named by a dynamically constructed variable, the function ref can be used which has a single REF parameter. Its argument is evaluated and then treated as a variable name, and the object it names is returned. So, in the example above, [set! [$to] [ref [$from]]] will set $b to hold the value held by $a.

The target of an applicator is automatically treated as if it used a REF mechanism. If the result of evaluating the target is a string, it is treated a variable name, and the object it names is then taken to be the target. So, if evaluating [&foo] returns the string $baz and if $baz holds a list, then [[&foo] 3] results in evaluating [$baz 3] which returns the 3rd element of the list.

2.5 Fields and Aliases

Storing and retrieving values is generally accomplished using the functions get, put! and set!. These functions treat aliases transparently. However, there are other functions which deal with aliases explicitly. This sections describes the details of both of these kinds of functions.

2.5.1 Base Field Functions

An object can have both named and indexed fields, and these fields can hold either aliases or objects: [getBase object name/index] returns the object or alias held in the named or indexed field of the specified object. The first parameter is a REF parameter, and the second is a VAR parameter, so for example,

[putBase! object name/index entity] places the provided entity in the named or indexed field of the specified object. The first two parameters are a REF and VAR parameters. The last parameter is a VARBASE parameter; this allows an alias to be passed; if a VAR parameter was used, the alias would first be resolved to the object it references, and that would be stored instead. putBase@ is like putBase! except that it also returns the entity that was stored in the field.

2.5.2 Constructing Standard Aliases

A standard alias identifies a named or indexed field in an object. The function [field object name/index] returns an alias given the object (a REF parameter) and the name or index (a VAR parameter), so, if $lst names a list, then [field $lst 3] returns an alias to the 3rd field of the list.

The related function classField returns an alias to a field of the class object of a specified object. So, for example, [classField $lst size] returns an alias for the field named size in the list class object (i.e. the field that holds the list size function). [classField $obj $nam] is equivalent to [field [getClass $obj] $nam].

Finally, the function threadField returns an alias to a field of the thread-specific object associated with the class of a specified object. [threadField $obj $nam] is equivalent to [field [getThreadContext [getClass $obj]] $nam]. For example, evaluating [set! [threadField $obj detailed] [true]] causes subsequent string conversions of all objects of the same class as $obj in this thread to be detailed (since by convention, toString instance functions evaluate [[field ^threadclass detailed]] to determine whether to print a detailed representation of the instance. Note that applying the result of the field function has the following effect: if the detailed field holds a function, that function is evaluated; if it just holds a string, the value of that string is returned.)

2.5.3 Operations on Aliases

An alias itself directly references some entity -- either an object, or another alias. For example, a standard alias identifies a field in some object; that field holds an entity -- so a standard alias directly references the entity stored in the field it identifies.

[getDirect alias] returns the entity directly referenced by the specified alias (except that if it is passed an object, not an alias, the object itself is returned). So, for example, [getDirect [field $lst 3]] returns the entity stored in the 3rd field of the list -- it is equivalent to [getBase $lst 3].

getDirect has a REFBASE parameter, so if $x holds an alias to the 3rd field of $lst, then [getDirect $x] will also return the entity stored in the 3rd field of the list. getDirectAlias is similar to getDirect, but it returns null if the alias directly references an object.

[putDirect! alias entity] has two parameters -- a REFBASE naming the alias, and a VARBASE parameter identifying the entity to be stored. It stores the entity through the the alias (so it will now be directly referenced by it). putDirect@ is like putDirect! except that it also returns the entity that was directly referenced by the alias.

2.5.4 Following Aliases

If $lst names a list, and the 3rd element of that list holds an alias, then [getBase $lst 3] returns that alias. However, [get $lst 3] (which has two VAR parameters) will instead return the object referenced by that alias. If the alias directly references another alias, get will follow the chain of aliases until it reaches an object (or null).

If the 3rd element of $lst holds an object, then [put $lst 3 "Happy Days"] (with all 3 VAR parameters) will replace that object by the string "Happy Days". However, if the 3rd element of $lst holds an alias, then put follows the chain of aliases (internally using getDirectAlias) till it finds the final alias in the chain (the one that directly references an object or null). It then stores the string directly (using putDirect!) through the alias. There is also a put@ function.

The putEntity! function is just like put! except that its 3rd parameter is a VARBASE parameter as opposed to a VAR parameter. If its 3rd parameter is an alias, put! will resolve it to an object before storing it; putEntity! will not. putEntity@ is like putEntity! except that it also returns the entity replaced by the entity supplied.

2.5.5 Parameter Functions

For each parameter mechanism, there is a corresponding function which returns whatever is passed to it as transformed by the parameter mechanism. We now take a look at how four of these functions -- refbase, ref, basealias and alias -- work in various situations. Suppose that $lst and the field named lst in $obj hold a list, while $ali and the field named ali in $obj hold an alias that references some other kind of object

2.5.6 Assignment Functions

The set! function has two parameters -- the second is a VAR parameter that resolves to the object to be stored. The first is an ALIAS parameter that determines where the object is stored.

For example, [field &x foo] returns an alias for the field named foo in the object named by &x, and [set! [field &x foo] 20] sets that field to 20.

The setEntity! is like set!, except that its 2nd parameter is a VARBASE parameter; so an alias is not resolved to an object before being stored. There are also set@ and setEntity@ functions.

freeze!, freeze@, freezeBase!, freezeBase@, freezeEntity! and freezeEntity@ have the same effect as the corresponding set functions, but in addition freezes the field that was changed so that it cannot be subsequently changed (though the object it holds can be). [freeze! alias] and [freezeBase! alias] freeze the field with its current value.

2.5.7 Composite Names

Templet allows composite names to be constructed using two infix naming operators (even in prefix mode) as shortcuts for the field and classField functions

The dot operator can be used as an infix alternative to the field function whenever evaluation is being done for any parameter mechanism that will treat the evaluated result as a variable name. So [var &x.foo] is equivalent to [var [field &x foo]], which returns the object held in the field named foo in the object named by &x, but [val &x.foo] just returns the string &x.foo

The related function classField and the corresponding infix operator colon is used to return an alias to a field of the class object of a specified object, and can be used in the same circumstances. So, for example, (in those circumstances), if $lst names a list, then $lst:size returns an alias for the field named size in the list class object (i.e. the field that holds the list size function).

When the colon operator is used in the target of an instance function application, it has an important side effect -- the object which is determined by the left-hand operand is stored in the instance field of the environment created by invoking the function. For example, evaluating [$lst:length] invokes the length function with ^this holding the list named by $lst, which thereby returns the length of that list. Also, using List's instance functions get and put!, getting and putting the 3rd element of $lst can be written as [$lst:get 3] and [$lst:put! 3 $val].

The dot and colon operators are interpreted from left to right, so $a.b:c stands for [classField [field $a b] c].

Note that, interpreted as variable names:

Also note that both names and indices can be used in field accesses.

Application and concatenation are performed before field access operators, so if [$foo] evaluates to the string b, [$moo] evaluates to the string az, and $baz names a list, then $[$foo][$moo].3 names the 3rd element of that list, as does $[$zoo] if [$zoo] evaluates to baz.3.

2.6 Infix Mode

Both infix and prefix modes are supported by Templet. A number of built-in functions switch to evaluating their parameter(s) in infix mode, including infix, which has a single infix parameter whose value is returned, and do which has multiple parameters (separated by commas, since it is in infix mode), which are all evaluated, but null is returned.

In addition, code enclosed in parentheses is evaluated in infix mode, and is equivalent to passing the parenthesized code to the infix function. The following pairs of code fragments are the same:

  [gt? &x 20] and (&x > 20)
  [$lst 3] and ($lst(3))
  [&foo $x "Bill Clinton"] and (&foo($x,"Bill Clinton")
A switch to prefix mode is triggered by using square brackets within infix mode code, so prefix mode can be intermixed with infix mode expressions. Thus the following are all the same:
  [gt? [sqr $x] 20], ([sqr $x] > 20) and (sqr($x) > 20)
In general, operators treat all their operands as VAR parameters. There are two exceptions to this.

In infix mode, blanks or parentheses are used to delimit values, variables and operators; operators and variable or values must be separated by blanks to distinguish the operators from parts of variable names. (Also, & always refers to the bitwise-and operator; &1 must be used to refer to the first indexed local variable.) Otherwise, blanks, in general are not retained in infix mode, except within quoted strings, and within the actual parameter to a single QUOTE-parameter function.

Implicit concatenation can only be used in limited cases -- e.g. to concatenate strings that do not represent variables, and even then, blanks need to be represented as literals. In other cases, especially when variable names are dynamically constructed, mixed modes or explicit functions such as ref, or val are required. Though not required, use of the concatenation operator # is convenient in infix mode. For example, the following are equivalent.

  [val [$x][$y]], ( $x() $y() ),  and ( $x() # $y() )
  [val [$x] is [$y]], ( $x() " is " $y() ), ( $x() # " is " #  $y() )
  [sqr $[$nam]], ( sqr( $[$nam] )), and ( sqr( ref( "$" $nam )))
If parentheses simply enclose a string with no operators (including implicit concatentation), then the string is used as a parameter to the ref( $foo ) returns the object named by $foo, and evaluating ( sum ) returns the object named by sum -- i.e. the built-in sum function.

2.6.1 Comparisons

Classes whose objects are comparable will define a cmp instance function which will return -1, 0 or 1, depending upon whether the first parameter is less than, equal to, or greater than the second parameter. If a comparison operator is provided, it is the basis for the infix comparison operators <, <=, >, >=, ==, and !=, and both the built-in static functions cmp, lt?, le?, eq?, ne?, gt?, and ge?.

If no cmp function is provided, an eq? instance function, if provided, becomes the basis for the infix operators ==, and !=, and for the the built-in static functions eq?, and ne?.

The cmp and eq? function for strings first checks whether both arguments represent numbers (with blank, empty, or null arguments representing zero). If so, it performs numeric comparisons; otherwise it performs lexicographic comparison.

The built-in static functions numcmp, numlt?, numle?, numeq?, numne?, numgt?, and numge? always perform numeric comparisons, treating any object that does not represent a number as zero.

The built-in static functions strcmp, strlt?, strle?, streq?, strne?, strgt?, and strge?, and the corresponding infix operators #<, #<=, #>, #>=, #==, and #!= perform lexicographic comparisons (based on a locale-specific sort order which can be changed as needed). Objects are converted to strings if necessary.

The built-in static functions trimcmp, trimlt?, trimle?, trimeq?, trimne?, trimgt?, and trimge?, and the corresponding infix operators #<#, #<=#, #>#, #>=#, #==#, and #!=# perform lexicographic comparisons on the trimmed strings (i.e. with all blanks removed from the front and back)

The built-in function ident? and the corresponding infix operator === (and !==) test whether (or not) two objects are the same. If no eq? or cmp function is defined, then the operators ==, and != test for identity.

2.6.2 Infix Assignment

In infix mode, the = operator is used for assignment, so the following are equivalent:

  [set! $foo.x $val]  and [do $foo.x = $val]

More generally, the = operator returns a value which is that of the right hand operand, but which has a side effect of performing an assignment. That is, ( $a + ( $b = 9 ) ) results in the sum of $a and 9, and also results in assigning 9 to $b.

Top-level parameter assignments are treated in a special way in infix mode for all parameters that are evaluated.

For example ( diff( $a, $b )) returns the difference between $a and $b, while ( diff( $b = $a, $a = $b )) returns the same result, while at the same time swapping the contents of $a and $b.

To just obtain the assignment side effects, the infix mode do function can be used. That is, [do $b = $a, $a = $b] simply swaps $a and $b.

2.7 Advanced Parameter Topics

2.7.1 Parameter Groups

Functions can take multiple parameters, divided into groups (called clauses) some of which may be optional or repeated. For example,

  [match &str][:pat/ x.*y]
finds (without overlap) all substrings matching the given regular expression, and returns a list of them, while
  [match &str]
    [:replace +++ ---]
    [:by ===]
    [:subst]
    [:limit/ 20]
or
  [match &str]
    [:limit 20]
    [:replace +++ ---]
    [:by ===]
    [:subst/]
or
  [match &str]
    [:limit 20]
    [:replace +++ ---]
    [:by ===]
    [:subst]
  [/match]
starts with the string stored in &str, finds at most 20 instances of the substring "===", replaces them alternately with "+++" and "---", substituting them back into the original string, which is then returned.

Typically there are many different clauses, many optional or alternates, and they can appear in any order. Note the use of the terminating slash or of the function name preceded by a slash to terminate the application.

This can also be represented in infix mode. Note that clauses which do not have parameters are not followed by parentheses.

  ( match(&str)
    :by(===)
    :subst
    :replace(+++,---)
    :limit/(20) )

Some functions are defined so that parameter groups may repeat, for example, a multiple assignment version of the set! function:

  [set!][:! $a 9][:! $b 10][/set!]
which sets $a to 9 and $b to 10.

The first argument of each clause is an ALIAS parameter; the second is a VAR parameter. All parameters are computed and passed before set! begins to execute and make the assignments. Consequently, [set!][:! $a $b][:! $b $a][/set!] swaps $a and $b.

2.7.2 Embedded Parameters

Parameters can be embedded between clauses, such as in

  [if (&x > 20)]
    [$foo &x]
  [:elsf (&y > 20)]
    [$baz &y]
  [:elsf (&x + &y > 20)]
    [$glop &x &y]
  [:else]
    [$glop &y &x]
  [/if]
An arbitrary number of parameters can be embedded between clauses -- these are known as embedded parameters to distinguish them from the direct parameters provided within the clause. If there are multiple embedded parameters between two clauses, then blanks are used as separators, and the parameters are trimmed. If the function is defined to only take a single embedded parameter, then all blanks in it are (by default) retained. Embedded parameters typically use the VALDEF parameter mechanism, and are frequently used by functions (such as if) that control the flow of execution.

Embedded parameters also can be used in infix mode, for example

  [do
    if( &x > 20 )
      $foo( &x )
    :elsf( &y > 20 )
      $baz( &y )
    :elsf( &x + &y > 20 )
      $glop( &x, &y )
    :else
      $glop( &y &x )
    /if]
If multiple embedded parameters are used in infix mode, they must be separated by semicolons (as commas separate direct parameters). For example, ifprog is like if, but allows multiple embedded parameters; when the enabling test is true, the parameters are each evaluated in turn, with the result being the value returned by the last one.
  [do
    ifprog( &x > 20 )
      &x = &blotz( &x );
      $foo( &x, &y );
      &x + &y
    /ifprog]
In this case,
  [do
    if( &x > 20 )
      &x = &blotz( &x );
      $foo( &x, &y );
      &x + &y
    /if]
has the same effect. if expects a single embedded parameter. That parameter uses the operator ; (as opposed to the separator ;) which evaluates both its left and right hand operands, returning the value of the right one.

2.7.3 Conditional Parameter Inheritance

Some functions (e.g. macro and bound functions) can be defined so that they either accept parameters, or, if no parameters are provided, they inherit the parameters of either their defining or calling environment. Bound functions conditionally inherit parameters from their defining environment, so suppose some function is passed 678 as a parameter, and that function contains the following code:

  [repeat 10]
    [&blech $]
  [/repeat]
The code fragment [&blech $] corresponds to a VALDEF parameter to repeat, and so becomes an anonymous bound function, which the repeat function calls 10 times, but without any parameters. So, within the anonymous function, $ refers to the outer functions's parameter, 678, and so &blech is called 10 times, each time passing 678 to it. But suppose the function instead contains the following code:
  [iter 10]
    [&blech $]
  [/iter]
Unlike repeat, iter calls its function with a parameter that, in this case, iterates from 1 to 10, so the first call to &blech passes 1 as a parameter, the second call passes 2, etc.

Now, suppose we wanted to pass to &blech the sum of the iteration index and of the parameter to the outer function (e.g. 678). This can be written as

  [set! &val $]     [\ $ refers to the outer parameter]
  [iter 10]
    [&blech (&val + $)]        [\ $ refers to the iteration index]
  [/iter]
A more traditional form of iter is available which names its control variable, avoiding the need to pass a parameter to the embedded code. Thus, the following code fragment has essentially the same effect.
  [iter! &knt 10]
    [&blech ($ + &knt)]        [\ $ refers to the outer parameter]
  [/iter!]

2.7.4 Iterative Control Functions

The body of an iterative control function may be executed repeatedly. Each time it produces a result, which is used to construct the overall result of the function. Templet's built-in iterative control functions (e.g. iter, for, while, etc.) support a number of clauses that determine how the overall result of the function is constructed from the results of each iteration of the body. In the simplest case, the results of each iteration are concatenated together, so (using iteration over string lists)

  [foreach Sandi|Andee|Kathleen]
    <p>I loved [$].
  [/foreach]
generates
    <p>I loved Sandi.

    <p>I loved Andee.
    
    <p>I loved Kathleen.
while
  [foreach Sandi|Andee|Kathleen][:trim][:separate ", "]
    I loved [$]
  [/foreach]!
generates
  I loved Sandi, I loved Andee, I loved Kathleen!
and
  [iter 10][:trim][:list]
    [&plotz $]
  [/iter]
produces a list of 10 values; the results of the calls to &plotz with parameters 1 through 10.

The clauses that can be used with the built-in iterative control functions include:

In addition, iteration functions can either use the clauses [:trim] or [:notrim]. [:trim] indicates that within the body of the iteration, all whitespace surrounding applicators is to be discarded -- e.g. [$foo] and then [$baz] is treated as [$foo]and then[$baz]. Specfiying [:trim] or [:notrim] sets the ^trimming field of the environment. If not explicitly set, its value is always inherited from its defining environment. (Note that in a trimmed environment, functions like [space] and [newline] are the only way to produce whitespace adjacent to an application.)

2.7.5 Evaluation, Quasi-evaluation, and Apply

The eval function evaluates a string, which is usually constructed by concatenating quoted code fragments together with values ordinarily evaluated from other code fragments. It can be simpler to provide the code as a string with the "quasi-quote" symbol ' used to mark applicators that needs to be evaluated first.

The quasiEval function quotes its argument, then evaluates only quasi-quoted applicators. For example, if [&args] evaluates to the string 3 4, then evaluating [quasiEval [sum ['&args]]] generates the string [sum 3 4]]

The code [eval [quasiEval [sum ['&args]]]] generates and then evaluates [sum 3 4] with a final result of 7. This can be short-circuited using the quasi-eval symbol ! to [!sum ['&args]].

Quasi-evaluation is also useful in deciding whether to include an optional clause. For example:

  [!match &str]
    ['if [&foo]]
      [:limit &limit]
    [/if]
    [:replace "&"]
    [:by "$"]
    [:subst/]

In many cases, the apply function (this is the built-in static apply function, not the instance function used for defining application) can be used as an alternative to quasi-evaluation. There are three variants of the built-in static apply function:

A variety of library functions assist in incrementally constructing, inspecting, and modifying parameter contexts, including those for functions with clauses and embedded parameters.

2.8 Conductors

Templet provides a unified model for transfers of control that, in Java, are represented by return, break, continue, and throw. Since all forms of traditional statements in Templet are represented as functions, these various transfers of control all essentially dynamically unwind the stack looking for an appropriate function invocation (i.e. environment) to leave.

The leave function takes a Conductor as a parameter (or creates one from provided values); it walks up the stack, looking for an environment which matches the conductor. When such an environment is found, it is exited with a value provided either by the conductor or resulting from executing a special clause defined as part of the function of the matching environment.

It must be noted that Templet does not use Conductors to the same degree that Java, for example, uses exceptions. In Templet, many out-of-bound or exceptional conditions generally result in a null value. In particular, access to undefined fields, an application with null as a target both return null.

2.8.1 Conductor Matching

A conductor named fields are public. The fields below are defined when a conductor is created. Additional fields may be added in particular situations as needed.

[Conductor.new match value name reason] creates a new conductor using the specified fields in the following way:

[Conductor.new!] is similar to [Conductor.new] except that it places the conductor in the field of the current class named by name and freezes the field. The conductor instance functions like and like! have the same parameters and effect as new and new! except that any fields not specifically provided are taken from the instance.

The leave function can either

Each environment also has a match field (i.e. ^match). Leave matches its conductor against the match fields of the environments on the execution stack. A leaving conductor matches a value if

2.8.2 The label and try Functions

Functions ordinarily place themselves in their environment's match field, so that a conductor whose match field contains a function can be used to return from that function. Two special functions, label and try, construct the environment's match field differently.

When the label function is called, it places the newly created environment itself in the environment's match field. It takes (in addition to an embedded VALDEF parameter which specifies the code to be executed) a direct ALIAS parameter in which the environment is stored as well. A conductor whose match field contains the environment obtained through that alias can be used to break out of the labeled code -- e.g.

  [label &lbl]
    [while 1]
      [$foo &x]
      [if [$tst]]
        [leave &lbl &x]   &x becomes the overall result of the label function
      [/if]
      [$baz]
    [/while]
  [/label]

A :label clause can also be used with a function definition (when we get around to showing how functions are defined). [:label variable] specifies a variable (interpreted within the context of the invoked function). A reference to the invoked environment is placed in that variable as well as in the match field of the environment (instead of just a reference to the function). So leaving with (a conductor based on) that variable leaves that specific instance of the function -- which may be useful when functions are called recursively.

The try function can take (in addition to an embedded VALDEF parameter which specifies the code to be executed) one or more parameters, which determine the match field to be placed in its environment when invoked.

The functions below are useful in generating conductors which can be used both for leaving as well as for parameters to try: All of the functions above place a reference to the conductor itself in its value field.

Function definitions, and try and label functions can specify a catch clause with an embedded VALDEF parameter which is executed when and if a conductor is caught. Its result becomes the result of the function instead of the value of the conductor. The actual conductor caught can be accessed via the environment field ^conductor.

A finally clause with an embedded VALDEF parameter can also be specified in the same situations; it contains code that will be executed immediately before execution exits the associated function, whether normally or via a leave. It does not affect the result returned.

2.8.3 Leaving Iterative Control Functions

The built-in iterative control functions in Templet (e.g. iter, for, while, etc.) support a :label clause that can be used to both continue and break with a number of variations. In the simplest case, we find the following three code fragments are equivalent:

  [while 1]
    [label &inner]
      [$foo]
      [if [$tst]]
        [leave &inner val]
      [/if]
      [$baz]
    [/label]
  [/while]

  [while 1][:label &inner]
    [$foo]
    [if [$tst]]
      [leave &inner val]
    [/if]
    [$baz]
  [/while]

  [while 1][:label &inner]
    [$foo]
    [if [$tst]]
      [continue &inner val]
    [/if]
    [$baz]
  [/while]
There are a number of built-in functions -- like continue -- which construct specialized conductors and then leave with them. All of these are caught by the matching control function. The various specialized conductors have different names; each one has a different effect on the flow of execution and on the construction of the value ultimately returned by the iterative control function.

2.8.4 Enabling Leaves

The leave function does not cause a non-local transfer of control by default. If leaving is not enabled, then the result of calling leave (or of a function that had leaving enabled and was left) is a string representation of its conductor (which is null, if [[field [getThreadContext Conductor] detailed]] is false).

Why not enable leaving by default? Even though non-local transfers of control are useful, they conflict with a primary goal of Templet -- incremental text generation. The result of a top-level function call in Templet is generally the generation of text, possibly quite long, that can be stored in a file or sent out over the network. Unlike other languages, Templet need not make explicit calls to a write or append function to build up this text. Templet inherently concatenates the results of executing successive and iterative applications, and this concatentated result is itself the text to be generated.

Templet "knows", when a top-level function is called, where the resulting text is to be stored or sent. Templet does not wait until the top level function returns to store or send the generated text. Waiting would limit the size of text that could be generated. Instead, Templet writes out the text incrementally -- as it is being produced and concatenated -- even within nested function calls. That is if fn1's and fn'2 code are

  [def! fn1]
    Hello [fn2].  How are you?
  [/def!]
  
  [def! fn2]
    my dear friends [fn3] and [fn4]
  [/def!]
Then as fn1 executes, "Hello " can first be written, then as fn2 executes, "my dear friends" cn be written, then the results of fn3 (written out incrementally as fn3 executes), etc.

Non-local transfers interfere with this model, since leaving a function generates its own value, ignoring whatever concatenated text has been incrementally produced by the function thus far.

Consequently, an environment must be explicitly enabled to support conducting (i.e. non-local transfers) through the [:conduct] clause, which may be used with iterative control functions, and with function and class definitions (below). If an environment supports conducting, then it does not perform incremental writes of concatentated text. An environment supports conducting if either its calling or its defining environment supports conducting.

Conducting is automatically enabled (and incremental writes are disabled) in a number of circumstances:

2.9 Defining Functions

The function def defines a new function -- by default, a static function -- and returns it. For example, the code below defines a function that takes a single VAR parameter and doubles it, storing the function in the local variable &double.
 [set! &double
  [def][:parms VAR](2 * $)[/def] ]
Since the default parameter mechanism is VAR*, which is consistent with VAR, this can be written even more simply as
 [set! &double [def (2 * $)]]
The more commonly used def! defines the function (by default, an instance function), and freezes it under that name in the class object of the current environment. For example --
  [class util.Math]
    [def! double][:static][:trim][:parms VAR]
      (2 * $)
    [/def!]
  [/class]
The class function creates an environment like the current environment, but with the class context set to the (context) object in util.Math (and with the instance field set to null). Within that environment, it executes its embedded argument -- the def! function invocation. The def! invocation defines a static function named double, and freezes the function under that name in util.Math. def and def! are identical to Function.new and Function.new!, static functions in the Function class.

There are a number of common clauses used with function definitions that affect the result. They are:

Both def functions can take a :label clause which names a local variable in which a reference to the function will be stored when the function is invoked. This is useful in conjunction with leaving (i.e. returning from) the function. A :conduct clause can also be used which enables conduction (i.e. leaves) within invocations of the function.

Built-in functions are implemented in Java; user-defined functions can be implemented in Java as well by including an :execute clause which specifies the fully qualified name of the Java function which implements the Templet function. When called, the Java function is passed the calling context, the function object for the function being called, and a parameter object (or null if there are no parameters). These can be accessed and manipulated via public Java API's for the various classes of Templet objects.

2.9.1 Defining Parameters

A parameterless function is indicated by [:parms]. When there are a fixed number of parameters, the parameter mechanisms of each can be listed -- for example [:parms VAR VAL VAR].

Various suffixes can be used following the parameter to specify repeated or optional parameters. Specifically:

There can be at most one parameter suffixed with a * or +. If there is more than one suffixed parameters, then each optional parameter must be prefixed with a unique number of exclamation marks that indicates its priority. For example, consider

  [:parms !!!VAR? VAL+ !REF? DEF !!REF?]
A minimum of two parameters are expected, a VAL and a DEF parameter. If there are 3 parameters, the VAR has the highest priority, so we have a VAR, followed by a VAL followed by a DEF parameter. The last REF has the next highest priority, so 4 parameters use the mechanisms VAR VAL DEF REF. With 5 parameters, we get VAR VAL REF DEF REF, and with 6 or more parameters, we can start repeating the VAL parameter, so with 7 parameters, we expect VAR VAL VAL VAL REF DEF REF.

The use of DEF indicates a call-by-def parameter, but does not specify what parameters it expects. This can be made explicit by specifying the parameters within angle brackets, for example DEF<REF VAL> indicates a function that has two parameters -- a REF and a VAL parameter. The default is DEF<VAR*>.

A class can have two or more completely separate definitions of the same function so long as the number of possible parameters for each definition do not overlap. The function #$ can be used to determine the number of parameters that were passed.

2.9.2 Naming Parameters

Names can be associated with some or all of the parameters. For example, if parameters are specified as
  [:parms name:VAL VAR newval:VAR]
then the 1st parameter can be named as either $1 (or just $) or as $name, and the 3rd parameter can be named as either $3 or as $newval. The second parameter is unnamed and can be referred to only as $2. Each named field in the parameter context holds an alias to the corresponding indexed field.

Since VAR is the default direct parameter mechanism, the name alone can be used for a VAR parameter that has no suffixes (so long as the name is not the same, including case, as one of the parameter mechanisms). The example above could have been written as

  [:parms name:VAL VAR newval]

Names are particularly useful when fields are optional or repeated. For example, in

  [:parms  foo:!!!VAR?  baz:VAL+  ref:!REF?  fn:DEF  newref:!!REF?]
the variable $fn will always refer to the DEF parameter, regardless of how many optional and repeated parameters precede it. To find out the number of actual repeated or optional parameters, the function numparms can be used -- [numparms baz] will indicate how many baz parameters were passed. The code [parms baz] will generate a new object with just the parameters corresponding to baz.

The variable $baz can be used to refer to the first one of the parameters associated with baz; the # operator can be used to refer to additional parameters. For example, $baz#3 refers to the 3rd of the parameters. The # operator can be used in conjunction with the dot operator applied to any object that has bound fields (keeping in mind that $name is a shortcut for ^parms.name). So obj.name#index is short for [boundField obj name index].

Another useful function is boundIndex, which also can be used with bound fields. [boundIndex obj name] returns the index in obj to which name is bound, so that obj.name#index can also be written as obj.( boundIndex(obj,name) + index - 1 )

Names can also be associated with the parameters provided to a DEF parameter. For example, given the code

  [set! &foo [def][:parms DEF][$ 123 4][/def]]
Then [&foo ($x + $y)] will result in 127.

2.9.3 Parameter Groups

A parameter group is defined by the clause :clause. For example, suppose we wanted to define a static void function setapply! (in the current class) with clause places and vals, that could, for example, be invoked as

  [setapply! sum][:places &john &mary][:vals/ 40 50 60]
and that would add up the given values, and store the sum in each of the named fields. We could define
 [def! setapply!][:static][:void][:parms REF]
  [:clause places][:parms placeparms:ALIAS+]
  [:clause vals][:parms valparms:VAR+]
    [set! & [apply $ [parms valparms]]]
    [iter! &i [numparms placeparms]]
      [set! $placeparms#[&i] &]
    [/iter!]
  [/def!]
It can also be defined without naming parameters as
 [def! setapply!][:static][:void][:parms REF]
  [:clause places][:parms ALIAS+]
  [:clause vals][:parms VAR+]
    [set! & [apply $ [parms @vals]]]
    [iter! &i [numparms @places]]
      [set! $@places#[&i] &]
    [/iter!]
  [/def!]
That is, [parms @vals] generates an object with all the parameters provided to the vals clause, and $@vals#3 names the 3rd such parameter.

Note that [parms @] generates an object with all the parameters provided to the main applicator, and $@#3 names the 3rd such parameter, as, of course, does $3.

2.9.4 Embedded Parameters

Embedded parameters which are expected before or after a particular defined clause are indicated by using :before or :after following a definition or clause. For example, a simple form of the iter function could be defined (using while) as

  [def! iter][:static][:trim][:parms VAR][:after fn]
    [set! &knt 1]
    [while (&knt <= $)]
      [$fn &knt]
      [incr! &knt]
    [/while]
  [/def!]
The local variable &knt is used to count from 1 up to the provided limit. On each iteration of the loop, &knt is provided as a parameter to the embedded code, named by $fn (with the default embedded parameter mechanism -- VALDEF<VAR*>).

Because :after is specified, but there are no clauses defined, iter must be terminated by using /iter.

When :before is used with the main definition, it means that the corresponding embedded parameter appears immediately before the terminating clause. In fact, since the above definition of iter only has a single embedded parameter, it could have been written using [:before fn] instead of [:after fn].

If a clause has an :after embedded parameter, the parameter need not appear immediately after its defining clause. If the following clause is defined with [:skipAfter], then the embedded parameter will be expected after it instead (unless the following clause is also defined with [:skipAfter]). :skipBefore works the same way in conjunction with :before. As an example, def! itself is defined with [:skipAfter]. Part of its definition is

  [def! def!][:void][:parms fnam fhold][:after code]
    [:clause void][:parms][:skipAfter]
    [:clause trim][:parms][:skipAfter]
    ...
    [:clause parms][:parms parmDescription][:skipAfter]
    ...
    [:clause catch][:after catchfn]
    [:clause finally][:after finalfn]
  [/def!]
That is, the code that defines the function does not appear immediately after the main def! applicator, but skips after the auxilliary clauses like :void and :parms, but before :catch and/or :finally.

The parameters associated with embedded parameters do not need to be explicitly named. If embedded parameters are specified after some clause foo, then $+foo names the first such parameter (and $+foo#3 names the 3rd such parameter). Similarly, $-foo names the first embedded parameter before foo, and $+ names the initial embedded parameter, while $- names the final embedded parameter. So, the code for iter can also be written as follows (keeping in mind that [:after] implies the default embedded parameter mechanism).

  [def! iter][:static][:trim][:parms VAR][:after]
    [set! &knt 1]
    [while (&knt <= $)]
      [$+ &knt]
      [incr! &knt]
    [/while]
  [/def!]
Another simple example is the definition of the library function with, which has both a direct and an embedded parameter, and executes the embedded parameter passing it the direct parameter:
  [def! with][:trim][:parms VAR][:after]
    [$+ $]
  [/def!]
For example,
  [with wobble]
    I [$], I [$]d, I will [$].
  [/with]
results in: I wobble, I wobbled, I will wobble.

2.9.5 Alternate Embedding

It is possible to define a function so that one of its parameters can be passed as either a direct parameter or an embedded parameter. For example, the built-in quote function is defined with [:parms QUOTE@] meaning that its single parameter can be provided as either a direct or embedded parameter, so a quote can be written as either of the following
  [quote this is a long quote]

  [quote]this is a long quote[/quote]
The built-in single-assignment set! function has parameters defined as [:parms ALIAS VAR@VAL] meaning that its second parameter can be provided as either a direct VAR parameter or as an embedded VAL parameter (but named in either case as $), so that one could write either of
  [set! &x (some complicated expression)]

  [set! &x]
    (some complicated expression)
  [/set!]
The comment and void functions are similar. The comment function quotes and then ignores its argument, and is defined as [def! \][:parms QUOTE@][/def!]. The void function evaluates its function argument, and throws away the result, and is defined as [def! void][:void][:parms VAR@VAL][$][/def!]

2.9.6 Repeated and Ordered Parameter Groups

In the examples above, parameter groups were optional and could appear in any order. If order matters or if parameter groups can be repeated, then the :ordered clause must be specified as part of the definition. In ordered function definitions, there are additional name bindings:

As an example, given a primitive cond function which takes a DEF parameter indicating a test, and an embedded VALDEF parameter specifying a function to be evaluated if the test succeeds, a simple if function can be coded as

  [def! if][:static][:trim][:ordered]
    [:parms DEF<>][:after VALDEF<>]
    [:label &fn]
  [:clause elsf][:parms DEF<>][:after VALDEF<>]
  [:clause elsfnot][:parms DEF<>][:after VALDEF<>]
  [:clause else][:parms][:after VALDEF<>]
    [cond [$] [leave &fn [$+]]   
    [iter! &i 2 [numclauses]]
      [cond
          [select [tag &i]]
            [:case elsf] [$@[&i]]
            [:case elsfnot] [not [$@[&i]]]
            [:case else] 1
          [/select]]
        [leave &fn [$@+[&i]]]
      [/cond]
    [/iter!]
  [/def!]

The multiple-assignment set! function can be defined in terms of the single-assignment version (the fact that there are two definitions is ok -- the single assignment versions takes either 1 or 2 parameters; this version takes no parameters).

  [def! set!][:static][:void][:ordered][:parms]
  [:clause !][:parms left:ALIAS right]
    [iter! &i [numclauses]]
      [set! $@[&i]$left $@[&i]$right]
    [/iter]
  [/def!]
Function definitions that ue :ordered can contain :clause clauses without a direct parameter. If there is more than one such clause, their associated :parms clauses must not conflict. This allows invocations of the function to have arbitrarily named clauses.

2.9.7 Specifying the Invocation Environment

When a function is defined, it retains its defining environemnt -- the environment in which it was specified, or an alternate one explicitly specified, using [:env environment].

When the function is invoked, the defining environment and the calling environment together produce the invocation environment, by setting ^class, ^this, ^parms and ^locals. There are five environment control clauses that can be used to control the overall setting of the of the invocation environment:

In addition

When a function is defined by virtue of being passed as a DEF or VALDEF parameter it defaults to

  [:bound][:condparms VAR*]
However, if it is being defined as the code of a def function, then it defaults to [:static], and if it is being defined as the code of a def! function, then it defaults to [:instance].

The redef function redefines a function, using any of the environmental control clauses above. In addition, [:env] can be used (without any parameter) to change the environment of the function to the environment from which redef is called; if :env is not used (with or without parameters) the returned function has the same environment as the original function.

For example, consider the block function. It has a single embedded parameter, and executes it in a environment just like the current environment except that it has a fresh local context. It can be defined in either of the two ways below (the second of which is preferred):

  [def! block][:macro][:trim][:locals][:parms][:after]
    [[redef $+][:env/]]
  [/def!]

  [def! block][:trim][:parms][:after]
    [[redef $+][:locals/]]
  [/def!]

2.9.8 Automatic Creation of Related Functions

It is common in Templet to have 2 or 3 related versions of a function. For example: Assuming a substring function, we can define the 3 chop functions via a single function definition as
  [def! chop][:trim][:parms str len]
    [substring $str 1 (len + 1)]
  [:make! str]
  [:make@ str]
    [substring $str (len + 1)]
  [/def!]
[:make! str] indicates that

[:make!] (without any parameters) indicates that the new function should have an additional ALIAS parameter (to store the result) as its first parameter.

[:make@ str] (or [make@]) can be applied to a function with or without a terminating !. It

2.10 Defining Classes

2.10.1 Defining New Classes

The functions newclass and newclass! (also available as the static Class functions Class.new and Class.new! create a new class, and execute their embedded parameter within an environment in which that class is the class context. For example, a Stack class can be defined as
  [newclass pkgs.util Stack]
  [:noNames][:privateNdxs][:trim][:conduct]
  
    [def! size][:parms]
      [size %]
    [/def!]
  
    [def! clear!][:void][:parms]
      [setSize! % 0]
    [/def!]
  
    [def! top][:parms]
      [% -1]
    [/def!]
  
    [def! pop!][:void][:parms]
      [removeBack! %]
    [:make@]
      [% -1]
    [/def!]
  
    [def! push!][:void][:parms VAR+]
      [foreach ^parms]
        [addBack! % $]
      [/foreach]
    [/def!]
  
    [def! new][:new][:parms VAR*]
      [if [#$]]
        [apply % push! ^parms]
      [/if]
    [:make!]
    [/def!]
  
  [/newclass]
Notes:

If a class does not provide an apply instance function then a default definition is used:

  [def! apply][:trim][:parms VAR*]
    [if [#$]]
      [apply % get ^parms]
    [:else]
      (%)
    [/if]
  [/def!]

Fields of class objects may be marked as private, indicating they can only be accessed within functions associated with the class. Fields holding functions can be marked private if the function has a [:private] clause. Other fields can be marked private by listing them in the markPrivate clause of newclass.

2.10.2 Characterizing Named and Indexed Fields

When a class is created, clauses are used to specify the disposition of the named and indexed fields of instances of the class.

For virtual and mediated fields,

A number of additional clauses may be used to optimize the implementation:

2.10.3 Extension

Templet facilitates extension of a class from other classes. The :extends clause, which takes one or more classes, can be used in conjunction with creating a new class. Every public instance function defined in the named classes is redefined in the environment of the new class. All the named classes must be field-frozen, otherwise newclass leaves with the Class.NotFrozen conductor.

Any redefined function can be overridden by explicitly redefining it in the body of the class definition. Any inherited functions which are not overridden are frozen in the new class except for those listed in a :noFreeze clause. [:noFreeze] indicates that none of the fields of the class should be automatically frozen.

If :extends causes redefined functions with the same name from different classes to have inconsistent parameters, or if a redefined function is inconsistent with a function that overrides it, then newclass! leaves with the Class.InconsistentDerivation conductor.

If functions from two or more extended classes have the same name and identical parameters (and are are not overridden), the one from the first class listed is redefined in the new class. If the parameters are consistent, but a function from a class listed later covers a wider range of parameters, then that function is also redefined, but only covering the number of parameters not covered by functions in earlier listed classes.

When :extends is used, the disposition of instance fields (e.g. :privateNdxs) is derived from the first class listed, but can be overridden by explicitly providing the disposition clauses.

[supports? object class] is true if the object is an instance of the specified class, or a class (possibly transitively) extended from it. It is a shortcut for [[[getclass object]:supportClasses]:contains? class].

Similarly, [supports? object function] is true if the specified function has parameters consistent with an instance function of the specified class. It is a shortcut for [[get [getclass object] [function:getName]]:consistent? function].

Extension is useful for "framework" classes in which some of the functions are expected to be replaced to vary the behavior of the class. For example, the SortedDictionary class is like an ordinary dictionary, except that it also (appears to) have bound indexed fields which order the dictionary entries -- i.e. getting the i'th indexed field gets the entry that is i'th in the ordering.

The SortedDictionary class uses a cmpElem function which takes the names of two entries, and indicates the relationship between the entries. The built-in cmpElems function (passed the names of two entries) sorts entries based on their values. Derivation can be use to sort in some other way -- e.g. based on the names of the entries -- by overriding cmpElems:

  [newclass! pkg.util EntrySortedDictionary]
  [:extends pkg.util.SortedDictionary][:trim][:conduct]
  
    [def! cmpElems][:parms VAR VAR]
      [cmp $1 $2]
    [/def!]
    
  [/newclass!]

2.10.4 Derivation

It is also possible to define a new class, but derive functions from one or more other classes using the derive function. Derived functions are not automatically frozen; a :freeze clause can be use to specify which functions to freeze, or all of them with :noFreeze additionally used to specify which ones should not be frozen. When using derive, every function (including private and non-instance functions) is redefined for the new class, however an :include or :exclude clause can be used to explicitly include or exclude specific functions.

Thus, although the benefits of the "supports" relationship are lost, EntrySortedDictionary can also be defined as:

  [newclass! pkg.util EntrySortedDictionary]
  [:noNdxs][:mediateNames][:trim][:conduct]

    [derive pkg.util.SortedDictionary][:exclude cmpElems][:freeze/]
    
    [def! cmpElems][:parms VAR VAR]
      [com $1 $2]
    [/def!]
    
  [/newclass!]
It is common when using :extends to use derive to import the static and private functions from the same class. Also, when extending from multiple classes, derive can be used to overide the default derived function with one from a class listed later.

2.10.5 Delegation

Delegation is typically used to define a new class that adds some orthognal functionality to an existing class or combines the orthogonal functionalities of two or more existing classes. For example, suppose a user had defined a Status class which maintained some complex notion of status along with functions to set and query the status. The user had also defined a Stack class, and now wanted to define a StackWithStatus class that provides the functions of both.

Using delegation, this class would be implemented so that each instance would hold a Stack instance and a Status instance. Stack-related function would be redirected to the Stack instance, and Status-related function would be redirected to the Status instance. This could be implemented as

  [newclass! &myclasses StackWithStatus]
    [:noNdxs][:privateNames][:freezeNames stk stat]
    [:extends pkgs.util.Stack &myclasses.Status]
  
    [delegate/ pkgs.util.Stack stk]
    [delegate/ &myclasses.Status stat]
    
    [def! new][:new][:parms]
      [pkgs.util.Stack.new! %stk]
      [&myclasses.Status.new! %stat]
    [make!]
    [/def!]
    
  [/newclass!]
The delegate function takes a class and the name of a field. For each public instance function of the class, it defines a public instance function in the current environment which does nothing but call the corresponding function with the same arguments accessed through the named field. Additional clauses can be used to exclude certain functions, or prevent them from being frozen. For example, the code above will define a push! function equivalent to:
  [def! push!][:trim][:parms VAR+]
    [apply %stk push ^parms]
  [/def!]

2.11 Enumerations, Streams, Promises and LazyLists

Enumerations, Streams, Promises and LazyLists all deliver values, but can delay their access or computation until needed.

2.11.1 Enumerations

An Enumeration can provide a sequence of values. It has two instance functions -- hasNext? which indicates whether more values are available in the sequence, and next, which provides the next value. For example, since lists have an elements function which returns an enumeraion of its elements, the following code calls foo on each element of the list lst.
  [set! &elems [&lst:elements]]
  [while [&elems:hasNext?]]
    [&foo [&elems:getNext]]
  [/while]
In fact, that is equivalent to use of the foreach function below, which works with any object that provides an elements function to generate such an enumeration:
  [foreach &lazylst]
    [&foo $]
  [/foreach]
In Java, Enumerations are an interface, and each different kind of enumeration is a different class. Because functions are objects in Templet, and can be stored as instance fields (i.e. fields in the instance context), for most uses, Templet only needs a single Enumeration class.

Enumeration's new function has a number of clauses with embedded parameters -- one to define initialization, and one each for the various functions defined for enumerations. All are optional -- if they are missing, either their functionality is synthesized from other provided clauses, or else the functionality is not supported:

These functions provided with the clauses are redefined by Enumeration as dynamic -- so that when called by the Enumeration code, % refers to the enumeration itself. This allows the provided functions to use the instance fields of the enumeration for its own implementation needs. Part of Enumeration's definition is shown below:

  [newclass! pkgs.classes Enumeration]
  [:privateNames][:privateNdxs][:trim][:conduct]

    [def! index][:parms]
      [%_index]
    [/def!]

    [\ ---------------------------------------]

    [def! hasNext?][:parms]
      [%_hasNext?]
    [/def!]

    [\ ---------------------------------------]

    [def! move][:void][:parms]
      [%_move]
    [/def!]

    [\ ---------------------------------------]

    [def! getAlias][:parms]
      [%_getAlias]
    [/def!]

    [\ ---------------------------------------]
  
    [def! getBase][:parms]
      [%_getBase]
    [/def!]

    [\ ---------------------------------------]
  
    [def! get][:parms]
      [%_get]
    [/def!]

    [\ ---------------------------------------]
  
    [def! getNext][:parms]
      [%_getNext]
    [/def!]

    [\ ---------------------------------------]
  
    ...


    [\ ---------------------------------------]
  
    [def! set!][:void][:parms VAR]
      [%_set! $]
    [/def!]

    [\ ---------------------------------------]
  
    ...

    [\ ---------------------------------------]
  
    [def! new][:new][:parms VAR*][:after]
      [:clause index][:parms][:after]
      [:clause hasNext?][:parms][:after]
      [:clause move][:parms][:after]
      [:clause getAlias][:parms][:after]
      [:clause getBase][:parms][:after]
      [:clause get][:parms][:after]
      [:clause getNext][:parms][:after]
      [:clause getPrev][:parms][:after]
      [:clause peekNext][:parms][:after]
      [:clause peekPrev][:parms][:after]
      [:clause moveToBeginning][:parms][:after]
      [:clause moveToEnd][:parms][:after]
      [:clause setBase!][:parms][:after]
      [:clause setEntity!][:parms][:after]
      [:clause set!][:parms][:after]
      [:clause setBase@][:parms][:after]
      [:clause setEntity@][:parms][:after]
      [:clause set@][:parms][:after]
      [:clause remove!][:parms][:after]
      [:clause remove@][:parms][:after]
      [:clause insert!][:parms][:after]
      [:clause insertList!][:parms][:after]
      [:clause add!][:parms][:after]
      [:clause addList!][:parms][:after]
      [:clause reverse][:parms][:after]
       
      [set! %_index [redef $+index][:dynamic/]]
      [set! %_hasNext? [redef $+hasNext?][:dynamic/]]
      
      [set! %_move
        [def][:instance][:parms]
          [if [#$]]
            [%_move $]
          [:else]
            [%_move 1]
          [/if]
        [/def]
      [/set!]
      
      [set! %_getAlias [redef $+getAlias][:dynamic/]]
     
      [set! %_getBase]
        [if ($+getBase)]
          [redef $+getBase][:dynamic/]
        [:else]
          [def][:instance][:parms]
            [getDirect [%:getAlias]]
          [/def]
        [/if]
      [/set!]
        
      [set! %_get]
        [if ($+get)]
          [redef $+get][:dynamic/]
        [:elsf ($+getAlias)]
          [def][:instance][:parms]
            [val [%:getAlias]]
          [/def]
        [:else]
          [def][:instance][:parms]
            [val [%:getBase]]
          [/def]
       [/if]
      [/set!]
         
      [set! %_getNext]
        [if ($+getNext)]
          [redef $+getNext][:dynamic/]
        [:else]
          [def][:instance][:parms]
            [%:move]
            [%:get]
          [/def]
        [/if]
      [/set!]
      
      ...

      [set! %_set!]
        [if ($+set!)]
          [redef $+set!][:dynamic/]
        [:else]
          [def][:instance][:parms VAR]
            [set! [%:getAlias] $]
          [/def]
        [/if]
      [/set!]
    
      ...
    
      [apply [redef $+][:dynamic/] ^parms]  [/ initialization]
    [make!]
    [/def!]
  
  [/newclass!]
So, for example, List could define its elements function so that it is simple, reasonably efficient, and complete, except for reversability, as
  [def! elements][:trim][:parms]
    [Enumeration.new %]  [\ % refers to the List instance]
      [zero! %i]
      [set! %lst $]      [\ $ refers to the List instance]
    [:index]
      (%i)
    [:hasNext?]
      (%i < %lst:size())  
    [:move]
      [sum! %i $]
    [:moveToBegining]
      [zero! %i]
    [:moveToEnd]
      [set! %i [%lst:size]]
    [:getAlias]
      [field %lst %i]
    [/new]
  [/def!]

2.11.2 Streams

A Stream extends an Enumeration, iterating through characters, and may be backed by either a string, a file, or a network connection. A stream backed by a string supports all enumeration functions with additional functions for scanning.

A stream backed by a file supports different sets of operations depending upon whether the file is open for input, output, or input and output. A stream backed by a file open for --

Each thread has a standard input and output stream associated with it, accessed by [stdin] (equivalent to [get [getThreadContext Stream] stdin]) and [stdout]. The standard streams are initialized when a thread is created, but it is possible to change them to other streams.

The codestream (or codestream!) function has a DEF (or an embedded VALDEF) parameter, and produces a stream based on the incremental output of the function passed to it (executed in a separate thread).

Streams can be connected together dynamically. In addition, a variety of functions are available to connect computations via streams. In particular:

2.11.3 Promises

A promise delays evaluation of a function until needed, but subsequent evaluation of the promise returns the same result, and does not recompute it. If &p holds the promise defined by [Promise.new][foo $][/new], then the first evaluation of [&p] will evaluate [foo $] (in the environment in which it was defined); subsequent evaluations of [&p] return the same value returned previously without recomputing it. Templet's VALDEF parameter mechanism, combined with the use of class-defined application make Promise an easy library class to implement as well.

  [newclass! pkgs.classes Promise]
  [:noNdxs][:freezeNames][:trim][:conduct]

    [def! apply][:parms]
      [if (%fn)]
        [set! %val [%fn]]
        [null! %fn]
      [/if]
      (%val)
    [/def!]
  
    [def! new][:new][:parms][:after VALDEF<>]
      [set! %fn $+]
    [:make!]
    [/def!]
  
  [/newclass]

2.11.4 LazyLists

A LazyList combines the functionality of Lists and Promises. It is created with a function that computes a needed element on demand given the lazylist ($lst) and the index of the requested element ($ndx), leaving with the conductor LazyList.NotAvailable if that element cannot be provided.

LazyList also remembers the value computed under that index, so it doesn't need to recompute it when subsequently asked for the value at that index. This provides a simple "memo function" capability. For example, to generate a memoized version of a function &f, just define &fmemo as

  [LazyList.new! &fmemo]
    [&f $ndx]
  [/new!]
That is [&fmemo 12] will return the same result as [&f 12], except that the &fmemo version internally remembers the result for later use. Heres a version that computes &f only for indices from 1 to 100:
  [LazyList.new! &fmemo100]
    [if ($ndx < 100)]
      [&f $ndx]
    [:else]
      [leave LazyList.NotAvailable]
    [/if]
  [/new!]
Memoization via LazyList is especially effective for recursive functions, such as fib
  [LazyList.new! &fib]
    [if ($ndx < 2)]
      1
    [:else]
      ( $lst( $ndx - 1 ) + $lst( $ndx - 2 ) )
    [/if]
  [/new!]

Iterations over an ordinary List typically compare a control variable to the size of the list. This does not work for a LazyList, since the size is not known a-priori. Instead of checking the size of a LazyList, the avail? instance function is used to determine whether a particular element can be made available. (This instance function is also defined for lists and string lists, with availability determined based on the size of the list).

If a LazyList is known to be able to make all fields available up to some (unknown) index, but none thereafter, then the code below, which works for both lazylists as well as lists and string lists, can be used to call foo for each available element of &lst.

  [for [one! &i] [&lst:avail? &i] [incr! &i]]
    [&foo [&lst &i]]
  [/for]
or its infix equivalent
  [dofor &i = 1, &lst:avail?(&i), &i ++ ]
    &foo(&lst(&i))
  [/dofor]
LazyList is defined so that it uses only a single named instance field which holds the function passed to the new! function. A (slightly simplied) definition of LazyList is:
  [newclass! pkgs.classes LazyList]
  [:freezeNames fn][:mediateNdxs][:trim][:conduct]

    [Conductor.matchClassName! NotAvailable]
    [freezePrivate! ^class.Undefined [Object.new]]

    [\ ---------------------------------------]

    [def! getElem][:macro][:private]
      [growSize! % $ ^class.Undefined]
      [if (%[$] === ^class.Undefined)]
        [set! %[$]]
          [try ^class.NotAvailable]
            [%fn % $]
          [/try]
        [/set!]
      [/if]
      [if (%[$] === ^class.NotAvailable)]
        [leave ^class.NotAvailable]
      [:else]
        (%[$])
      [/if]
    [/def!]

    [\ ---------------------------------------]

    [def! get][:parms VAR]
      [^class.getElem]
    [/def!]

    [\ ---------------------------------------]

    [def! put!][:parms ndx val]
      [put! % $ndx $val]
    [/def!]

    [\ ---------------------------------------]
  
    [def! avail?][:void][:parms VAR]
      [try ^class.NotAvailable]
        [^class.getElem]
        [leave ^class.avail? [true]]
      [/try]
      [leave ^class.avail? [false]]
    [/def!]
  
    [\ ---------------------------------------]
  
    [def! elements][:parms]
      [Enumeration.new %]  [\ % refers to the LazyList instance]
        [zero! %i]         [\ % refers to the Enumeration instance]
        [set! %lst $]      [\ $ refers to the LazyList instance]
      [:index]
        (%i)
      [:hasNext?]
        [%lst:avail? (%i + 1)]
      [:move]
        [sum! %i $]
      [:moveToBegining]        [\ note: moveToEnd not provided]
        [zero! %i]
      [:getAlias]
        [field %lst %i]
      [/new]
    [/def!]

    [\ ---------------------------------------]
  
    [def! new][:new][:parms][:after VALDEF<lst:VAR ndx:VAR>]
      [set! %fn $+]
    [:make!]
    [/def!]

  [/newclass!]
Notes:

2.12 Compilation

2.12.1 Parsing & Incremental Compilation

When a file containing Templet code is loaded, the code is first parsed into an internal form, and then partially compiled. However, an application cannot be fully compiled unless the target is known (and frozen to the extent necessary) since that determines how its parameters are to be compiled.

To make compilation easier, (1) a function, once defined, cannot be changed, and (2) the most common function for defining a new function, def! takes the location where the new function is to be placed, and freezes that field (see below).

Still, a function (e.g. one of a mutually recursive group of functions) may not be even be defined when an application of it is first parsed. However, the targets of most applications in a function are often frozen by the time the function is first executed. So, if a function is not fully compiled when first executed, Templet will try to complete the compilation at that point.

Nonetheless, there may be applications whose targets are never frozen, and which may or may not change. For each such application, Templet essentially does the following:

2.12.2 Casts

If a function has multiple clauses, the function and all its clauses must be available when the function is first parsed, since it is otherwise impossible to match the clauses and ensure unambiguous parses. Since this is not always possible, casts can be used.

If the name of a multiple parameter group function is constructed dynamically, or if the function is computed, then a known function with the same signature must be specified, using the cast operator '.

For example, if [$foo] computes a function with the same signature as match, then an example invocation would be

  [[$foo]'match &str][:pat/ x.*y]

Multiple parameter group instance functions must always use the cast operator to specify the class of the instance. For example, if some class Zlot had an instance function glom with a terminating clause /glom, then if $zlot held a Zlot, glom would be invoked on it by

  [$zlot'Zlot:glom] ... [/glom]

If the function (or its name) were dynamically computed as well, then the name would have to be cast as well:

  [$zlot'Zlot:[$foo]'glom] ... [/glom]
Casting at the invocation site could be avoided by allowing fields to declare the class of objects they can hold. This would trade off dispatch code for run-time checks on assignments, though the latter could be ameliorated by type-inferencing. In any case, such declarations are not presently suppored.

2.12.3 Freezing

Templet supports a number of different levels of freezing through built-in function, or through clauses associated with a function or class. Each of these allows optimization during compilation. These include:

Deeper freezes allow the Templet compiler to perform better optimization. The Templet compiler marks a function if it determines that applications of it can be optimized by pre-evaluating it if all of its parameters are frozen, and this is used when compiling an invocation of it.

2.12.4 Compile-time Transformation

User-defined functions that have only QUOTE parameters can interpret those parameters any way they want. Effectively, such functions have complete control over the syntax they interpret. They can delay this interpretation until they are invoked. However, Templet offers another alternative.

A function with only QUOTE parameters can specify that it is a transformer by using the :transform clause. In that case, when the compiler encounters an application of the function, the function's code is immediately invoked with its actual parameters. The result of the invocation must itself be a string, which is substituted for the original application, and is compiled in its place.

The infix-mode functions (e.g. infix, do, dofor, etc.) operate in this way, converting infix-mode code into equivalent prefix-mode code.

2.13 User-Defined Aliases and Mediators

2.13.1 Aliases

Any object class can act like an alias as long as it defines the following functions:

The function makeAlias can be used to turn such an object into an alias. That is, if $obj holds such an object, then [makeAlias $obj] returns the corresponding alias. Operations on the alias are passed on to the underlying object.

The standard alias class is defined as

  [newclass! pkgs.alias StdAlias][:noNdxs]
  [:freezeNames obj id][:trim][:conduct]
  
    [def! getDirect][:parms]
      [getBase %obj %id]
    [/def!]

    [\ ---------------------------------------]
  
    [def! getDirectAlias][:parms]
      [if [alias? %obj.%id]] [refbase %obj.%id] [/if]
    [/def!]

    [\ ---------------------------------------]
  
    [def! setDirect!][:void][:parms VARBASE]
      [putBase! %obj %id $]
    [/def!]

    [\ ---------------------------------------]
  
    [def! setDirect@][:parms VARBASE]
      [putBase@ %obj %id $]
    [/def!]
  
    [\-------------------------------]
  
    [def! new][:make!][:new][:parms VAR VAR]
      [putFreeze! %obj $1]
      [putFreeze! %id $2]
    [/def!]
  
  [/newclass!]
This is a slightly different version of StdAlias which always resolves aliases immediately to the eventual object.
  [newclass! pkgs.alias BarrierAlias][:noNdxs]
  [:freezeNames obj id][:trim][:conduct]
  
    [def! getDirect][:parms]
      [get %obj %id]
    [/def!]

    [\ ---------------------------------------]
  
    [def! getDirectAlias][:void][:parms]
    [/def!]

    [\ ---------------------------------------]
  
    [def! setDirect!][:void][:parms VARBASE]
      [put! %obj %id $]
    [/def!]

    [\ ---------------------------------------]
  
    [def! setDirect@][:parms VARBASE]
      [put@ %obj %id $]
    [/def!]
  
    [\-------------------------------]
  
    [def! update!][:void][:parms VAR VAR]
      [set! %obj $1]
      [set! %id $2]
    [/def!]
  
    [\-------------------------------]
  
    [def! new][:make!][:new][:parms VAR VAR]
      [set! %obj $1]
      [set! %id $2]
    [/def!]
  
  [/newclass!]
Finally, the following code describes a direct alias -- one that holds onto an object (or other alias) directly, rather than pointing to a field where it is held.
  [newclass! pkgs.alias DirectAlias][:noNdxs]
  [:freezeNames hold][:trim][:conduct]
    
    [def! getDirect][:parms]
      [refbase %hold]
    [/def!]

    [\ ---------------------------------------]
  
    [def! getDirectAlias][:parms]
      [if [alias? %hold]] [refbase %hold] [/if]
    [/def!]

    [\ ---------------------------------------]
  
    [def! setDirect!][:void][:parms VARBASE]
      [setBase! %hold $]
    [/def!]

    [\ ---------------------------------------]
  
    [def! setDirect@][:parms VARBASE]
      [setBase@ %hold $]
    [/def!]
  
    [\-------------------------------]
  
    [def! new][:make!][:new][:parms VARBASE]
      [setBase! %hold $]
    [/def!]
  
  [/newclass!]

2.13.2 Mediators

Aliases are simply a special case of mediators, special object-like entities that mediate or monitor access to object or to individual fields. For example,

Templet's infix-mode support uses mediators to implement maintenance of one-way constraints. The infix-mode function maintain expects an infix assignment expression as a parameter. It places update hook mediators or all variables mentioned on the right-hand side of the assignment (or on the objects they hold, if not strings). The mediator then arranges for the assignment to be re-executed in order to maintain the constraint when an update hook mediator is triggered.

2.13.3 Field Mediators

Any object class can act like a field mediator and may optionally define the functions below. An alias is a field mediator, and can define these as well.

In general, these functions can do one of three things:

Within Templet, all operations that obtain the entity stored in a field ultimately execute either getBase or get. When this happens on a mediated field, Templet does the following:

Setting a new entity via a field works somewhat similarly. Within Templet, all operations that store an entity in a field ultimately execute either putBase!, put!, or putEntity!. When this happens on a mediated field, Templet does the following:

putBase@, put@, or putEntity@ precedes the putBase!, put!, or putEntity! with a getBase or get. If one of the get functions results in a rejection, the appropriate put! is still executed but the overall result is null.

An object that can act like a field mediator becomes associated with a field by the mediateField function. This causes the onAssociate functions of all the existing mediators of that field to be invoked to accept or reject the new mediator. The onDisassociate functions are called in response to unmediateField.

Typically, an access hook mediator would leave all functions with Mediator.Accept (after invoking its hook function):

  [\ The FieldHook mediator is created with an object
     that defines the methods onGet, beforeSet and afterSet.
     They are then called at the appropriate times. ]

  [newclass! pkgs.mediators FieldHook]
  [:noNdxs][:freezeNames hook][:trim][:conduct]
  
    [def! getVal][:void][:parms VAR VAR VARBASE]
      [%hook:onGet $1 $2 $3]
    [/def!]
  
    [\-------------------------------]
  
    [def! setEntity][:void][:parms obj field]
      [%hook:beforeSet $obj $field [$obj $field]]
    [/def!]
  
    [\-------------------------------]
  
    [def! postsetEntity][:void][:parms obj field val]
      [%hook:afterSet $obj $field $val]
    [/def!]
  
    [\-------------------------------]

    [def! new][:make!][:new][:parms VAR]
      [set! %hook $]
    [/def!]
  
  [/newclass!]
An access control mediator would leave with Mediator.Accept if access is allowed, and with Mediator.Reject if access is denied. A non-alias field mediator would only return a value from a function like getVal (instead of leaving) if it wanted to transform the result in some way.

Below we show a class that can be used as an access control mediator (except that Templet provides built-in support for this one).

  [\ ClassPrivate could be used to ensure that only a field
     of an object is only accessed by methods whose class
     is the same as the object's class ]

  [newclass! pkgs.mediators ClassPrivate]
  [:noNdxs][:noNames][:trim][:conduct]
  
    [def! test][:macro][:void][:private]
      [if (^caller.class == [$obj:getClass])]
        [leave Mediator.Accept]]
      [:else]
        [leave Mediator.Reject]]
    [/def!]
  
    [\-------------------------------]
  
    [def! getVal][:void][:parms obj]
      [^class.test]
    [/def!]
  
    [\-------------------------------]
  
    [def! setEntity][:void][:parms obj]
      [^class.test]
    [/def!]
  
    [\-------------------------------]
  
    [def! onAssociate][:void][:parms obj]
      [^class.test]
    [/def!]
  
    [\-------------------------------]
  
    [def! onDisassociate][:void][:parms obj]
      [^class.test]
    [/def!]
  
    [\-------------------------------]
  
    [def! new][:new][:parms]
    [:make!]
    [/def!]
  
  [/newclass!]

2.13.4 Object Mediators

An object that can act like an object mediator become associated with an object by the mediateObject function. This causes the onAssociate functions of all the existing mediators of that object to be invoked to accept or reject the new mediator. An object mediator can optionally support the following functions:

2.14 Web and Database Support

2.14.1 Server-Side Session Management

Templet is configured so that it can be used as a back-end server for arbitrary protocols, maintaining contexts specific to a request, and to a session. Depending on the protocol, sessions can be defined per-connection or via session-id's automatically maintained by Templet. In the case of HTTP, session id's are passed via cookies if the browser supports cookies; otherwise they are encoded in URLs. Protocol support is also planned for NNTP, IMAP4 and POP3.

The environment variable ^session references a context in which session-specific variables may be stored. As the server receives a request, it identifies the session with which it is associated, and associates the approriate session context with the initial environment used to process the request.

For protocols like HTTP in which session lifetimes cannot be determined, Templet allows specification of the length of time that session objects will be retained without use before they are garbage collected. For long-term storage, Templet can be configured so that some or all strings stored as session variables are backed up in a database.

Templet can be configured to deal with multiple simultaneous requests in the same session, allowing

As a web-driven back-end, Templet can either run as a Java servlet or as a separate Templet server, depending on a servlet, other server plug-in or CGI program to pass appropriate HTTP requests on to the Templet server; other configurations can be readily supported.

2.14.2 Server-Side User Management

Templet has a number of modules that can be used to determine the principal connecting to Templet. For HTTP, support is provided for automatic determination of the user via certificates, cookies, or basic authentication. For cases where developers want more control, Templet allows the explicit setting of a user id, and keeps sessions and users associated with one another. A variety of functions are available that assist in managing users-ids, including support for verifying e-mail addresses based on responses to registrations.

The environment variable ^user references a context in which user-specific variables may be stored. As with the session context, Templet can be configured so that some or all strings stored as user variables are backed up in a database.

Templet can be configured to control whether more than one session can simultaneously exist for the same user. Templet can

In any case, functions exist to allow Templet code to determine the sessions associated with a current user, the last time a request was processed for a given session, and to kill a session.

2.14.3 Server-Side Request Management

The environment variable ^request references a context in which request-specific variables may be stored. Templet supports inclusion of per-protocol plug-in modules that parse requests into request variables. In the case of HTTP:

URLs meant for (and produced by) Templet can be specially encoded. In addition to containing a session id, it can encode other information which is automatically parsed by the HTTP protocol module. A Templet URL can encode:

The plug-in for a protocol determines which Templet function is invoked to process a request. In the case of HTTP, a simple URL (without additional encoded information) starts a new session and delivers some home or entry page. The function used to provide this page is based on the path portion of the URL and is configurable via an initialiation file. URLs on that page (i.e. for links and forms) that refer back to the same site are dynamically produced and encoded.

As noted above, a URL for Templet can encode the export name of a function to be invoked, or of a file to be loaded and run, to process the request. The association between Templet files or functions and their export names is established by the exportFile or exportFunction function, which is typically called by the Templet code that initializes a site.

For RPC-like (i.e. synchronous) protocols like HTTP, each request produces a response, which is the result of the function called to process the request.

Some protocols, like HTTP, include a header, as well as content, as part of their response. The contents of the fields of ^request.response are used to determine the response headers. These must be filled in before enough content is produced to fill the first packet of the response.

2.14.4 HTML Support

As described above, URL's passed to Templet may encode session id's, export function names, and request and parameter values. These URL's need to be generated as part of documents that Templet produces. HTML.url, a static function in the HTML class, takes

and produces a suitably encoded URL. It obtains the current sesion id automatically (it can be obtained explicitly by calling [getSessionId]) and obtains the request values from the current values of ^request.values. Every field in ^request.values that contains a string is included as a named request value of the URL. In effect, ^request.values holds values that are passed along (and possibly changed along the way) from request to request.

Two additional HTML functions take the same parameters as HTML.url: The function HTML.link constructs an HTML a element, whose href parameter is the constructed URL, and whose embedded parameter is placed between the a element and the closing /a element. The function HTML.form similarly creates an HTML form element.

Support is also provided via HTML.plainUrl, HTML.plainLink, and HTML.plainForm for producing plain URLs without encoded parts. However, both methods do perform the "%"-style encoding used to transmit special characters. These functions take

The function HTTP.getUrl is used to make a request to a web server. In addition to the VAR parameter for the URL, it has two ALIAS parameters -- the first one indicates where an object will be stored into which the headers of the response will be parsed. The second indicates where a stream will be stored through which the content can be accessed. If the second parameter is not provided, the content will be returned instead as the result of the function. Similarly, HTTP.postUrl is used to post a request to a web server. It is additionally passed an object whose fields determine the input values passed.

Similar functionality is also planned for other HTTP operations (including support for WebDAV), for sending mail via SMTP, and for posting newsgroup articles via NNTP.

Templet includes a variety of functions for simplified construction of various HTML controls, including lists and groups of radio or check boxes with dynamically-determined initial values. For example,

  [HTML.radioGroup satisfaction]
    [:labeldef * <b>[$]</b> *]
    [:label very low|low|medium|high|very high]
    [:init low]
    [:separator/ <br>]
constructs a group of radio buttons. :labels determines the labels of the radio buttons; each label in the list is processed by the DEF parameter of the :labeldef clause to determine its actual value. Since there is no :values clause, the bare labels are also the values of the radio buttons. :init specifies the value of the radio button initially checked, and :separator indicates how the radio buttons are separated. The function invocation above produces the text
    <input type=radio name=satisfaction value="very low">* <b>very low</b> *<br> 
    <input type=radio name=satisfaction value=low checked>* <b>low</b> *<br> 
    <input type=radio name=satisfaction value=medium>* <b>medium</b> *<br> 
    <input type=radio name=satisfaction value=high>* <b>high</b> *<br> 
    <input type=radio name=satisfaction value="very high">* <b>very high</b> *

2.14.5 Client-Side Support

When Templet becomes available for client-side scripting, new classes (e.g. Window, Document) will becomes available that correspond closely to the EcmaScript model. We also expect that many of these objects (supporting DHTML operations) will be available on the server-side as well.

Templet code files will be able to be explicitly loaded and run on the client side, typically to define Templet functions which run in response to client-side events. In addition, the Templet script function will define Templet code that will be embedded within HTML SCRIPT tags and run on the client. Via an initial Server object, client-side code can retrieve references to other server-side objects, whose functions can then be called from client-side code.

2.14.6 Database Support

Templet provides access to databases through SQL as well as through a variety of functions that support a variety of standard kinds of access and update. While there are functions that allow a developer to use multiple databases and control connections to them, developers in general will use a single database and allow Templet to manage connections automatically. In such circumstances, code can be written similar to:
  [DB.SELECT husband, wife, date
   FROM Marriages
   WHERE [&cutoffDate] > date]
    <p>[$husband] was married to [$wife] on [$date]
  [/SELECT]
That is, DB.SELECT is a static function in the DB class with a single direct VAR parameter that (afer evaluation) represents the remainder of an SQL SELECT statement. Its embedded parameter is repeatedly invoked for each selected row, with the results concatenated together. The embedded parameter is invoked in an environment in which the column names correspond to parameters -- holding the value for the corresponding column in the current row.

If this statement will be executed multiple times, a Query can be constructed first, and separately processed:

  [DB.newquery! &query]
    SELECT husband, wife, date
    FROM Marriages
    WHERE date < [&cutoffDate]
  [/newquery!]

  [&query'Query:execute]
    <p>[$husband] was married to [$wife] on [$date]
  [/execute]
However, [&cutoffDate] will be evaluated once when the query is defined. If we want to recalculate the cutoff date between queries, we can use parameterized queries (where each parameter is cast to an SQL datatype)
  [DB.newquery! &query][:sqlparms cutoff'DATE]
    SELECT husband, wife, date
    FROM Marriages
    WHERE date < $cutoff
  [/newquery!]
  
  [&query:prepare [&cutoffDate]]

  [&query'Query:execute]
    <p>[$husband] was married to [$wife] on [$date]
  [/execute]
The query parameter name appears (prefixed by $) within the SQL statement. Preparing a query provides the values which replace the query parameters prior to processing the query. The same query can be prepared multiple times with different values.

3.0 History

3.1 Templet 1.0

Templet 1.0 was designed as a macro language for server-side scripting of HTML pages, and introduced the key characteristics of the Templet languages:

While built-in functions had parameter groups and supported the primary parameter mechanisms of Templet 3.0. user-defined functions in Templet 1.0 only allowed call-by-val and did not support clauses. In addition, all variables were global, except for indexed parameters.

Templet 1.0 had only two classes of objects -- functions and strings. Strings in Templet 1.0 always represented themselves; parameters were never interpreted as variables, thus the Templet 3.0 code [foo $] had to be written as [foo [$]]. Quotes were not used, and the string [] was used instead of blanks to separate parameters.

A prototype implementation of Templet 1.0 was produced in 1996 proving the power of the approach, and indicating the need for additional functionality.

3.2 Templet 2.0

Templet 2.0 added some important language features

A large number of built-in functions were included in Templet 2.0. Streams were added as well, including support for substitution of streams for strings in scanning string operations.

Templet 2.0 also included significant server-side functionality and web support, including transparent construction of URLs with session ids, request values, and function names (as part of defining links and forms), and simplified construction of controls with dynamically-determined initial values.

Templet 2.0 was completed in mid-1997, and was used to implement a number of personal web sites. It was available briefly as shareware, but unfortunately is no longer available or supported.

3.3 Templet 3.0

Templet 3.0 is now in the final stage of design. The primary language changes and extensions to Templet 2.0 include

Contact Ellis S. Cohen (e.cohen@acm.org) for more information on availability or on how you can participate in the implementation.