This page uses CSS, and renders properly in the latest versions of Microsoft Internet Explorer and Mozilla.
This is Frontier: The Definitive Guide by Matt Neuburg, unabridged and unaltered from the January 1998 printing.
It deals with the freeware Frontier 4.2.3; for commercial Frontier 8, look here, and for the inexpensive Radio UserLand, look here. See my Web site for information.
Those wishing an offline or printed copy may purchase the book, which is still in print.
Copyright 1998 O'Reilly & Associates, Inc. ALL RIGHTS RESERVED. Reprinted with permission.
TOP UP: UserTalk Basics NEXT: Arrays

10

Datatypes

This chapter discusses the UserTalk datatypes: what sort of entities they represent, how they are represented as literals, and how they are transformed from one type to another.

UserTalk has 31 datatypes (plus an extra type, unknownType, for handling objects that have not yet been assigned a value). Every UserTalk value is of exactly one of these types. It is customary to speak of an object's type ("s is a string"), and I often speak that way too; but this is actually shorthand for speaking of the object's value's type. Objects themselves do not have datatypes; indeed, any object can be assigned a value of any type in UserTalk, even if it already has a value of a different type. We say that UserTalk lacks strong typing.1

Internally, Frontier designates the datatypes with string4 identifiers. (String4s are discussed further below.) However, these string4 identifiers are given English-like constant names, so you don't have to use the string4s yourself. In system. compiler.language.constants, English-like names for the datatypes are paired with the string4 values whereby Frontier designates the datatypes. So, for example, to make a new outline in the database, you can say:

new (outlineType, @workspace.myOutline)
    « you don't have to say, new ('optx', @workspace.myOutline)
    « (though you could if you wanted)

The English-like names for the datatypes are reserved words; see Chapter 9, Special Evaluation .

You can learn the datatype of a value by passing it as a parameter to the verb typeOf() ; what is returned is the string4 designating that datatype.

For most of the datatypes, Frontier also provides coercion verbs, which let you transform a value to an equivalent value of a different datatype.

A rather daunting feature of Frontier is that there are so many different names associated with types in various contexts. The English-like constant names are not the same as the names you see in the Kind column of a table edit window; these in turn are not the same as the names in the Kind popup at the bottom of a table edit window; and these are not the same as the names used in the Table menu to make a new object. The name of the coercion verb and the constant name of the type sometimes don't stand in a simple relationship. And the string4 names are just codes, and no facility is provided for translating them into English; when you receive a string4 from typeOf(), you may not know what datatype it means, and there is no automatic way to find out.2

Table 10-1 summarizes the situation.

Table 10-1 Types, Coercion Verbs, and Names Associated with Them

ID

Constant Name

New...

Kind Column

Kind
Popup

Coercion Verb

'optx'

outlineType

Outline

outline

Outline

[N/A]

'wptx'

wptextType

Text

wp text

WP-Text

[N/A]

'tabl'

tableType

Subtable

table

Table

[N/A]

'scpt'

scriptType

Script

script

Script

[N/A]

'mbar'

menubarType

MenuBar

menu bar

MenuBar

[N/A]

'pict'

pictureType

Picture

picture

Picture

[N/A]

'shor'

intType, shortType

[N/A]

number

[N/A]

short()

'long'

longType

Number

number

Number

long(), number()

'exte'

doubleType

Float

double

Float

double()

'sing'

singleType

[N/A]

single

[N/A]

single()

'fixd'

fixedType

Fixed

fixed

[N/A]

fixed()

'TEXT'

stringType

String

string

String

string()

'char'

charType

Character

char

Character

char()

'type'

string4Type

String4

string4

String4

string4()

'date'

dateType

Date

date

Date

date()

'data'

binaryType

Binary

binary

Binary

binary()

'bool'

booleanType

Boolean

boolean

Boolean

boolean()

'addr'

addressType

Address

address

Address

