scScript Intro 3
Hello2.sc is a functionally more complex example, written to illustrate
nested functions and closures in scScript. The main body, bottom four
lines of source code, makes two calls to MakeGreeter, a local function
that itself creates (and returns) a Greeter function. The resulting
functions are stashed as part of global variable (FGreeter and SGreeter)
declarations, and subsequently called with no args.
The script starts with some constant declarations. Def is
used just like Var, but is only a compiler directive to
define a name (preferably with a # prefix) for a
number or string literal. The scope of Def is determined
by where it is declared, global in this case and accessible by all
subsequent lines.
MakeGreeter is a local function that first defines arrays
of strings, in 4 languages, then chooses one for its DoWArr
(Day-of-week-array) using a Switch. So far, the operation
is trivial.
But MakeGreeter also declares a
nested GreetFunc that uses DoWArr from its
(superior) container function. When called, MakeGreeter
returns its nested GreetFunc after choosing a language. It
returns the function itself, as defined, but does not call
it. This is just like C returning a function ptr, but there is more to
it!
Storing or returning such a local function actually creates
a closure, the combination of a function and all inherited
local variables it needs to operate. Each called instance of
MakeGreeter sets its own value for DoWArr,
determined by the Language in param (#French
or #Swedish in this example). But unlike persistent global
variables, DoWArr will be gone
when MakeGreeter returns. So the GreetFunc inside
MakeGreeter is forced to close over this inherited
variable; otherwise, it will be hopelessly lost
after MakeGreeter returns and terminates.
The closed-over value of DoWArr lives on and
persists in the closure for the particular GreetFunc
instance! (The closure has a pointer to the storage location of
each variable it closes over.)
Each call to MakeGreeter creates a new run for it (scScript uses a
RunRecord instead of a stack frame) and a whole new set of local
variables. Similarly, the GreetFunc that it returns will
close over a correspondingly different local DoWArr. Since
the two closures, for #Swedish and #French are
stored in two globals, it is an easy matter to compare them with a
system call:
Show.Vars(Show.#Max)(SGreeter, FGreeter);
This will generate a detailed listing of the two:
Each of the two global vars has a 16 byte (VEnt) storage location in memory that contains a different closure. The closures (Clos) are both 32 bytes, but it would be quite common for one to be slightly bigger as memory is constantly re-used and sometimes a slightly larger block is recycled without enough excess to warrant carving off another.
Each closures (in this case) has its own single Closed Var, but
both reference the very same Code block. In fact,
the (Ref:3) notation for the Code:2 indicates a
third pointer, and that comes from MakeGreeter itself
which also maintains a pointer to its own Code block.
Note: different images in this document have been generated from different development versions of scScript, there will be some inconsequential discrepancies. For examples, internal system functions may have different numbers, bytecode variations or their args may be different, and display formats may evolve with advancing versions. (The latest version also shows flags for vars that have them!)