Our first script
Let's start simple: we'll write a script that counts from 1 to 10.
A script in Frontier is an object type, parallel to a wptext or an outline. In fact, a script is a kind of outline, a fact which makes writing (and reading!) Frontier scripts particularly easy.
It also means that I am not going to explain how to select, type, move, and delete lines in a script, because you're supposed to know that already from your experience with outlines (possibly from the previous tutorial).
A typical place to make a new script, until you've decided where else to put it, is in the workspace table; so choose Open: Workspace, then Table: New Script, and call your first script by a name that describes what it will do: "Counter" (without the quotes).
A window will appear that looks something like this (you are free to change the font and size of the whole window in the usual way):
The first line of our script has been written for us:
on Counter ()
and the insertion point is on a line indented beneath this first line.
The word "on" is a keyword, meaning that it is a structural term within the strict vocabulary making up the UserTalk language. The keyword "on" introduces a handler, that is, it introduces the commands that are to be executed when there is a call to the verb whose name we are declaring in the "on" line.
That's a lot of concepts to swallow at one time, so just forget about it for a moment and we'll come back to it!
The important thing is this: we are saying in this first line that there is to be a new handler, called "Counter", and (here comes the really, really important part) this handler will consist of all the UserTalk commands appearing indented beneath the "on" line.
The indented line for the first of these commands has been created for us, and the cursor is sitting on it, waiting for us to type. In effect, Frontier is saying to us: "Okay, you wanted a new script called Counter, so I've made one, and I've put into it a handler called Counter for you; now, what would you like Counter to do?"
Our first command
What we would like Counter to do is count from 1 to 10. But count where? What we really mean by "count" here is display the numbers 1 to 10.
The typical place to display things while a handler is executing is in the About Window, and there is a Frontier verb that performs such a display. This verb is called "msg".
To see it in action, first bring the About Window to the front and into a good position so that we can see it simultaneously with workspace.Counter; now go back to workspace.Counter and type so that the script looks like this:
on Counter ()
msg (1)
and press the Run button. Ooops! We got an error message already! This is because we have said what we want Counter to do, but we have not included a command to actually do Counter. So add a further line, not indented, so that the script looks like this:
on Counter ()
msg (1)
Counter ()
and press the Run button. The number "1" appears in the Main Window. Great! So far our script works perfectly. But exactly what did we just do?
The only command in our Counter handler is "msg (1)". The word "msg" is a verb, that is, a Frontier command. The word "Counter" is a verb too. The only difference is that "msg" was built in to Frontier's vocabulary already, whereas "Counter" is a verb we invented ourselves.
We invented it -- actually, in computer jargon we defined it -- in the "on" line; in effect, "on" tells Frontier that you are creating a new verb. Everything indented under the "on" line is part of the definition of "Counter". So far, that's only one line - the line that says "msg (1)".
Calling a verb
After that comes the line "Counter ()", which, since it is not indented under the "on" line, is completely independent, a new thing. It actually uses the verb "Counter," giving it as a command.
If we had not already explained to Frontier what we meant by "Counter," using "Counter" as a command this way would have generated an error, because, unlike "msg," "Counter" is not built in to Frontier's vocabulary.
(To prove this, in the last line change "Counter" to "Bounter" and push the Run button! Okay, put it back the way it was.)
Now, stop and look for a moment at what the two lines have in common where the two verbs "msg" and "Counter" are used. After each verb there are parentheses. The parentheses after "msg" contain something, the parentheses after "Counter" don't -- we'll come to that in a moment.
But this is the important part: to use a verb in Frontier, to actually give the command which that verb signifies, you must put parentheses after your use of its name. This is known as a call to that verb.
(The verb "msg" tells Frontier to display something in the Main Window. When you make a call to "msg," you must put inside its parentheses what it is you want displayed in the Main Window.)
So let's go back to that confusing sentence, where I said: The keyword "on" introduces a "handler", that is, it introduces the commands that are to be executed when there is a call to the verbwhose name we are declaring as part of the "on" line. Do you see now what this means?
More important, do you see now what our script means? Going line by line, it means: "[1] Okay, Frontier, when I give the command Counter, I mean you should [2] display the number 1 in the Main Window. Have you got that? All right then, [3] Counter!"
Counting from 1 to 10
It is now obvious how to make Frontier display the numbers 1 to 10 successively in the Main Window. We just modify our script to look like this:
on Counter ()
msg (1)
msg (2)
msg (3)
msg (4)
msg (5)
msg (6)
msg (7)
msg (8)
msg (9)
msg (10)
Counter ()
Press the Run button, and presto, Frontier counts from 1 to 10 in the About Window.
Hmm, perhaps it counted rather a little too fast; you could barely see any of the numbers before 10 as they zipped by. What we need is a delay before each new number.
There's a verb that accomplishes this: "clock.waitseconds". In its parentheses you put how many seconds you want Frontier to wait before going on to the next command.
So modify the script to look like this:
on Counter ()
msg (1)
clock.waitseconds (1)
msg (2)
clock.waitseconds (1)
msg (3)
clock.waitseconds (1)
msg (4)
clock.waitseconds (1)
msg (5)
clock.waitseconds (1)
msg (6)
clock.waitseconds (1)
msg (7)
clock.waitseconds (1)
msg (8)
clock.waitseconds (1)
msg (9)
clock.waitseconds (1)
msg (10)
Counter ()
Press the Run button, and watch the Main Window. Ahh, very nice.
Of course, it happens that this script is utterly contrary to the basic principles of computer programming, because we've got a repetition and yet we're dictating every step of the repetition ourselves, whereas the whole idea is to let the computer do the repeating. But we'll fix this in the next chapter.
Meanwhile, let's mop up some points raised by the script we've written. To begin with, you will no doubt have noticed that there's a difference between the verb names "Counter" and "msg" on the one hand, and "clock.waitseconds" on the other.
Verbs are table entries
The verb "clock.waitseconds" has a name that's divided into two parts, demarcated by a period between them -- which should remind you of how we refer to the names of table entries. This actually is how the vast majority of Frontier's built-in verbs are named ("simple" names such as "msg" are relatively few), and it is very convenient, making the verbs easier to name uniquely and to remember.
There are about three dozen pre-defined categories of verb, making up the first part of the name; and then within each category, the verbs are named appropriately to their function.
The resemblance between this way of naming verbs and the way table entries are referred to is not a coincidence; the verbs are in fact table entries. To see this, just take a look at them: they're at system.verbs.builtins, so jump to that table, find the "clock" entry, and confirm that it does indeed contain a "waitseconds" script. This point is important for two reasons.
First, looking in system.verbs.builtins is one way to remind yourself (or to learn) what verbs exist in Frontier. Second, as you create new verbs, which you probably will do constantly, this is how you will refer to them.
Within our script "Counter", when we wish to call the Counter handler, it is sufficient to say "Counter ()", plain and simple, because parts of the script can "see" each other (according to rules about scope, which we'll discuss later on); but if you wanted to call Counter from within a different script, you would have to say "workspace.Counter ()" (assuming that Counter still lives in the workspace table).
To see that this is true, create a new script, workspace.CounterCaller, and make it consist of these lines:
on CounterCaller ()
Counter ()
CounterCaller ()
Press the Run button, and you'll get an error message which makes it clear that, from here, Frontier knows nothing of Counter. Now change the second line of CounterCaller so that it goes:
on CounterCaller ()
workspace.Counter ()
CounterCaller ()
As you can see from watching the About Window, the effect is that Counter is indeed called.
The fact that verbs are table entries should give you a keener appreciation of why it is necessary, when you call a verb, to include the parentheses after the verb's name. Suppose, for example, we had written:
on CounterCaller ()
workspace.Counter
CounterCaller ()
Try it. When you push the Run button, nothing happens; but you don't get an error either. That's because you've said something perfectly real and legal -- but you're not referring to what you think you are, in the way that you want to.
What you've referred to is the table entry workspace.Counter; in other words, you've mentioned the script object (you've spoken of it), but you haven't called the script (you haven't given that script as a command). This is a frequent source of mistakes and confusion among beginners.
Parameters
We should also talk about the difference between Counter, on the one hand, and msg and clock.waitseconds, on the other. I refer here to what goes into the parentheses after the verb's name when you call it.
Every verb, as part of its definition, provides a rule about how many things must go in those parentheses. The things in the parentheses are called parameters, and the definition establishes, in the "on" line, how many of them there must be.
In the "on" line where we defined Counter, we didn't put any parameters in the parentheses, and this tells Frontier that there are to be no parameters in the parentheses when Counter is called. (To see that this is true, change the last line of workspace.Counter to "Counter (10)" and see what happens when you press the Run button.)
On the other hand, if you look at the definition of clock.waitseconds at system.verbs.builtins.clock.waitseconds, you will see that it has one parameter, one item in the parentheses. This tells you that whenever you call clock.waitseconds you must supply exactly one parameter.
To know what this parameter signifies, however -- that it is the number of seconds Frontier is to pause before proceeding -- you have to consult the documentation for clock.waitseconds; we'll talk about the documentation in a later chapter.
|