[Nickle]A tale of two unions

Keith Packard keithp@keithp.com
Mon, 12 Mar 2001 15:32:21 -0800


Nickle currently has two levels of type checking, static typing and pure 
polymorphism.  In other words, you either get strict compile-time 
typechecking or run-time type checking.  No middle ground for those 
programmers who waffle between the extremes.

I've implemented two different union types to try and reach a compromise.

First, take hints from existing practice.

The first attempt was to take a combination of pascal varient records and 
tagged unions from other languages.  The goal here is to allow for union 
types with compile-time typechecking within explicitly run-time 
typechecked blocks.

	typedef union {
		integer	i;
		string	s;
	} string_or_integer;

	string_or_integer	variable;

	variable.i = 10;
	variable.s = "hello world";

	union switch (variable) {
	case i:
		printf ("Integer i %g\n", variable.i);
		break;
	case s:
		printf ("String s %g\n", variable.s);
		break;
	}

While it would be nice within the union switch to refer to the variable 
without the tag, there's no effective way of restricting modifications 
to the variable that would switch the tags.  Hence, this version requires 
run-time type checking at each access.  Clipping the wings of this could
do something like:

	union switch (variable) {
	case i:
		integer temp = variable.i;
		printf ("Integer i %g\n", temp);
		break;
	case s:
		string temp = variable.s;
		printf ("String s %g\n", temp);
		break;
	}

Of course, we could hack this to make 'variable' contain said temporary 
value during the varients, but this would make 'variable' unavailable as 
an lvalue that referenced the original union.

Back to the drawing board.

It seems to me that the goal of allowing compile-time typechecking 
conflicts with the existing 'poly' type within nickle; the benefit of that 
type is that it provides easy-to-use syntax along with full run-time 
typechecking.  The second kind of unions I implemented brings a modicum of 
compile-time typechecking to a pure polymorphic world.  The goal here is 
to allow useful compile-time typechecking along with run-time typechecking 
to verify program type-correctness.

	typedef union {
		integer;
		string;
	} string_or_integer;

	string_or_integer	variable;

	variable = 10;
	variable = "hello world";

	typeswitch (variable) {
	case integer:
		printf ("integer %g\n", variable);
		break;
	case string:
		printf ("string %s\n", variable);
		break;
	}

Note that this version requires no tags; that's because the interest is in 
capturing the essence of pure polymorphism while still providing some 
typechecking; pure polymorphism allows untagged access to the value stored 
in each location with the type following the value, not the union variable.
There's no problem within the 'typeswitch' statement with capturing the 
value of the variable as all accesses must be run-time typechecked in any 
case.

It seems to me that this second version captures the usage of pure 
polymorphism within nickle while still allowing for useful typechecking to 
occur at compile time.  Without unduly restricting the 'union switch' 
semantics, the first version doesn't offer any significant compile-time or 
run-time advantages while requiring additional work and book-keeping to be 
done by the programmer.

-keith