[Nickle] Nickle storage lifetime modifiers

Barton C Massey bart at cs.pdx.edu
Sun Nov 7 00:44:13 PST 2004


One valid way to think of global, static, and auto lifetimes
is this:
  + Storage with auto lifetime is allocated every time
    an auto definition is encountered.
  + Storage with global lifetime is allocated
    exactly once, when the function defining it is first
    encountered.
  + Storage with static lifetime is the tricky case.
    Static storage is allocated whenever the function
    containing it is defined, and reallocated whenever
    the definition is re-evaluated.
Note that because C doesn't have nested functions and
closures, global and static would both behave the same
there.  This is why C has only the static keyword :-).

We say that Nickle has separate notions of the scope of a
name and the lifetime of storage: a name may go out of scope
without its storage being deallocated, and may come into
scope and be bound to previously allocated storage.

Consider these functions, similar to yours:

  int() cntfac()
       /* counter factory */
  {
      return (int func() {
	  return (static int x = 0)++;
      });
  }

  int() cntfac2()
       /* counter factory */
  {
      int bar() {
	  return (static int x = 0)++;
      }
      return bar;
  }

These functions behave identically, as you would hope.  The
inner function definitions in both are evaluated every time
the outer function is invoked.  Thus, new storage is
allocated for x at that point, and returned.  Now consider
what happens if we say:

  int() cntfac3()
       /* shared counter factory */
  {
      static int bar() {
	  return (static int x = 0)++;
      }
      return bar;
  }

Because bar() itself is static, its definition is only
evaluated once, at the time the definition of cntfac3() is
evaluated.  Thus the definition of x is only evaluated
once, and all the "shared counters" returned by cntfac3() work
off the same storage for x.

Finally, consider more or less what you wrote:

  int cnt()
      /* counter */
  {
      int bar() {
	  return (static int x = 0)++;
      }
      return bar();
  }

Note the difference in return type: cnt() invokes bar() to
get an int result.  Since bar is auto, new storage will
be allocated for x on each call to cnt(), which is the
behavior you observed.

Changing the lifetime of x from static to global does, as
you observed, make x act like a counter: subsequent
re-evaluations of the definition of bar don't allocate new
storage for x.

  int cnt2()
      /* shared counter */
  {
      int bar() {
          return (global int x = 0)++;
      }
      return bar();
  }


(If you reload or re-type-in the definition of cnt2(), it
will start from 0 again.  This is because you effectively
define a "new" cnt2() the second time, and it has its
own new "bar()".  In a statically scoped world, names don't
uniquely identify storage.)

Finally, if you modify cnt() by making the definition of bar
static, you'll also get a useful counter.

  int cnt3()
       /* shared counter */
  {
      static int bar() {
	  return (static int x = 0)++;
      }
      return bar();
  }

By now, hopefully, the reasoning is obvious.

Somebody should probably put some of the above into
the tutorial.  Please let me know if you have further
questions.

	Bart

In message <Pine.GSO.4.58.0411062157370.20308 at adara.cs.pdx.edu> you wrote:
> 	I am reading through the tutorial on the nickle.org site I was
> wondering if you could explaining the difference between the two following
> code samples reproduced below:
> 1)
> int() function incrementer () {
> 	return (func () {
> 		static int	x = 0;
> 		return ++x;
> 	});
> }
> int()	a = incrementer();
> int()	b = incrementer();
> 
> 2)
> function foo () {
> 	function bar ()	{
> 		global	g = 1;
> 		static	s = 1;
> 		g++;
> 		s++;
> 		return (int[2]) { g, s };
> 	}
> 	return bar ();
> }
> 
> 	So in 1) when a or b is called each has its own independent x, but
> when using foo() only g gets incremented and s is always 2.  I am not sure
> how to understand the difference?



More information about the Nickle mailing list