Tutorial - Functions and Prologs in Candle


In this tutorial, we'll go through the top-level language constructs in Candle, the routines and prologs in Candle.

At top-level, there are several important differences between Candle and XQuery, which highlights the different design philosophy of the two languages. Candle is designed to be a general-purpose scripting language, capable of developing large applications; whereas XQuery is designed to be a focused DSL (domain-specific language). Based on this contrast, it will be easy to understand the top-level language differences:
There are 4 types of routine in Candle: expression function, statement function, template and method. The first two types are covered in this tutorial, and the other two are covered in following tutorials.

Global Variable vs. Tunneled Variable

Candle does not support global variable. This is an intentional design. In an ad-hoc query, like XQuery, global variables can be useful. But in a large, complex application, global variables can become unmanageable. This is also one of the reasons Java does not support global variable.

The other reason that Candle does not support global variable is to encourage Candle users to adapt a better language facility, i.e. the tunneled variable, which can make Candle program more adaptive. Say function A calls function B, they'll see the same global variable value. There's no way for function A to change the values of the global variables that function B is going to see, unless we go procedural (which is very bad practice). But with tunneled variable, that's easy. Function A can shadow any value on the stack by putting new value on the stack with the same name before calling B.

The value of tunneled variable in Candle is more than just saving the trouble of passing parameters around. The other, probably more important, usage of it is to make routines adaptive or context-sensitive. When a routine takes value from the tunneled stack, it actually participates in context inheritance. Context inheritance is probably one of the most important things invented after class inheritance. Web page designers understood its value and make heavy use of it when they design CSS stylesheets. With the popularization of XSLT and Candle, we'll see more creative usage of this feature in the programming world in the coming years.

Functions in Candle

There are two types of functions on Candle: expression functions and statement functions, as 'good things come in twos'. The following table compares two types of function:
Expression Function Statement Function
Syntax function name(parameters) as return-type { expression } function name(parameters) { statements; }
Return Value Expression function always has return value. Statement function does not return value directly.
Routine Body The body of an expression function is a single expression, most likely a nested FLWOR expression. The body of statement function contains many statements.
Functionality An expression function evaluates an expression and returns its value. A statement function executes some statements, which construct some node output in the default output document in the context.
Common Features Two types of function do share many common features. Though the syntax is slightly different, but the semantic is almost the same.
FLWOR expressions and node construction expression. FLWOR statements and node construction statements.
Different Features Some node construction features are only supported at statement-level, like the apply; and apply to expr; statements for applying template transformation.

While two types of functions might have similar appearance, they have quite different usage. As you practise more, you should be able to tell when to use one and when to use the other. Generally, if you are processing some atomic values, then you should use expression function; and if you are constructing some nodes, then statement function is normally more convenient.

There's no absolute barrier between these two types of routines in Candle, as between method and function. In Candle, following the principle of separation-of-side-effects, a function can never call a method. But between these two types of functions, the separation is just syntactic. They can easily call each other. The example below is an illustration:
function format-month-expr(dt) as string {
  let month-str = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec")
  return month-str[dt?month]
function format-month-stam(dt) {
  let month-str = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
  { month-str[dt?month] }
function format-date-expr(dt) as string* {
  (!! we intensionally calls the format-month statement function
  <temp>format-month-stam(dt);</temp> + " " + dt?day + ", " + dt?year,
  " or ",
  !! calling the format-month expression function
  format-month-expr(dt) + " " + dt?day + ", " + dt?year)
function format-date-stam(dt) {
  {format-month-expr(dt)} " " {dt?day} ", " {dt?year}
  " or "
  format-month-stam(dt); " " {dt?day} ", " {dt?year}
function main() {
  let today = today();
  "Today is: " { format-date-expr(today) } <br/>
  "Today is: " format-date-stam(today); <br/>
Try it yourself »

Prologs in Candle

There are only are a few types of prologs in Candle. They are:
Prolog Type Syntax Remarks
namespace declaration namespace default-ns, prefix1=ns:name, prefix2='uri'; The markup is reserved for default markup namespace declaration and script is reserved for default script namespace declaration.
import prolog import at 'uri'; Import from other scripts.
schema declaration schema name { ... } Defines a node schema. Shall be covered in details in following tutorial.
grammar declaration grammar name { ... } Defines a grammar. Shall be covered in details in following tutorial.
expression function declaration function name(parameters) as type
{ expression }
Already covered in this tutorial.
statement function declaration function name(parameters)
{ statements; }
Already covered in this tutorial.
template declaration template <pattern> name (parameters) { statements; } Defines a template. Shall be covered in details in following tutorial.
method declaration method name(parameters) as type
{ statements; }
Defines a method. Shall be covered in details in following tutorial.

Namespace Declaration

In the latest release of Candle, a new qualified name and namespace notation is introduced. This new notation is similar to Java's namespace. XML's namespace syntax using URI is still supported for backward compatibility.

Here's an example of namespace declaration:
namespace ns:default:name:space, sty=ns:org:candlescript:style, svg='http://www.w3.org/2000/svg';

ns:default:name:space is a default namespace declaration. It must proceed the prefixed namespace declarations.

The new recommended namespace notation is a hierarchy of names, starting with ns and separated by token ':'. Candle does not mandate domain names to be used after the top level name ns, but for the names to be globally unique, you should do so. The top level name ns allows us to easily differentiate fully expanded name from prefixed name.

Here's an example element in the same document containing the above namespace declaration:
<foo bar=baz svg:d=(M,100,50,L,75,110,Z) sty:fee:foe=123>

Qualified names in a markup document are resolved based on the following simple rules:
The above rules apply to any Qname in a document, whether it is element name, or attribute name, or Qname in the literal value.

Import Prolog

The import prolog allows you to import routine, grammar and schema definitions from another script. Note that the definition import is not transitive. That is if script A imports script B, and script B imports script C. Definitions in script C are only visible to script B, not script A. (In current beta release, the URI should only point to a local file. It cannot be a script from the Internet.)