A lesson in variable name scoping from a longtime Frontier user.
By Scott Lawton, http://www.tiac.net/prefab/
Although I've been using Frontier for quite awhile, I just got bit by
something that's likely to happen to others. It took me a long time to
track down, so I thought I'd share the lesson to perhaps save someone else
the trouble.
In Frontier's UserTalk and any other block-structured language, all
variables have a "scope", i.e. a context in which they are valid. Although
UserTalk doesn't require that you declare variables (using "local"),
experienced scripters highly recommend it.
local « declare x as a variable
x
Traditional computer languages such as Pascal and C require that variables
be declared with a certain datatype, e.g. string, number or whatever.
Frontier lets you change the datatype on the fly, just by assigning a new
value. (A very handy feature!) You can also assign an initial value in
the "local" block, and define many variables in a single block. (In fact,
you don't have to use the block structure for local, though I prefer it.)
local « declare x & y and give them initial values
x = 1
y = "hello"
In general, the scope of a variable is the "block" (level of outline) in
which the "local" definition appears (or the first use, if you don't
declare it), plus all deeper levels.
A made-up example:
local
x = 10
msg (x)
msg (y) «error: y hasn't been declared or defined
if x > 5
local
y = 5
msg (x) «x is still valid here
msg (y) «y's "scope" is this "if" block
Note that you can use the same name for two different values, if you
declare the variable again at a lower level. However, this approach can
lead to confusion and is not recommended.
local
x = 10
msg(x) « 10
if x > 5
local
x = "hello" « a new variable that's also called "x"
y = 5
msg(x) « hello ... NOT 10
msg(y) « y's "scope" is this "if" block
msg(x) « 10 ... NOT hello (note the outline level)
Again, it's confusing to have the letter x stand for two different
variables ith different scope, so it's generally not a good idea. Why make
scripting harder than it has to be?
By the way, one of the coolest things about Frontier is that the block
structure is tangible since it's reflected in the outline (i.e. the
indentation).
Of course, the Object Database is central to Frontier. But, be careful.
Some locations are "global" to all of your scripts. If you accidently use
a name that happens to correspond to a global variable, and you didn't
declare yours as a local, you could change the value of something that
really shouldn't be changed.
Let's say you liked to use short variable names. Maybe you needed a flag
to tell you whether or not you created an outline.
cr = false «assume it hasn't been created
Wait! If you run this script, you've just reassigned
system.verbs.constants.cr from "\\r" (carriage return) to false. That's
likely to break lots of scripts, and maybe confuse you when you eventually
discover the error and have long since forgotten about your abbreviated
"create" flag.
Two lessons:
1. declare variables
local «this is fine, it won't overwrite the global
cr = false «assume it hasn't been created
2. use longer variable names, e.g. created or createdFlag
Sometimes you want to use variables from the object database that aren't in
a global location, e.g. FileMaker.id. In the old days (Frontier 1), you
always had to type out the full Object DB path. Fr. 2 (I think) added the
"with" keyword.
The following two are equivalent.
msg (FileMaker.id)
with FileMaker
msg (id)
Here's where it gets tricky. What if you have some globals, some locals,
and some variables in a table referenced by "with"? Which wins? Well, I
lost lots of time by not paying attention to this question.
local
ID
with myApplication
ID = create(s)
doSomething(ID)
dispose(ID)
Do you spot the mistake? I remembered to declare the local, but I declared
it the wrong place, i.e. one level too high. Frontier (apparently) looks
first in locals declared inside the "with" block, that at the tables
referenced by "with", then walks up the hierarchy for other locals, then (I
think) the globals.
So, although I didn't realize it, my local "ID" was left alone, and I
essentially ran:
myApplication.ID = create(s) « OOPS, not what I meant
Since my "glue" scripts depended on myApplication.id being the correct
string4 creator code (not the values passed back by create), the rest of my
script broke.
Here's a sample glue script:
on doSomething(someValue)
with myApplication
return appleEvent (id, 'xxxx', 'yyyy', '----', someValue)
So, Frontier was trying to send the AppleEvent to a sort of random place
since the ID was wrong.
Happy scripting; I hope my comments are helpful.
© Copyright 1996-97 UserLand Software. This page was last built on 5/7/97; 1:43:29 PM.
It was originally posted on 9/2/96; 3:35:29 PM.
Internet service provided by Conxion.