General:.When Glee
begins operation, you are already in a block. This is the root
block. There you can create and invoke other blocks. Those blocks can create
and invoke others. In this context, all the blocks above a block are visible
and callable. All variables in those blocks are also visible but they are not
assignable. To assign to a variable above a block (i.e. in a caller), the block
must be passed a reference to the variable. Glee finds
objects given their names. It begins in the most local namespace and continues
up the chain until it finds an object with the name it is looking for. If that
object is a block, it invokes it. If it is a variable, it uses it in an
expression or displays it. If that object is a reference, it can be used as any
other variable. Also, when you assign to it, you are assigning to all
referents. This section will deal with blocks inside of blocks and some of the
object location issues. It will also introduce variable "stickiness",
arguments and initilization. All arguments are name oriented rather than
positional. There is no caller / callee contract (i.e. signature) in
Glee.
Global and argument values:
This is the familiar compound interest program. I begin by clearing the
namespace and assigning global variables:
(#ns=>@;.05=>i;8=>n;). Next I define the compound interest
block and name it: ('cmpd'#pgm{'i='i';n='n';f='(1+i^n)';'};). I
display the block using :stringv: (Pgm[R2C1:P] Code: {'i='i';
n='n'; f='(1+i^n)';'}). And then I invoke the program with no arguments:
(cmpd $;). The result shows that the program sees the global values
for "i" and "n". Next I run the program
with just the "i" value given as an argument:
(.1[.i]cmpd $;) and the result shows that it is now seeing the
argument "i" but the global "n"
(i=0.1;n=8;f=2.14359;). I run it again argument free. I get the same
result as before. This is because the block's namespace retained the value
passed by the argument in the previous run. I change the global
"n" and run it niladically to show it still sees the global
value for "n" but the local value for
"i". I run it again with a different argument
"i" (i=0.1; n=4; f=1.4641;). This illustrates the
visibility of variables outside the block. It illustrates the passing of
arguments into the block. It illustrates the "stickiness" of
variables within the block. And in general it illustrates poor style. As a
rule, you don't want your programs to find their variables outside the block.
You want your programs to create their own variables as functions of variables
passed as arguments.
Default values and reloading
arguments (using #ARGS): One way to give a program default
behavior is to initialize variables and then overwrite them with data passed as
arguments. This example illustrates the technique. First I assign the variables
(10=>n;.07=>i; ). But this isn't what the program does first.
The program first loads the arguments into its namespace. Assignment of these
same variables then replaces the argument values. If no arguments are given
this presents no problem. But if you give arguments, they immediately get
clobbered. The #args operator reloads them. You can do this anywhere
and any time in your programs. This technique removes the
"stickiness" characteristics of the arguments. If arguments are not
given, the defaults always take their place.
Initialization
(#IX) and execution: For those cases where we want initialization
and then we want the program to have an adaptive state, the #IX
operator is used. #IX is followed by a block definition { ...
}. #IX just runs the code in the block at scope one higher than
itself. Typically, this will be scope 0 of the program. It then sets a switch
so every time it is incurred in the program after that, it is ignored. The
effect is to have a way of creating helping functions and doing other
initializations when the program is created.
Initialization
(#IX) and immediate return: For those cases where you want
initialization only, add the return (: return or GLEEphicly
:<- ) to exit the program after initialization.
Interest formulas: This
is the familiar suite of interest formulas: SPCA: Single Payment Compound
Amount; SPPW: Single Payment Present Worth; USCA: Uniform Series Compound
Amount; SFP: Sinking Fund Payment; CR: Capital Recovery; and USPW: Uniform
Series Present Worth. I implement them all off of the base SPCA block. I give
this block an initial expression that all the others use. In some cases I must
reference the interest rate "i" for independent use. To do
this I use the "spca:ns" namespace reference with the field
".i" (spca:ns.i). The Glee
interpreter uses long right scope to resolve this compound reference. It does
not invoke "spca" here. Rather, it is simply addressing it
as an object with state. I initialize the spca namespace by running
once which just invokes the #ix block and exits. You may also notice I
used "spca" as if it were just another variable. In these
cases, it is being invoked. It is its result that I am using. The
Glee interpreter is a pretty sophisticated collection of state
machines. It recognizes at run-time that the "spca" left
argument is not a namespace and so does not use or compromise that argument. It
just executes using data in its own namespace or global objects it finds in the
chain. This makes the coding very natural and free of unnecessary
parenthesizing.
Local Programs and Loading
Arguments: This example is just a little different twist of the preceeding
example. Instead of having several programs in the global namespace, I have a
single program named "AllInt". This program has two local
programs: "fmt": ('fmt' #pgm{ 'i='i'; n='n';
'fn'='r'.'$};) does the formatting. "ci": ('ci'
#pgm{1+i^n};) is the compound interest calculation used by the expressions
for the various interest calculations. Here again I use the
"#args" operator. In the first example, I used it in the
initial expression. Here, I use it in the main AllInt program. Any
time this special operator is invoked, it reloads the arguments into the local
namespace. In this example, I run it after I set the defaults (#ns=>@;
.08=>i; 10=>n; 'ci'=>fn;). The effect is the same as the use by
the initial expression and the strategy is the same. This technique is a little
cleaner and it obviates an effort of the caller to set the initial expression.
If the caller sets the initial expression, it does get run. However, his
efforts are trumped with the clearing of the local namespace
(#ns=>@;) followed by code to set the default values. Try running
this with the comments checkbox selected. It will highlight the operations.
Notice when I called with arguments I made sure the "n"
value was a sequence using the enclose (>) operator
(9..11>). If I hadn't done this, it would have taken the
"9" for "n", the "10"
for "i" and would have ignored the remaining data.