address()

'QDpt'

pointType

Point

point

[N/A]

point()

'qdrt'

rectType

Rectangle

rect

[N/A]

rect()

'cRGB'

rgbType

Color

rgb

[N/A]

rgb()

'tptn'

patternType

Pattern

pattern

[N/A]

[N/A]

'obj '

objspecType

Object Spec

objspec

Object Specifier

objspec()

'fss '

filespecType

File Spec

filespec

File Specifier

filespec()

'alis'

aliasType

Alias

alias

Alias

alias()

'enum'

enumeratorType

Enumerator

enumerator

Enumerator

enum()

'list'

listType

List

list

List

list()

'reco'

recordType

Record

record

Record

record()

'dir '

directionType

Direction

direction

Direction

direction()

'tokn'

tokenType

[N/A]

token

[N/A]

[N/A]

'code'

codeType

[N/A]

compiled code

[N/A]

[N/A]

Summary of the Datatypes

The datatypes may be categorized as follows:

Non-scalar

These are types that have their own edit windows within Frontier: outlineType , scriptType, wpTextType, tableType, menuBarType, pictureType. All other types are scalars , meaning that they are editable by typing directly into a table's Value column. A local variable can be of a non-scalar type; it is perfectly legal, for example, to declare a local variable and use new() or assignment to turn it into an outline. You can even open an edit window on such an outline!

Number

intType and longType are integers; intType is like a short in C, longType is like a long and is more commonly used. singleType and doubleType are single-precision and double-precision floating-point; doubleType is the more common. fixedType is fixed-point, and exists mostly for compatibility with the computer's toolbox routines (I have never seen it used).
Shorts range from -32768 to 32767; longs range from -2147483648 to 2147483647. Single-precision is accurate to about 9 digits, double-precision to about 19. A numeric literal is assumed to be a long, unless it contains a decimal point, in which case it is assumed to be a double. Frontier does not understand scientific notation. A hexadecimal literal may be represented by beginning it with 0x (for example, 0xF is 15).3

String

stringType is a string; a string literal is delimited either by double quotes (straight quotes) or by smart quotes (curly quotes). charType is a single character; a char literal is delimited by single quotes. string4Type is four characters packed into the same space as an unsigned longType; a string4 literal is four characters delimited by single quotes. charType and string4Type have certain affinities with strings, but also certain affinities with integers, as we shall see when we discuss coercion.
The literal representation of strings and chars is a large topic and is discussed further at the end of the chapter.

Date

Dates, embracing both dates and times, use the dateType , which is actually an unsigned long representing the number of seconds since some fixed date (on Mac OS, this is midnight on 1 January, 1904).

Binary

binaryType lets a scalar contain raw data of any kind; for example, a picture file such as a GIF can be imported into the database as a binary. A binary actually has two types: its own, which is binaryType, and that of its contents.

Boolean

booleanType is the type of the constants true and false and of the results of logical tests; what appears after if must be a boolean or be coercible to a boolean. For the operators that can be used in a boolean expression, see Chapter 44, Operators .

Address

addressType is a pointer to an object. Addresses are represented as literals in tables and scripts by @ followed by the name of the object pointed to; elsewhere, they appear coerced to a string naming the object.

Pattern

patternType is a representation of a 16-by-16 bitmap pattern as 16 hex digits; I have never seen this used.

List

listType and recordType are ordered collections of items of any scalar type. As literals, they are delimited by curly braces and the items are separated by commas; so, for instance, this is a list, consisting of two items which are strings:
{"hi", "there"}
Records are lists where the individual items also have names, which can make it more convenient to specify an item; the names must be (or evaluate to) string4s. As literals, records look like lists except that each items is preceded by its name plus a colon. Thus, for example:
{'name':"Matt Neuburg", 'occu': "none", 'aged': 43}
Lists and records can be used to communicate with other programs, which sometimes expect or return these types, and they are also valuable as internal datatypes.

