Menu

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.

Hello2 showing nested functions and closures.

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:

Output of Hello2, showing the two closures.

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!)