TOP | UP: UserTalk Basics | NEXT: Datatypes |
In general, Frontier evaluates names before using them. When you say:
x = y
it is the value of y that is assigned to x ; when you say:
myScript(y)
it is the value of y that is handed as a parameter to myScript(). Under certain circumstances, however, Frontier behaves exceptionally with regard to evaluation. Sometimes it doesn't evaluate a name; sometimes it bypasses the normal evaluation rules. This chapter is about these and related matters.
Four verbs - defined(), parentOf(), sizeOf(), and nameOf() - are special forms : they look like ordinary verbs, but they aren't. When they are used, their parameter is not evaluated. These verbs have in common that their parameter is an object reference - not an address - and it was this object reference itself, not its value, which passed to the verb.
To see why this is a good idea, consider defined(), which returns true or false depending on whether its parameter is the object reference of an existing object. Suppose defined() were not a special form. If we handed defined() an object reference and that reference was evaluated, we wouldn't be talking about the object itself. We could not hand defined() an address, though, because if the object in question didn't exist, it might be illegal to take its address in the first place.
The other special forms are parallel; one can speak to them of nonexistent objects without causing a runtime error.
If what you have is an address or string denoting the object reference, and you wish to hand that object reference as parameter to one of these verbs, you must dereference the address or string. For example:
defined (fakeTable.fakeEntry)
« false
myString = "fakeTable.fakeEntry"
defined (myString^)
« false, as expected; if we had said defined(myString)...
« ...it would have returned true - the string variable myString exists
Because of the non-evaluation, it is permissible to dereference a string denoting an unresolvable reference.1
Like most computer languages, UserTalk has a number of reserved words . The user is not free to employ such reserved words to designate ordinary objects. For example, the keywords, such as if, with, on, return, and local, are reserved words. If you could call a variable local, then whenever you said local, Frontier would have a hard time deciding whether you were referring to your variable or starting a local declaration.
The way Frontier enforces the special status of its reserved words is by pre-evaluating them. When you use a reserved word, Frontier never even tries to resolve it as a name; it discovers beforehand that the word is reserved. The resolution process is, as it were, short-circuited in order for reserved words to work.
Pre-evaluation is implemented by way of three lists of reserved words, which are maintained inside system.compiler. The keywords are kept at system.com-piler.language.keywords. Certain verbs that need special treatment - including the special forms we have just mentioned - live at system.compiler. language.builtins. In addition, the UserTalk constants - in other words, names whose values are predefined at compiler level - are in system.compiler. language.constants.
It is best not to attempt to give an object a name that is a reserved word. Because of pre-evaluation, which can extend even to individual elements of an object reference, you will not be able to refer normally to such an object. Here are some examples of the kinds of knots you can tie yourself in if you try it:
with workspace
pack()
Also, it is unwise to call a database entry by one of the names constants, keywords, or builtins.
The built-ins and reserved words are discussed individually later on, under the topic appropriate to the functionality of each. The UserTalk constants are as follows:
We have seen that you can obtain an extra round of evaluation for an element of an object reference that is a valid UserTalk expression, by enclosing the element in square brackets:
people.[user.initials]
By the same token, you can obtain an extra round of evaluation for any UserTalk expression. The expression must be constructed as a string; that string is then handed to evaluate() and the result of evaluating it is returned.
evaluate() is useful when an expression is unknown until runtime. This situation typically arises when a script, during the course of its execution, turns to the user to obtain a UserTalk expression. The communication takes place by way of a string. For example, suites.samples.basicStuff.runClipboard() is a script that lets the user copy text from another application for Frontier to evaluate in UserTalk; the user can then paste the result into the original application. And use of evaluate() in Frontier's Web site management facilities is what makes it possible to calculate pieces of a Web page at runtime; for instance, if {clock.now()} appears within the text of a Web page, the current time is substituted when the page is actually produced.
TOP | UP: UserTalk Basics | NEXT: Datatypes |