Set of shorts

pointType , rectType, and rgbType are ordered sets of two, four, and three shorts, respectively, which represent points (x and y), rectangles (top, left, bottom, and right), and RGB colors (red, green, and blue). As literals, they cannot be represented directly; they must be coerced from lists or strings of comma-separated numbers.
On Mac OS, the Cartesian plane is inverted top-to-bottom; the main screen has a top-left of 0,0 and both x and y values on the screen are positive.

Direction

directionType is used for the direction constants, which are passed as parameters to UserTalk verbs that manipulate the cursor in wptext and outline edit windows.

Token

tokenType is used for the tokens into which UserTalk keywords and built-ins are translated at compile time.

Compiled code

codeType is the type of the local "variable" generated by executing an on() declaration; the local handler is copied as compiled code into Frontier's compiler stack. Also, it is possible to strip the source from a compiled script for security purposes, leaving only the compiled code, which then can be called, but not read by a human being; a database entry containing such code is of type codeType.

Other

objspecType , filespecType, aliasType, and enumeratorType are mainly for communicating with other programs, which sometimes expect these types as parameters. It is only occasionally necessary to coerce explicitly to any of these types, chiefly in tweaking "glue" verbs (Chapter 32, Driving Other Applications ). It often makes sense, when storing the pathname of a file in the database, to make it a fileSpec or alias.4

Coercion

Coercion is the transformation of a value into an equivalent value of a different datatype. To request coercion explicitly, one generally uses the coercion verbs, listed in Table 10-1.

No type-checking of parameters is performed when a verb is called. If, however, a value of incorrect type is passed as a parameter to a UserTalk verb that is genuinely built-in (i.e., implemented as a token somewhere in system.compiler), or if an object is subjected to an operator inappropriate to its type, Frontier will silently attempt to coerce the object to the correct type. If an operator that takes two operands is applied to objects of different types, Frontier will silently attempt to coerce at least one of them so that their types match. Such silent, automatic coercion is called implicit coercion . If Frontier cannot perform an attempted implicit coercion, a runtime error occurs.

We now discuss a couple of types for which coercion is performed by verbs not listed amongst the coercion verbs, and then describe the most commonly encountered coercions.

Binaries

A binary has two types - its own, which is binaryType , and that of the data it contains, called its internal type . The internal type can be any type whatsoever. In fact, it does not even have to be a Frontier datatype; it is merely a string4 attached to the binary, and this can be any string4 at all.

The ordinary coercion verb binary() may be used to coerce a value of any type to a binary; the binary's internal datatype is automatically set to the type of the original value. Going the other way, if a binary's internal type is one of the scalar types, that scalar type's coercion verb can be used to extract the data from the binary. For example, if you have said:

workspace.mybinary = binary("Hello, World!")

then string(workspace.mybinary) evaluates to "Hello, World!"

However, transformation to and from a binary can also be performed with the verbs pack() and unpack(). pack() is identical in effect to binary(), differing only in the syntax of the call; you provide as second parameter the destination address. So these are equivalent:

workspace.mybinary = binary("Hello, World!")
pack ("Hello, World!", @workspace.mybinary)

The reverse of pack(), namely unpack(), is the only way to extract a non-scalar from a binary. It also has a major advantage over coercion as a way to extract what is in a binary: you don't have to know in advance the type to which the binary is to be coerced - the internal type is used automatically. unpack() takes two addresses, that of the source (the binary) and that of the destination. So the following:

unpack (@workspace.mybinary, @workspace.whatWasInMyBinary)

would place the string "Hello, World!" into workspace.whatWasInMyBinary.

