General:. Blocks are the
code (program) elements of Glee. Blocks are objects
containing several members. Among these members is the Glee
code. Glee blocks are delimited by curly braces (
{...} ). Everything between these braces is taken as
Glee code. It is to be interpreted and executed. Another
member of a Glee block is its local namespace. A block's
local namespaces is persistent. This means that when you create and name
objects in the block's namespace, they are there the next time you return to
the block. This is different than most other languages whose
"programs" are stateless. When a block contains a block, a namespace
chain is formed. The inner-most block is said to be at scope 0. It's
caller is at scope 1. This proceeds all the way to the root block
which is the block where all Glee programs start. Named objects
in this chain are visible all the way to the root unless shadowed. Shadowing
means two objects in the chain have the same name. When Glee
looks for named objects, it works up the chain and quits as soon as it finds
one. If the scope is less than 2, the block may write to the object. Scopes
greater than 1 are read only. If you write an object (at scope greater than 1)
with the same name, the higher scoped object is unaffected but becomes
shadowed. The concepts of Glee blocks are very rich. I'll start
out very simple and slowly expand on the issues. In this tutorial you'll get a
view of blocks from 20,000 feet. We'll see them do their thing and only with
subsequent block tutorials will we come to know how they do it. But as with
other elements of Glee, you really need to know the "how it
works" to make good use of the facilities.
Hello World:This is
Glee's Hello World program. It requires very little setup.
Slap a left brace before the code; a right brace after the code; and it is
ready to execute. Just press go.
Monadic Form of Naming a
Block: Blocks with no names serve to localize namespaces. But blocks with
names also allow the block to be used again and again without redefining it. In
Glee, as in other languages, this is the concept of a stored
program. Our purpose is to associate the block code with a name and then invoke
that code through the associated name. In the monadic form of #PGM,
the left argument is a string representing the block's code definition. If this
is assigned to a variable as shown here to HW, the block is implicitly
given the variable's name. This is a common way of defining Glee
programs from external media like files. When blocks are defined and named
within an executing program, the dyadic form of #PGM is more commonly
used. This is illustrated in the next example. As the examples in this tutorial
are stand-alone, we will incorporate the dyadic form of block naming from here
on.
Hello World program named HW:
For a block to be invoked more than once, it must be put into a namespace. Here
I save the block under the name "HW" using
Glee's #pgm operator. This is called a named
block and is the familiar program object. The #pgm operator
assumes the program name on its left and a block on its right. It creates a
block object and stores it in the local namespace under the given name. You may
be tempted to use the assignment operator by doing something like
({'Hello'}=>p). But when you think about it you realize that
Glee invokes the block and returns its result. This is then saved
in the variable.
The HW program being
called: We can use a program in a Glee expression. When you
invoke a program (or call it), it examines its left argument to be a namespace.
It then uses this namespace to define objects in its local namespace. The block
is executed and passes its result on to the right. If the left argument is not
a namespace, the program is executed and the result is stranded with the
left argument. Programs don't take right arguments so anything on the right is
stranded as well. I use verbose display to illustrate what is really happening.
The HW program with
argument: Here I use the field list concepts to deliver an argument to my
program. I begin by defining and displaying the program <1>. I can
display the members of the named block through it's :ns namespace
member <2>. No members exist because the program has not yet been
invoked. I call the program supplying its argument [.arg] and
verbosely display the stranded result <3>. Now I again display the
program namespace showing that the argument arg now exists <4>.
Next, I call the program without supplying an argument (no [.arg]) .
The verbose display reveals the left argument stranded to the program result
(which now uses its persisted arg value) <5>. Correcting the
mistake and supplying the left argument namespace correctly solves the problem
<6>. Programs copy the contents of the left argument namespace into their
namespace when called. So if you supply arguments that are not used by the
program, they still get copied into the program's namespace (see
xyz)<7> and <8>.I
HW using global
variable: I begin this example by clearing the root namespace. My version
of HW clears its local namespace this time too (#ns =>
@;). When the block is invoked this is what happens: 1) The argument
namespace is copied into the HW block's local namespace. 2) The block
is invoked and the first instruction clears the local namespace. 3) The
interpreter tries to find the "arg" variable by first
looking in its local namespace. Failing there, it looks up the namespace chain
in the call chain. Getting all the way to the root namespace (just one step
this time), it still doesn't find it and reports NoValue. Then I
create the "arg" variable in the root namespace and do it
again. This time, the HW program block still doesn't find
"arg" in its local namespace but it does find it in a
namespace up the chain ... in the root namespace. Clearing the blocks namespace
also clears its arguments. The next example resolves the issue.
HW clearing namespace and
preserving arguments: The previous example cleared the namespace and lost
the arguments. The #args niladic operator resupplies them as show in
this example. Since blocks are persistent and may leave objects around during
execution, it is probably a good idea clear the namespace before starting. It
is also a good idea to clear it when ending, especially if the program is
dealing with large objects like pictures. The next example shows how this can
be done.
HW cleaning up namespace at
exit: Because the named blocks have state, every object introduced into the
block is remembered. It will be there when you return to the block. Sometimes
you just want to clean up and return a result. A method (admittedly not
elegant) is illustrated here. The last statement arg
' hello world '(#ns => @)->_1}. The output is stranded as
before with one more object. The (#ns =>
@)} clears the namespace and strands in an empty namespace object.
The ->_1} drops the last element of
the sequence before returning. And <4> demonstrates that the blocks
namespace is in fact clear as a result.