To learn a binary's internal datatype, call getBinaryType( theBinary ). To set a binary's internal datatype, call setBinaryType( addrBinary, theType ). In practice, these verbs are used chiefly to manipulate the internal datatype as a marker - to create your own custom types, for instance, or to allow the data to be handed to another program that expects a particular type. For example, when Frontier's Web management features are used to read a GIF or JPEG image file into the database, the resulting binary is explicitly given an internal type of 'GIFf' or 'JPEG'; when the binary is to be written out to disk as a file later, the internal datatype is checked in order to determine the suffix to be appended to its name.

Sets of Shorts

Points, rects, and rgbs can be set by coercing a comma-delimited string or list consisting of the correct number of shorts; for example, these both work:

myPoint = point ("1, 2")
myPoint = point ({1, 2})

To obtain the value of a component of a point, rect, or rgb, one approach is to coerce it to a list, because it is easy to get a list's n th element (discussed in Chapter 11, Arrays ):

myPointAsList = list (myPoint); x = myPointAsList[1]

But it is often more convenient to use verbs which set or get all the component values at once. The set verbs have the desired entity as their result; the get verbs take as parameters the entity and addresses of objects into which to put its parts. Thus:

point.set (x, y)
point.get (pt, addrX, addrY)
rectangle.set (top, left, bottom, right)
rectangle.get (rect, addrTop, addrLeft, addrBottom, addrRight)
rgb.set (red, green, blue)
rgb.get (rgb, addrRed, addrGreen, addrBlue)

A Mac OS color is represented by a set of three unsigned shorts; but Frontier has no such datatype. Thus white, which is represented by {65535,65535,65535} in Mac OS, is represented as {-1,-1,-1} in Frontier.

There is a search path to system.verbs.colors , which contains 140 named rgb colors, ready to use. Thus, to set a color, one can simply say:

myColor = aquamarine

However, these colors are not rgbs: they are hex triples, in Web style. To convert one of them to an rgb, use the following utility.

Example 10-1 tripletToRGB( )
on tripletToRGB(trip)
    on sign(n)
        if n > 32767 {n = n - 65536}; return n
    on extract(n)
        return number("0x" + trip[n] + trip[n+1]) * 257
    local
        theRed = sign(extract(1))
        theGreen = sign(extract(3))
        theBlue = sign(extract(5))
    return rgb.set(theRed, theGreen, theBlue)

Coercion Results

On the whole, the various datatypes coerce from one type to another in an intuitive and intelligent manner. The following discussion is not complete, but in it I have tried to cover the most interesting and important cases.

alt
TIP Frontier helps you know what coercions are possible amongst scalars, in any table edit window; when you select a table entry, the Kind popup enables only those datatypes to which the selected entry (given its kind and value) can be coerced. (And choosing from the popup performs the coercion.)
alt
Non-scalars

The non-scalars consisting of textual matter - script, outline, menubar, table, wptext - can be coerced to strings.5 There are no verbs for coercing back in the other direction; however, pasting into a script, outline, or wptext can be a substitute.

Scripts and outlines are coerced to strings identical to the result of copying the script or outline and pasting it into a textual environment.6 Coercion of a wptext to a string loses formatting information, but the text itself remains intact.

Menubars are an odd case, because a menubar is an outline in which an item can have a script associated with it. When a menubar is coerced to a string, the result is like that for a outline, with each script appearing indented below its menu item. Curly braces and semicolons are not added to the script parts, because the menubar is itself more like an outline than a script:

Add to Glossary...
	local (name = webBrowser.getFrontWindowTitle ())
	if dialog.ask ("Name:", @name)
		local (s = "<a href=\"" + webBrowser.getFrontWindowURL ())
		s = s + "\">" + name + "</a>"
		user.html.glossary [name] = s
		filemenu.save ()
Open Glossary
	Frontier.bringToFront ()
	edit (@user.html.glossary)

Tables are coerced to strings so as to simulate the layout of a table, using a three-column tab-delimited structure. Each line contains the object's name, its type (followed, oddly, by a colon), and its value (coerced to a string). The conversion does not work perfectly, and chokes on tables of any real size, but it is useful for small tables of simple data.

myshort       number:       6
mysingle      single:       6.0
newAlias      alias:        Desire:AppleScript Utilities:Frontier 4.1:
newBinary     binary:       0xAF963122
newBoolean    boolean:      false

For conversion between outlines and lists, see on toys.outlineToList(), Chapter 18, Non-Scalars .

Scalars

Strings are the most universal type: they can be coerced to and from all other scalar types. Since coercions involving strings are generally obvious, what follows is mostly a summary of the commonly encountered scalar coercions not involving strings:

Numbers

Number types coerce to one another as expected. Coercing a floating-point to an integer truncates its value.

Chars and string4s

Numbers, chars, string4s, and strings are interrelated. A string is made up of chars; but, from another point of view, a char is a short and a string4 is a long.
Coercing a char to a string yields a one-character string, and a one-character string can be coerced to a char. Coercing a char to a number yields its ASCII numeric value, and vice versa (UserTalk has no asc() function). This can feel surprising; one expects number('3') to be 3, but of course to get that you'd have to say number(string('3')). A string4 can be coerced to a long, and vice versa.

Booleans

The boolean values false and true can be coerced to numbers, yielding 0 and 1, respectively. In the reverse operation, any number except 0 yields true; 0 yields false. Any string except "false" (not case-sensitive!) or the empty string coerces to true.

Points, rects, and rgbs

A point can be coerced to a long and vice versa; the x-component of a point is stored as the lo-byte of the long, the y -component as the hi-byte. Points, rects, and rgbs coerce to strings as comma-delimited series of numbers.

Lists

A list or record coerces to a string which looks just like the list's or record's literal representation, curly braces and all.7 For example:
string( {"hello", 3} )
    « "{\"hello\", 3}"
Any scalar can be coerced to a list; the list contains one element, namely, the original scalar. Just the other way, a one-element list can be coerced to a scalar (though this would be an odd way to code; it is simpler just to extract the element using array notation).
Nothing except a string can be coerced to a record.

Directions

The directions can be coerced to and from numbers (nodirection becomes 0, up becomes 1, down becomes 2, and so on).

Files

FileSpecs and aliases are coerced to and from strings that are pathnames, describing paths to files in standard format. On the Mac, a file called myFile on my hard disk which is called HD can be aliased as alias("HD:myFile"); my hard disk would be alias("HD:") (notice the final colon after volumes and folders). Coercing an alias to a fileSpec resolves the alias (if the alias is valid; otherwise, an error results). One cannot coerce to a fileSpec unless the folder containing the file actually exists; but an alias can be made from any pathname string.

Dates

A date coerced to a string is formatted like this on my machine: "5/7/97; 11:35:04 PM". But your results may be different, because the coercion calls the system, which consults the date format settings in the Date & Time control panel.
A string can be coerced to a date even if it doesn't quite hold to this format; various separators can be used, and the name of the month can be written out or abbreviated. Thus, Frontier is flexible enough to handle:
date("7 May 1997")
date("may-7-97")
date("7 may, 1997 11:43 PM")
and many other variations. What isn't so easy is getting a simple time to be understood; date("11:36 AM") works because there is no 36th of the month, but date("11:30 AM") does not - you have to use a date verb (discussed in Chapter 16, Dates ).

Addresses

An address is coerced to a string which, if dereferenced, would yield the same object reference as the address; for example:
string (@workspace)
    « "root.workspace"
string (@scheduler)
    « "suites.scheduler"
Strings can be coerced to addresses if they can be resolved as object references at the moment the coercion is made; thus, address("glubglub") fails because there is no object in the search paths called glubglub, but it would succeed if you said it within the scope of a local variable called glubglub. As mentioned in Chapter 8, Addresses , a dereferenced string is implicitly coerced to an address first.

Type-Dependent Operations

Where an operation takes two operands, if these are of the same type, the operation generally yields that same type and is performed according to the rules for that type. This seems sensible enough, but many beginners are surprised by the results.

Consider addition. The + operator is overloaded: adding numbers adds them arithmetically; adding strings concatenates them. Thus:

50 + 1
    « 51
"User" + "Talk"
    « "UserTalk"
"50" + "1"
    « "501", because they're strings

So far, so good, but when adding variables instead of literals one may be unaware of their datatype. Even very experienced UserTalk programmers confess to being occasionally caught out by this. This script asks the user for two numbers, adds them, and displays the result; but it displays the wrong result, because a dialog's edit field always contains a string, so the operands in the last line are strings and are concatenated, not added:

local (firstnum, secondnum)
if dialog.ask ("Give me a number.", @firstnum)
    if dialog.ask ("Give me another number.", @secondnum)
        dialog.notify (firstnum + secondnum) « oops

Integer operations sometimes pose surprises, too. If both operands are integers, the result will be an integer; so, if the result is out of range, it wraps around from the other end:

infinity + 2 == -infinity
    « true! This should give Georg Cantor something to think about

An integer divided by an integer yields an integer; the decimal part of the result, if any, is thus thrown away. Beginners are confounded by this sort of thing:

5 / 2
    « 2

If they know about coercion, they may try to compensate like this:

double (5 / 2)
    « 2.0

But of course the coercion comes too late; the division has already been performed as an integer division; having obtained the wrong result, we are merely expressing it as a double. Similarly, the following does not give the right answer:

double (2147483600 * 2)

The multiplication is integer multiplication, which goes out of range and wraps around to a negative result before the double() coercion ever takes place.

It is sufficient in cases like these (for reasons we shall explain immediately in the next section) to coerce one operand to the type of the desired result before the operation, like this:

double (5) / 2
    « 2.5
double(2147483600) * 2
    « 4294967200.0

Mixed Operands

Where an operation takes two operands, and these are of different types, or not of the right type, a rather tricky form of implicit coercion takes place. In general, as each operation is handled, there is coercion of its operands to the type of the "higher" operand. The precedence of types is roughly arranged as follows, from lowest to highest: boolean; short; point, long, char, string4, and direction; date; single and fixed; double; rect, rgb, alias, and address; string; binary; list; record.

So, taking addition as an example:

"50" + 1
    « "501", because they're both strings after the coercion

Evaluation is pairwise, so if you add several numbers with a string mixed in, the operation won't turn into concatenation until we reach the string. Thus:

3 + 4 + "5"
    « "75", because 3+4 is 7 and then "7"+"5" is "75"

When an operation involves two different types with the same precedence, the coercion is to the type of the left operand. Here, for example, is what can happen when integers and chars are mixed in an arithmetic calculation:

1 + '3'
    « 52, because '3' is ASCII 51
'3' + 1
    « '4', the char whose ASCII value is 52

Things are similar if you add a string4 and an integer.

The second example is particularly tricky. Because one operand is an integer, the operands are added as integers, but the result is a char. (Compare also the arithmetic treatment of dates; they become numbers for the purposes of the operation, but the result is presented as a date.) Contrast what happens when two chars are added:

'3' + '1'
    « "31"

Here, the operands are treated as strings (and concatenated), though neither is a string. It is not always easy to know what Frontier will do; experimentation is helpful.

The fault, though, is one of virtue: instead of complaining, Frontier tries to obey your commands gracefully. What on earth, for example, can the following possibly mean?

'helo' + true

Yet Frontier will permit it: true is converted to a string4 in accordance with the precedence rules, and then the two string4s are added, which means treating them as integers but representing the result as a string4 (the answer turns out to be 'help'). We may think of booleans, dates, chars, string4s, together with actual numeric types, as "numeric equivalents"; if they are involved in numeric operations, they may be treated as numbers.

Runtime Coercion

There is no built-in facility to perform coercion to a datatype which will not be known until runtime.8 Yet one does occasionally need such a facility. Suppose, for example, we want to present a scalar database entry in a dialog so that the user can edit it, and then replace the original value with the edited value. The edited value is always going to be a string, so we need to coerce it back to the type of the original scalar - whatever that may be.

Here is a utility verb, coerceTo() , which takes both something to coerce and a datatype to which to coerce it. It's simple: we make a record of all of the datatype-coercion verb pairs and perform a lookup; then we just construct and perform the coercion command. The script looks perfectly horrid, but it is worth having on hand.

Example 10-2 coerceTo( )
on coerceTo (whatToCoerce, whatType)
    theRec = {charType:"char", longType:"long", binaryType:"binary", \
        dateType:"date", booleanType:"boolean", addressType:"address", \
        doubleType:"double", stringType:"string", directionType:"direction", \
        string4type:"string4", pointType:"point", rectType:"rect", \
        rgbType:"rgb", \
        fixedType:"fixed", singleType:"single", filespecType:"filespec", \
        aliasType:"alias", listType:"list", recordType:"record"}
    return theRec[whatType]^(whatToCoerce)

String and Char Literals

Character literals in UserTalk are delimited by single quotes. What appears between the single quotes must represent exactly one character, so 'h' is legal, but 'hi' will raise a compiler error.

String literals in UserTalk may be delimited either by ordinary double quotes, sometimes called straight quotes (""), or curly quotes, sometimes called smart quotes ("").9 This provides an elegant way to express strings within strings. Suppose, for example, you wish to assign to a variable a literal string whose value is:

"I've dropped my toothpaste," said Tom crestfallenly.

You cannot do this by simply enclosing the value in straight quotes, like this:

x = ""I've dropped my toothpaste," said Tom crestfallenly." « won't compile

The problem is that Frontier sees what it thinks is an empty string literal:

x = ""

and then can't figure out what the rest of the line can possibly be. The solution is to enclose the value in curly quotes instead:

x = “"I've dropped my toothpaste," said Tom crestfallenly.”

You can write it the other way round, too (use straight quotes for the outer pair and curly quotes for the inner pair), but the result is not the same; in the actual value of x , the quotes around what Tom says will be curly.

A second way to render a double quote inside a string is to "escape" it. "Escape" means that a special character is used within a string to change the value of what follows in some way. The basic escape character is the backslash. Note that all the backslash escape combinations can be used in either a string literal or a char literal. They are as follows:

\"

Indicates a double quote. (Not needed in a char, but not illegal.)

\'

Indicates a single quote. (Not needed in a string, but not illegal.)

\r

Indicates a return character (char constant equivalent:10 cr).

\t

Indicates a tab character (char constant equivalent: tab).

\n

Indicates a linefeed character (char constant equivalent: lf).

\x nn

Indicates the character whose hexadecimal numeric equivalent is the two-digit short nn. It's okay to omit one of the digits (or both, in which case this is interpreted as \x00).

\\

Indicates a backslash; necessary because the backslash is otherwise understood as escaping the next character, not as a literal backslash.

It is not an error to escape a character other than those listed here, but it won't have any effect, either.

Escape-notation sometimes confuses beginners. The key is to remember that it's just a way of writing; when you say "\r" in UserTalk you have used a string only one character long.

String literals are limited in UserTalk to 255 characters. However, string values (of database entries and variables) may be of any length, and one may use concatenation (or any other string operation) to generate them. Thus:

myString = "[ 500 characters ]" « fails
myString = "[ 250 characters ]" + "[ 250 more characters ]" « fine

How Frontier Displays String and Char Literals

The way Frontier displays string and char literals varies with the context; this can be confusing.

In reporting the value of a char or string in the Quick Script result box, or as a result of choosing Run Selection within a script, or in a table, Frontier will surround the literal with any required delimiters (such as double quotes or single quotes), and will use escape notation - for example, if the string contains a return character, you will see \r.11 This is because the value is handed to the verb displayString() first, which generates the "external representation" of a string or char, the literal you would type if you were talking to Frontier. This is hard to read, because you have to understand escape-notation, but it's more precise because you really see what each character is, and it's useful because it shows how to represent the string as a literal.

In a dialog, or in the Main Window, a string or char value is interpreted. If the string contains a return character, then in a dialog you will see a new line at that point; the Main Window can't display interpreted return characters at all, because it's only one line. By the same token, 'a' displays 'a' in Quick Script, but msg('a') displays a in the Main Window, because the value is coerced to a string.

You can call displayString() yourself. For example, saying:

msg(displayString("Hello\rThere"))

causes "Hello\rThere" to be displayed, just as it would be if you said "Hello\rThere" in Quick Script. It gets tricky if you display the result of a call to displayString() in Quick Script, because displayString() is really being called twice. For example:

displayString("Hello\rThere")
    « "\"Hello\\rThere\""

This tells you the string literal you would have to type in order to make a dialog display "Hello\rThere" - which may or may not be what you wanted to know.

For a very different use of displayString(), see Chapter 32, Driving Other Applications .


1. Contrast Pascal and C, where a specific type is associated with every object name. UserTalk's approach to types is more like Scheme's.

2. Well, there is an automatic way, actually, but it's rather an elaborate trick. To find out, for instance, what the constant name for the 'qdrt' type is, execute the following script in the Quick Script window:

with system.compiler.language.constants {displaystring('qdrt')}

3. To convert a number to its hex representation, use string.hex(); see Chapter 14, Strings and Chars .

4. An alias stored in the database works just as an alias file does: if the file to which it points is moved, it changes its value and continues to point to it. A fileSpec, on the other hand, is frozen and cannot be counted on to be valid the next time Frontier is started up. In the case of fileSpec database entries, Frontier compensates, to some extent, by silently coercing a fileSpec database entry to an alias when Frontier is shut down, and back to a fileSpec when Frontier is next started up; but on the whole, fileSpec values should be regarded as temporary and confined to a particular script execution.

5. A picture can be coerced to a string, but it just says "<<picture>>\r".

6. We have already discussed how Frontier pastes a copied script into a textual environment. Outlines are just the same, except that no curly braces or semicolons are inserted.

7. When a list is coerced to a string, there's a bug: if any one item of the list becomes a string longer than 256 characters, the excess is truncated. For example, if x is a string of 200 characters and y is a string of 200 characters, then string({x,y}) will work, because each individual item is less than 256 characters long; but string ({1,{x,y}}) won't work, because one item, {x,y}, is some 400 characters long.

8. On Mac OS there is a verb coerceValue() , but it isn't really a Frontier verb; it simply hands the arguments to the system's AECoerceDesc() function. This doesn't work for every Frontier datatype.

9. On Mac OS, curly quotes are typed with Option-[ and Option-Shift-{.

10. These constant equivalents are at system.verbs.constants. They cannot be used inside a literal string, obviously, but are useful shorthand alternatives to char literals.

11. There is a difference between the representation of literal strings in these situations, though: in the Quick Script result box, or as a result of choosing Run Selection, there are straight quotes around the string; in a table, there are not. Therefore, in a table, double quotes in a string literal are not escaped.


TOP UP: UserTalk Basics NEXT: Arrays

This is Frontier: The Definitive Guide by Matt Neuburg, unabridged and unaltered from the January 1998 printing.
It deals with the freeware Frontier 4.2.3; for commercial Frontier 8, look here, and for the inexpensive Radio UserLand, look here. See my Web site for information.
Those wishing an offline or printed copy may purchase the book, which is still in print.
Copyright 1998 O'Reilly & Associates, Inc. ALL RIGHTS RESERVED. Reprinted with permission.