The document attached below is written in 1996. Some comments on Java may be out of date.


Transframe, Java & C++: A Critical Comparison

David L. Shang



Index



PREFACE


Indeed, a language should shape the concept of our ideas without a distortion of the body of our thought. Language, the dress of thought, should not hamper our efficient thinking. Fitting a "circular" concept in a "square" not only requires the square dress larger and heavier, but also, the worse, twists out of the true meaning of the concept. A programming language should provide frameworks to fit our application software ideas, rather than warp applications to fit the language framework. In his Turing Award Lecture Paper, John Backu's critic on rigid frameworks that need to be big in order to suit users still holds true for many current languages.

The fast expanding software business has introduced many new dimensions of applications, which sets up more and more requirements on specific language features to support things like object-oriented programming, parallel computations, persistent objects, internet authoring, and event-based simulation. Obviously, we cannot put all the good stuff into a language, and making the language a super hodge-podge. Trade-offs must be made among lots of conflicts such as simplicity versus functionality, safety versus flexibility, and efficiency versus dynamicity.

The choice of a programming language feature is then difficult. C++ added features to C for object-oriented programming, while keeping the language as close to C as possible. Since its original invention, the language has been changed significantly. New features and facilities have been added to the language to patch the weak spots inherited from C, which, at the same time, has overly complicated the language. There are still weak spots remain uncovered. C++ results in a heavy dress with patches on patches.

Java, another language based on C and similar to C++, tailored C in a different way. Java is designed to be a safe language; so that many unsafe C features have been deleted. Meanwhile, a number of new features from other languages are included. Tailoring, rather than patching, has rid Java of the burden of the C-compatibility and made the language safer and simpler than C++. Does Java's tailoring work result in a perfect dress to suit every special need in the thought of software developers? Applications and programmers have their favors. A good feature cannot favor all application developers. The feature considered good in one application may be considered bad or harmful in another application. A typical example is garbage collection.

Transframe, yet another language based on C and similar to C++, is designed in a different philosophy. Instead of doing simple tailoring work, Transframe unifies hybrid C and C++ concepts and provides a transformable abstraction vehicle that are flexible enough for users to tailor their own domain-specific "dresses". Transframe's unification enables a flexible framework for various applications. It provides a customizable dress that shapes software concepts faithfully. By unification, the fixed and built-in part (the hard part) of the language framework becomes smaller while the user-definable part (the soft part) becomes larger so that the framework are flexible enough to build various domain-specific application models.

This document is a honest comparison among the three programming languages: Transframe, Java, and C++. It is not an attempt to explore the deficiencies in one language or another. Rather, the focus is on the difference in various language features, as well as their contribution to the entire software production process. All discussions are presented through solid examples.

I assume that readers have an extensive knowledge and experience with C++. The definition or explanation for a C++ terminology shall be omitted. When a non-C++ terminology is given, either an equivalent C++ terminology shall be presented in parentheses or an explanation shall follow.


Chapter 1. Overview


The C++ programming language is a follower of Simula as an object-oriented language based on C. The language design is an attempt at a better C. It extends C with various facilities for object-orientedness. The language has become very popular during the last decade.

The Java programming language is a follower of C++ but is neither a subset nor a superset of C++. The language becomes very hot because of a number of attractive characteristics in the language environment and the boost of the internet programming.

The Transframe language is also related to C and C++ but is designed rather differently than Java. Transframe is designed to be a general purpose as well as domain-specific language. The language includes many innovative approaches to solve problematic or conflicting requirements.

1.1 C-Based

All the three languages are derived from C, with the hope that the majority users in the current software practice can use them without extensive training. C++ is designed to be compatible to C. "this is not counted as a mortal sin here," as Markku Sakkinen in his C++ critique article[MS92] said, however, "taking the weakly typed and weakly structured language C as a base has become an irremediable handicap." The basic defects of C++ are mostly inherited from C. Pointer arithmetic for arrays is an example. It is impossible to implement array bounds checking with a full compatibility to the C array. The language becomes very complicated with the effort to keep the hybrid and redundant features of C. For example, the struct type constructor in C is a redundant feature when the class concept is introduced, and the worse, C++ sets up different accessibility rule for structures and classes.

Java removes the unnecessary complexities of C++ by giving up the C-compatibility goal. It retains many of the object-oriented features and makes itself look and feel like C++. Meantime, Java excluded a number of features of C++ and included a few features from other languages. It is a result of addition and substraction. Most C++ features omitted in Java are deficiencies in safety. Examples are explicit deallocation and pointer arithmetic.

Transframe is also a superficially C/C++ alike language, but is designed rather differently. It is not a result from addition and substraction, rather, it is a work of unification and simplification based on C and C++. A generalized Transframe concept covers a number of C/C++ concepts and the beyond. The language is simplified by reducing the number of hybrid concepts and by eliminating ad hoc features. Unlike Java, Transframe eliminates the unsafe defects in C by upgrading them into a safe unified concept, rather than simply excluding them. Examples are pointers and variable number of function inputs. Transframe enables useful concepts in other language by integrating them into the existing unified concept, rather than simply adding them into the language to get yet another hybrid language.

1.2 Object-Oriented

C++ is designed to be not purely object-oriented. For example, function calls are not necessarily through objects. Java is designed to be purely object-oriented: no function shall be available otherwise than as a method defined within a class. Transframe is designed to be purely object-oriented too, but with the unified class concept, free function is again enabled, just as a free class.

Consider the HelloWorld example in the three languages:

C++:

   void main (int arg_num, char** args)
   {
	println ("Hello World, this is my message:");
	for (int i=0; i < arg_num; i++) println (args[i]);
   };

Java:

   class HellowWorld
   {
	static public void main (String args[])
	{
		System.out.println ("Hello World, this is my message:");
		for (int i=0; i < args.length; i++) System.out.println (args[i]);
	};
   };

Transframe:

   function HellowWorld (args: char[]...)
   {
	println ("Hello World, this is my message:");
	foreach (str in args) println (str);
   };

The C++ example inherits the basic style of C program which must start with a function named main. The main function is not an object-oriented feature in language concept. Calling the function shall create a process within the system to perform the job described in the function body. The conflict is: the process created is not an object in language but may be an object in the operating system.

The Java code unnecessarily complicates this straightforward example by a redundant nested structure. In their first look at Java, people would get confused by the keyword static. Why should the main method be static? How should I call main without an object of HelloWorld being created?

The Transframe code is as simple as C++, but uses an object-oriented feature in language concept. In Transframe, the concept of function has been unified into the concept of class. HellowWorld is a class. Its superclass is function. The function body is its constructor. Calling HelloWorld creates an activity object in the system to perform the job described in the constructor. The following code is also valid in Transframe:

   class HellowWorld is function
   {
	public:		enter (args: char[]...) // constructor
			{
				println ("Hello World, this is my message:");
				foreach (string in args) println (string);
			}
   };

1.3 General Purpose

All the three languages are designed to be general purpose.

C++ in its current state is a rather large and complex language. Additions such as templates make the language even more so. Java, though eliminated many complicated features (including templates) in C++, has added a couple of more features on order to address a wider application domain. Examples are features for concurrent programming and garbage collections.

Language design is always affected by compromise and trade-offs to some extent. But a language is not just a syntactic combination of all the concepts in isolation. Keeping adding built-in features can only result a hybrid language that may favor one application domain but may disfavor the other. For instance, Java's garbage collection forced on object variables may depreciate itself in real-time/embedded market where a specific memory management is required.

Being more general purpose does not mean having more features. A basic promise of programming is to enable the efficient development of sophisticated software by the adoption of the least features possible. This promise is also true to programming language design: the smallest number of concepts with the widest coverage. Excessive complexity is more likely to preclude clear and consistent solutions.

Being more general purpose does not mean having less specificity. Whether a language covers an application domain is determined by the extent to which a concept supported by the language can easily be applied to the given problem. If one must take up a hacking method, or the solution is error-prone or ill-structured, it can hardly say that the language is suitable to this application domain.

Instead of providing more and more language features, Transframe provides a fewer concepts. Each concept is a nature extension to the familiar one, and can be adapted into various high-level computational models for different application domains. The following diagram shows the basic language concepts in C++ and Java and their counterparts (not equivalents) in Transframe.

C++ Java Transframe
class class class (free)
instance instance instance
function class (free)
member function method class (member)
nested class class (member)
template class (parameterized)
primitive type primitive type class (primitive)
value value instance (of a primitive class)
array type array type class (array)
variable name instance (of a referential class)

Classes and their instances are the basic language concepts of Transframe. As discussed in the following chapters, Transframe's concept covers a wider range than a similar C++ or Java's concept. For example, Transframe parameterized classes are real classes and support dynamic parametrism; while C++ templates are not real classes and support static expansion only.

1.4 Simple

The designers of C++, Java, and Transframe all claimed that simplicity is an important design criterion. C++ has been approved not a simple language. This is especially true for programmers who have no experience with C.

How much simplicity does Java gain by removing features from C and C++? To a certain extent, the C++ complexity is reduced. The most important contribution are the removal of multiple inheritance and pointer arithmetic. But is it true that the Java language is so simple that its learning curve is cut down significantly than C++'?

The illusion of a short learning curve is based on a superficial level. Yes, you can pick up C++, Java, or Transframe rather quickly, even in a couple of days. But a thorough understanding is not a matter within a week or two.

The most important functionality of a language is communication. Therefore, to make the program written in a language understandable is more important than to make the language simple. Software engineers spend much more time in learning various software packages than the time in learning the language used to write these packages.

Simple languages do not necessarily produce understandable programs, however, an excessive complex language is less likely to provide understandable codes.

The only means to write understandable software packages is to write them as if they were presented in a native tongue. Without bringing a domain-specific interface and architecture to a software package, a language can hardly be said simple to use because the software packages presented in the language is not easy to understand.

Consider a 3D modeling package. Transframe's unlimited nested architecture enables the following 3D modeling application:

   class My_3DScene is World3D
   {	with Camera
	{	style = Perspective;
		position = (-1.0, 2.00, 8.0);
		orientation = (-0.2, -0.9, -1.5, 1.1);
		focalDistance = 12.5;
	};
	with Background
	{	style = Picturesque;
		scenery = "bluesky.jpg"
	};
	object Light is DirectionalLight { direction = (0.0,-0.2,-1.0); };
	object RollingFrame is Frame3D
	{	object SphereFrame is Sphere
		{	position = (0.0,0.0,0.0);
			radius = 8.0;
			draw_attr = DrawWire;
		};
		object BlueCylinder is Cylinder
		{	with Material
			{	style = PerPart;
				diffuseColor = (0,0,1);
				specularColor = (0.5,0.5,1);
				shiness = 0.5;
				transparency = 0.6;
			}	
			position = (1.0,0.0,0.0);
			radius = 1.0;
			hight = 3.0;
		};
	};
   };

The above Transframe code is obviously more understandable to read and easier to write than the following procedural style (in Java):

   class My_3DScene extends World3D
   {
	DirectionalLight Light;
	Frame3D RollingFrame;
	Sphere SphereFrame;
	Cylinder BlueCylinder;

	My_3DScene()
	{
		Camera.SetStyle(Perspective);
		Camera.SetPosition(-1.0, 2.00, 8.0);
		Camera.SetOrientation(-0.2, -0.9, -1.5, 1.1);
		Camera.SetFocalDistance(12.5);
		Background.SetStyle(Picturesque);
		Background.SetScenery("bluesky.jpg");
		Light = new DirectionalLight (this);
		Light.SetDirection(0.0,-0.2,-1.0);
		RollingFrame = new Frame3D (this);
		SphereFrame = new Sphere (RollingFrame);
		SphereFrame.SetPosition(0.0,0.0,0.0);
		SphereFrame.SetRadius(8.0);
		SphereFrame.SetDrawAttr(DrawWire);
		BlueCylinder = new Cylinder (RollingFrame);
		BlueCylinder.Material.SetStyle(PerPart);
		BlueCylinder.Material.SetDiffuseColor(0,0,1);
		BlueCylinder.Material.SetSpecularColor(0.5,0.5,1);
		BlueCylinder.Material.SetShiness(0.5);
		BlueCylinder.Material.SetTransparency(0.6);
		BlueCylinder.SetPosition(1.0,0.0,0.0);
		BlueCylinder.SetRadius(1.0);
		BlueCylinder.SetHight(3.00);
	};
   };

Transframe's capability to build up various domain-specific frameworks significantly simplifies the application developer's work. Unlike existing application frameworks wrapped in an unfitting box, for example, Taligent framework in C++ templates, Transframe application frameworks are presented in their fitting architecture. Application developers will always use their own native model to describe what they are going to develop as if they were using their own domain specific language. Though the Transframe language design provides more simplifications than the Java language design, Transframe's utmost goal is not a simplified language for the compiler/interpreter composition, but a language easier to use and easier to understand.

1.5 Safe

All the three languages are claimed to be safe. However, there is no absolute safe. No matter how safe a language is, it is impossible to guarantee the program written in the language free from crash.

Safety is a relative concept. A language is safer than another if the language can help to catch more error in the code before the code is released. Error detection is not a proper criterion for safety. To avoid errors is better than to open a door for errors and then detect them. Many errors are fatal. Once they happen, the system can do nothing but crash.

The proper criterion for safety should be error prevention. The more possible errors can be caught before they happen, the safer the language is.

Theoretically, if a language is absolutely safe, the code written in this language should never report an error at its final release. This is almost impossible. However, a language can be and should be type-safe, i.e., the code written in this language should never generate type errors at its final release.

The word release, rather than the word run-time, is used because of the following two reasons:

C++ is safer than C. But it still inherits many unsafe features from C. They are pointer arithmetic, unchecked array boundary, variable number of function inputs, union type, and unchecked type cast. The added template feature, though safe at run-time, but not safe at the time of release. Since templates are released in its source code and type parameters are not checked until the template is used.

Java has an improved safety by removing many unsafe features including templates, pointers, variable number of function inputs, union type, and unchecked type cast. Java redesigned array types in replace C++'s unsafe array, and added garbage collection for dynamic memory management in replace of C++ pointers. The simple elimination and replacement have brought a number of inconvenient aftereffects. The enforced GC disables the language users to use other memory management protocols; Java's array makes mass data processing inefficient. Union type is practically useful but is not available in Java.

Java still has some unsafe features. Java's array reports type errors at run-time. Without a support of parameterizations, many interface cannot describe the type dependencies among inputs; hence the code relies more on run-time type casting and possibly produces more run-time type exceptions.

Transframe has a further improved safety. The language is designed to be type-safe. No run-time type exceptions shall be reported after the code has successfully been compiled. Transframe improves the safety by upgrading the unsafe features into a unified safe concept. Pointers are integrated into the object reference concept which also includes smart references as well as other user-defined references. Variable number of function inputs are treated as a heterogeneous object stream where type information are preserved. Union types are upgraded to a concept of superclass construction. Array is presented by an abstract parameterized class which is safe and efficient. Transframe's unique parameterization supports a more precise interface specification that diminishes many run-time type checks which would otherwise be required in C++ and Java.

1.6 Efficient

C++ is designed to be efficient in a principle that you never get a performance penalty from the things you do not use. Object-oriented features are carefully selected to exclude some features that would require heavy overhead. The code written in C++ may achieve a comparable size and space efficiency to that written in C. Java is not designed for compute-intensive applications that are critical in space and speed, as it is stated in The Java White Paper:

Java included some SmallTalk-alike mechanism as language built-in features for method and class search. It lacks the support for high speed computation for mass data structure. The so-claimed Java's high performance is limited to interactive applications and something similar.

Another fact is that each Java program needs the support of the built-in java.lang package, no matter how small and simple your application is. Concurrency is built in every object. You'll get the space penalty even you do not need garbage collection and concurrency. Many small embedded systems would not be able to use Java.

Transframe shares the same C++ efficiency principle. It follows the design philosophy of making the fixed and built-in part of the language framework as smaller as possible. The garbage collection package will not be linked into applications if dynamic memory allocation is not required. Concurrency is not built-in and can be developed by the language user via Transframe's domain-specific support. Like C++, the object-oriented features in Transframe are carefully selected to exclude those requiring heavy overhead. However, due to Transframe's powerful generic framework, flexible dynamic programming features can be developed and supported in the environment. Applications requiring extensive dynamic programming features such as dynamic class design, class hierarchical re-configuration at run-time, and multiple dispatch are supported by the language programming environment for rapid development.

1.7 Portable

There are quite a few problems for a C++ program to be portable. The most problems are caused by the lack of standards. The size of the primitive types are implementation dependent. There are many undefined conversions such as conversions from double to float and long integer to integer, which leads to different implementations. The language itself is not in a stable state. Different C++ compilers may accept different C++ codes in different language versions. For example, an existing application defining a type named bool may get errors from the future C++ compiler when the type becomes built-in.

Java is designed to support applications that will be deployed into heterogeneous network environments. It specifies the size of its data types and defines all the conversion standards, which solves the problems in data type incompatibilities across different hardware and software architectures. The Java environment is supported by a architecture neutral platform - Java's virtual machine. The same Java code will be able to run on all platforms that support the virtual machine.

However, it is doubtful that whether the Java language itself is stable. The language has left some weak spots that have a high possibility to be fixed in future. Array type is an ad hoc type that requires a better design in order to fit into the class hierarchy and to prevent run-time type errors. The omission of parameterization could not be maintained long term. When not wanted, the smart reference being enforced to all objects needs an alternative as it does in Eiffel, Sather, Modula-3 and many other languages that support garbage collections. The language has included many system-dependent features and a number of ad hoc features (e.g. string conversion method) in the built-in classes, which makes it hard to keep built-in classes unchanged in future.

Transframe is designed to be as portable as Java. Primitive classes sets up a standard in operation interface, semantics and implementation for its primitive objects. The Transframe translation system will provide two kinds of outputs: standard ANSI C for native machine code translation and the Transframe Virtual Machine code for interpretation in multiple platforms.

Transframe has a very small set of built-in language features integrated in a unified concept that covers a large scope of application domains. Compared to Java, the language is less likely to be changed in future.

1.8 Dynamic

How dynamic a language is? We must define a criterion first.

By the above definitions, C++ and Java support dynamically typed pointers, static typing and static type.

However, with the help of the Java environment, both C++ and Java can support dynamic typing at the linking stage. When new code modules are linked in dynamically, new types defined in the modules are added into the system as if they were defined at run-time.

Transframe supports dynamically typed references, dynamic typing and static type. Transframe's dynamic parameterization supports an efficient dynamic typing (refer to chapter 7 for detail.)

With the help of the Transframe environment, dynamic type is supported: static classes can melted into the environment to become dynamic classes . They can be changed after they are created. Though dynamic classes are not exactly a language concept, they can be supported by the environment for fast development and dynamic design.

The following diagram shows the dynamicity chart of the three languages:

Transframe Java C++
Dynamically Typed supported supported supported
Dynamic Typing supported supported in environment supported in environment
Dynamic Type supported in environment

Chapter 2. Syntax


Let us start the detailed comparison with syntax. Though the syntax is perhaps the least important issue in modern language design, it is the most manifest side of a language. In many cases, the syntax is a matter of taste, rather than a choice of the best. Keeping this in mind, the designers of C++, Java, and Transframe have more or less retained the C syntax as much as possible.

2.1 Lexical Conventions

2.1.1 Tokens

Transframe has a different way to form tokens than C++ and Java. When collecting character into tokens, C++ and Java form the longest token based on a fixed set of operators. For example:

	x-----y

will be broken into the following tokens:

	x, --, --, -, y

which actually is not valid in semantics. The compiler will not create tokens:

	x, --, -, --, y

which actually is valid in semantics.

Transframe does not have a fixed set of operators. Users can define their own combinations of operator characters. Therefore, Transframe cannot collect characters as C++ or Java does.

Transframe compiler forms the longest token until it meets a white space, a separator, a transition from an operator character to a non-operator character, or a transition form a non-operator character to an operator character. Therefore, the above character sequence will be broken into the following tokens in Transframe:

		x, -----, y

To write the correct code, one must use white spaces:

	x-- - --y

or use separators:

	(x--)-(--y)

Transframe encourages users to write more readable expressions by using white spaces. For example, expressions:

	x-=-y
	x++-=---y

should be written:

	x-= -y;
	x++ -= - --y;

for a better readability. By a careful selection of separators, one can write most C++ expressions without unnecessary white spaces. For example, expression:

	x[--i+y++]

will be broken into tokens:

	x, [, --, i, +, y, ++,]

because [ and ] are separators.

2.1.2 Identifiers

Transframe has the same rule as C++ to construct an identifier except that an identifier cannot start with the underscore character.

Java identifiers may contain characters in a much larger Unicode character set including Chinese, Japanese and Korean characters.

Whether should an identifier include characters for different international languages by using Unicode? We think that the advantage of the freedom in expression may bring about a problem in releasing, reusing, and exchanging software packages internationally. Identifiers are used for names of objects and classes. If a software package contains classes and methods with names in Japanese characters, this software package will be useless to people who do not speak Japanese.

Transframe supports Unicode in data type only. Developers can use Unicode to develop applications with interfaces for different nationalities.

2.2 Declaration Syntax

2.2.1 Variable Declarations

Both C++ and Java have the C-style declaration syntax:

	type name_list;

However. pointers and arrays are not the part of type in C++ variable declarations, this would cause a problem of confusion. In the following declaration:

1	int* i,j;

the "*" belongs to the variable "i", not to the type "int*". Java does not have pointers with "*", but have arrays with "[]". The declaration in C++:

2	int a[], b[];

can be alternatively written as:

3	int[] a, b;

in Java.

Transframe changes the C-style variable declarations completely. It uses Pascal-style declaration: name_list: type;

Thus the above array declaration will be written as:

4	a, b: int[];

in Transframe. The reason for this choice is not only a matter of taste, but also a necessity, because in Transframe, the format:

	type name;

declares a subclass of the type, rather than a reference to the instance of the type.

2.2.2 Function Declarations

Both a C++ function and a Java method have the C-style function declaration syntax:

	output_type function_name (parameter_list);

For example, declaration:

1	int foo (int x, int y);

specifies a function that has one integer as its output and two integers as its input.

The concept of function has been unified into the concept of class. Transframe has two alternative syntaxes for class declarations:

	class name is superclass_list interface_list
or,
	superclass name interface

The second syntax can be used when the class has only one superclass and one interface. Therefore, the function foo can be written in Transframe as the following:

2	function foo (x,y: int): int;

which declares a class foo whose superclass is function and the class foo has an object instantiation interface which takes two integers as its inputs and returns an integer as its output.

This declaration syntax looks exactly like a Pascal function declaration but has a more general meaning in semantics, because function is not a keyword, it can be replaced by thread, process, remote_procedure, port, handler, or any other user-defined class names.

2.2.3 Static Specifier

The keyword static in C++ has multiple meanings:

Therefore, Java removed the second usage and reserved the keyword static only for class members.

static is no longer a keyword in Transframe.Instead, Transframe uses the keyword meta to indicate a member that has a class scope: it is a meta member.

In Transframe, static is a built-in class name that implements an object reference protocol in which objects are allocated statically. Other object reference protocols such as smart references allocate objects dynamically.

2.2.4 Final Specifier

The keyword final is not used in C++.

Java uses final for three different meanings and sometimes may cause minor confusions:

Transframe uses final only for the first case. For the second case, Transframe uses the keyword const. For the third case, Transframe uses the keyword sealed.

2.2.5 Volatile Specifier

The specifier volatile is available in C++ and Java, but not in Transframe. Without a proper mechanism to support a concurrency on shared data, the specifier volatile is necessary in C++, which provides a poor and unreadable ad hoc solution.

Java's synchronized method provides a better and clearer solution for accessing shared data, thus the specifier volatile should have been removed.

Transframe's generic object reference protocol can support any user-defined object-sharing protocol. The keyword volatile is then unnecessary.


Chapter 3. Promitive Types


3.1 Orthogonality

Primitive types in C++ and Java are not classes. Their values are not objects. Primitive types are classes in Transframe. Their values are their instances. The advantage of this orthogonality is that objects of primitive types will also have the capability to answer the who-are-you question in a polymorphic situation, and primitive types are not necessarily to be treated differently than other types. This achieves a simpler but more flexible type system.

In order to use numbers and other primitive values as objects, Java introduced a parallel class hierarchy in the java.lang package. They are Boolean, Character, Number, Integer, Long, Float, and Double. This unnecessarily complicated the usage of primitive objects. Since they are not primitive values, obligatory conversions must be used frequently. For example:

1	Double x, y, z;
2	x = new Double(3.0);
3	y = new Double(4.0);
4	z = new Double( x.doubleValue() + y.doubleValue());

The worse, Java does not support operator for class methods. Therefore, an instance of Integer cannot use operators such as +, - , *, and / as we usually use them for primitive types. As result, mathematical expressions using Java number classes will be extremely complicated. In Transframe, int, float, double, etc. are classes in which operators are defined as member classes (methods). Let's take a look at the "+" method defined in the int class:

1	sealed value class int is numeric
2	{
3		public:
4			class operator + is function
5				 prefix (): int,
6				 (int): int;
7	};

The member class "+" is defined as an operator which has two interfaces: the first interface is a prefix interface and the second interface takes an integer as input. Users can write similar C++ expression with integer objects. For example:

1	z := +x + +y;

The class int is sealed, i.e., further subclasses are not allowed. It is also a value class, i.e., names having the declared type int are static names by default. For example, in declaration:

1	x, y: int;

x and y are static names, i.e., the object associated with these names are allocated statically and they never share values.

If dynamic references must be used for objects of a value class such as int, an explicit declaration must be made:

1	x, y: smart of int;

x and y are now smart references to integer objects.

3.2 Numeric Types

C++ has many integral types including char, unsigned char, short, unsigned short, int, unsigned int, long, and unsigned long. The size of these integral types are not strictly defined, which leaves a hole for problems in portability. Besides, char is really for character type, which can hardly be considered as integers.

So Java has a simplified version of integral types: byte, short, int, and long. The size of their values are strictly defined as 8-bit, 16-bit, 32-bit, and 64-bit, respectively. Transframe has only one integer: int, which is a mathematical type and has nothing to do with the implementation, that is, how many bits are used in a integer. Programmers should always use methods like size, min_value, max_value to get the implementation-dependent data and never assume one by themselves.

Machine-dependent types are not integers in a mathematical point of view. They are binaries in Transframe: bit8, bit16, bi32, bit64, bit128, and so on. These binary types encapsulate all the necessary bit operations for system programming.

With Transframe's capability to define primitive class's interface, it is not difficult for user to define a specific integer based on a particular implementations by using one of the binary types. C++ supports three floating-point number types: float, double, and long double.

Both Java and Transframe support float and double. Float implements IEEE 754 floating-point numbers and double implements IEEE 754 double precision floating-point number. In Java, values of float are 32-bit and values of double are 64-bits. In Transframe, programmers should always use methods like size, min_value, max_value to get the implementation-dependent data and never assume one by themselves, bearing in mind that the standards may be changed in future as the underline hardware platform get changed.

3.3 Character Type

C++'s character type are char and wchar. Their values are 8-bit and 16-bit respectively.

Java has only one character type: char, which has 16-bit values representing Unicode characters.

Transframe has an abstract class character, which defines a common interface for all possible kinds of characters. Under the abstract class, there are two sealed classes: char and uchar. The type char implements a 8-bit character type for ANSI characters or any other character-sets that are within the boundary of 256. The type uchar implements a 16-bit character type representing Unicode characters. With Transframe, users may define their own character type by deriving a subclass from the character class. One possible useful type may be the variable-length character type.

3.4 Enumeration Type

Both C++ and Transframe support enumeration type.

Java does not support enumeration type except a specific type boolean. Enumeration types in Transframe are value classes derived from the abstract value class enum. bool is a sealed subclass that has two enumeration values: true and false:

1	sealed class bool is enum ( false, true );

3.5 Set Type

Only Transframe supports set type. All set types are derived from the abstract value class set.

Example of a set type:

1	sealed class FrameStyle is set 
2		(
3			visible,
4		 	resizable, 
5			movable,
6			springy,
7	 		scrollable
8		);

3.6 Referential Type

C++ supports one referential type: pointer, which requires explicit deallocations and may result memory leaks and dangling pointers.

Java also supports one referential type which uses smart reference protocols. Memory is deallocated automatically by garbage collection. In The Java Language Specification, reference types are defined as class or interface types and array types. This is quite misleading. Class types are types for objects, and the object type are not equivalent to the type of the reference used to refer to these objects.

Transframe supports a generic referential class that defines common protocols for all kinds of object references. Under this generic referential class, four referential classes are derived: type, static, smart, and pointer. The type reference is used to refer to a type, rather than an object; the static reference implements the static object allocation protocol, which is extensively used by many real-time/embedded systems where dynamic memory allocations are not required; smart reference implements a dynamic memory management with garbage collections; and finally, pointer is a low-level dynamic memory allocation/deallocation protocol which should be used only for low-level system programming.

Users should be able to define other referential protocols such as remote references and persistent references.

For detail, see chapter 4.


Chapter 4. Object References


4.1 The Concept of Object Reference

Object references are names that used to refer to objects.

In C++ and Java, object references are introduced by declarations. Consider C++ or Java declaration:

1		Image  x;

The name x is a name representing an object of Image.

The concept of object reference in C++ and Java is built-in.

In C++, all declared names are by-value names (or static names) unless the name is declared as a pointer. Pointers are low-level dynamic object references which must be deallocated manually.

In Java, names of primitive types are by-value names (or static names), while names of classes are by-reference names (or dynamic names) which use garbage collection for automatic memory deallocation. Those built-in reference semantics cannot satisfy various application requirements. There has been a long debate in history that whether a language should provide smart references or not. But garbage collection should not be the focus of the debate. Indeed, a language should provide smart references with garbage collection for a leek-free memory management. But the presence of smart references should not disable other options. Smart references are not the whole solution. Some real-time applications would not prefer to any dynamic memory management. Some long-living objects cannot be garbage collected even though they are dangling for the time being. Some Internet objects cannot contain any dynamic references relying on the local memory address space; they should package themselves as closures to travel in the network space.

Therefore, neither C++ nor Java's object reference concept can cover the needs of various applications.

The concept of name in Transframe is unified into the concept of object. That is, a name itself is an object. It is an instance of a referential class.

A declaration introduces one or more names in a scope. It regulates the range of objects which a name can represent, prevents the name from referring to an object of the wrong type, and possibly, associates an initial object to the name. Consider the following example:

1	x1: Image;
2	x2: static of Image;
3	x3: pointer of Image;
4	x4: URL of Image;
5	x5: Persistent of Image;

All names declared above are names representing an image object. But they are different names. Names declared from the first line to the third line use built-in Transframe reference classes: x1 is equivalent to a smart reference with garbage collection in languages like Eiffel and Java; x2 is equivalent to a variable in languages like C++; and x3 is equivalent to a memory pointer in languages like C++ and Pascal, which should be used only for low-level system programming; Names x4 and x5 use user-defined reference protocols. x4 is a remote reference pointing to an image located in the internet. URL is a user-defined reference class for locating web resources. x5 is a persistent reference pointing to an image residing in a persistent storage. Persistent is a user-defined reference protocol for locating objects wrapped in an object database.

In Transframe, referential is an abstract class. All names declared by declarations are instances of a subclass of referential. The referential class is parameterized by the type of the object that can be associated with the name:

1	interface class referential #(sealed ObjectType: type);

where ObjectType is the class parameter that denotes the actual type of the object represented by the name.

Transframe has four built-in sub referential classes. They are type, static, smart, and pointer. A type reference is a name representing a type (class). Other references are names representing objects. A static reference is a name permanently bound to an storage that is allocated to hold the state of an object at the time of the declaration of the name. A smart or a pointer reference is a built-in dynamic name which points to a dynamically allocated piece of storage to contain the object, and the location of the storage can be changed during the life-time of the name. Smart references provide an automatic memory reclamation, enabling a memory-leakage free programming environment. Pointers are "stupid" names that does not guarantee free of dangling objects or invalid references, which should only be used in low-level programming.

4.2 Static Reference

A static reference is similar to C++ or Java's by-value name.

Transframe has a more flexible way to specify a static reference than Java. By Java, only primitive typed names are static. By Transframe, a static name can be declared by one of the following two methods:

For example:

1	x: int;
2	y: static of image;

x is a static reference because int is a value class. Though Image is not a value class, y is still a static reference because it is declared explicitly as a static name, i.e., the type of the reference is static #(ObjectType: type of Image).

Java's limitation on static names is inconvenient and inefficient for programming large amount of composite data. Consider a pixel matrix composed of a huge number of 2D vertices. It is certainly not efficient to use dynamic references for the elements of the matrix, which may require more space and computation time; it may also break the memory into many tiny pieces.

Transframe's static name is more flexible than C++ or Java's by-value names. A transframe static name can be polymorphic. Consider an example:

1	c: character;
2	if (some_condition) c:= `c'; else c:= u'c';

The name c is a static name because the class character is a value class. It is a polymorphic name because character is also abstract. The name c may represent an ASCII character, a Unicode, or a character in any other coding standard as long as the coding does not use more than 32 bits for a character (the class character is a fixed class that has a maximum limitation on the size of its object.)

4.3 Dynamic References

Pointers are the only dynamic references supported by C++;

Smart references are the only dynamic references supported by Java.

Transframe supports both. However, pointers are the low-level names and should only be used for system programming, for example, the implementation smart references and other user-defined high-level object references.

In Transframe, whether a name is dynamic or static is determined by the name itself, not by the type of the object. Therefore, an object of a value class can use a dynamic reference as well:

1	x: smart of int;

Java does not has such flexibility because whether a name is dynamic or static is solely determined by the type of the object. As result, Java must keep a set of redundant classes such as Character and Integer for primitive types that cannot use dynamic references.

4.4 User-Defined References

Only Transframe support user-defined references.

An object may be created in the memory for temporary use, in a persistent storage to outlive the creator (program), or in a network server to be shared by many people. Objects must have places to live, and they require different kinds of living places. Therefore, we need different object references.

It would be so nice to forget about the memory, the disk, and the network; and just focus our attention on objects themselves. For example, let blue_sky be an image object. We would like to use the object by an expression

1		blue_sky.drawSelfOn(myWindow);

without worrying about where the image object is placed. It might live in the local disk, or perhaps in an internet server thousands miles away. But we don't care. Once blue_sky becomes valid, the only thing we should know is that it is an image object. In C++ or Java, things are not that simple. To get an image object from a network server, we have to do the following:

1		open an URL connection, say, X;
2		read the (binary) contents from X;
3		check whether the binary is in an image format;
4		create an image object in the memory, say, blue_sky
5		call blue_sky.drawSelfOn(myWindow);
6		free blue_sky;
7		close the connection X.

In Transframe, the following code is possible by a user-defined URL reference:

1		blue_sky: URL of Image = "http://www.foo.com/images/blue_sky.jpg";
2		if (blue_sky) blue_sky.drawSelfOn(myWindow);

We do not need to know the concept of network connections; and we should never worry about remembering clean-ups such as closing a network connection and freeing the temporary memory. Let the system take care all the difficult things. URL is another kind of smart reference that handles objects in the Internet.

By value replacement, the object contained in the destination name is replaced with the object contained in the source; by copied reference replacement, the destination name creates a new copy of the source; and by shared reference replacement, the destination name represents the same object denoted by the source name. The following figure shows the default replacements used for different name combinations.

4.5 Assignment

C++ and Java only support value assignment (for by-value or static names) and shared reference assignment (for dynamic names).

Transframe handles a wider range of object references. A program may have more than one kind of dynamic references. Dynamic references of different kinds may not be able to share objects. For example, a smart reference referring to objects in the memory cannot share the object referred by a persistent object references.

Transframe supports copied reference assignment: objects can be copied from one name to another name of the different type. Consider an example:

1	blue_sky: URL of Image = = "http://www.foo.com/images/blue_sky.jpg";
2	image_cashe: Image = blue_sky;

blue_sky is a URL reference and image_cashe is a smart memory reference. They are different dynamic references. The assignment at line 2 will created a new smart references in the local memory and copies the image contained the blue_sky reference into the local memory referred by image_cashe. Transframe also supports tuple assignment. For example:

1	x: int; y: double; z: char;
2	(x, (y, z)) := (0, (1, `c'));

4.6 Simplicity in Usage

Programming in C++ pointers is not only dangerous, but also very difficult in terms of its very complicated syntax with operators like &, *, and ->. This is especially true when pointers are combined with templates, array structures, variable number of function inputs, etc. Just consider an example in using a template in C++'s STL:

1		Set<T*> y;
2		for (Iterator x(y); x; x++) (*x)->M(); 

where M is a member function name defined in the class T. The code is the second line is very tricky. It is almost impossible for an average C++ user to figure out why the operator * should be used. Java and Transframe simplifies the usage of object references. Operators like &, *, -> are not required. There is no tricky C++ expressions like &(**x)->foo(). There is no need to delete. Names of all types have a simple and uniform interface for usage; for example, operator "." is used for all names: no matter it is static or dynamic.

The above C++ example can be written in Transframe in a more readable form:

1		y: Set of T;
2		foreach (x in y) x.M(); 

With the simplified usage, however, Java loses a certain C++ power in expression. In fact, there is no choice for user to select a static name or a dynamic name. Operators "." and "=" cannot be overloaded for specific object access and assignment protocols.

Transframe, with the same simplified usage, get at least the same, if not more, power than C++ in expression. Like C++, user can select between static names and dynamic names, and operators "." and ":=" can be overloaded for specific object access and assignment protocols.

When more than one object references must be returned from a routine, there is no direct way for a Java method to provide such interface. Let's consider an example: given a URL resource address, we need a method to decompose the resource address into three strings: the path, the resource file name, and the resource type name. That is, for an address:

	"http://www.foo.com/images/blue_sky.jpg"

we would like to use this method to break the address into the following three strings:

	"http://www.foo.com/images/"
	"blue_sky"
	"jpg"

In C++, we can write a function with the following interface:

1		void decompose ( const char *url_address,
2				char *&url_path,
3				char *&resource_name,
4				char *&resource_type
5			);

The C++ interface declares the three outputs in terms of reference of pointers. It is explicit but difficult to understand. What does "*&" mean? Should I write "*&" or "&*"? Java does not have a much simpler way to describe this interface, either array or extra class should be introduced as the following example:

1		void decompose ( String url_address,
2				 String[2] decompose_result
3				);

The Java method interface does not have any improvement in readability. People have to guess the meaning of expressions like decomposite_result[1]. The worse, if elements of the array must be in different types, the type of each elements must also be guessed. Since Transframe support tuple type constructor, a Transframe decompose function can be written as the following:

1		function decompose 			
2			(url_address: String):
3				(	url_path: String;
4					resource_name: 	String;
5					resource_type: String
6				);
7						

The output type of the decompose function is a tuple that contains three strings, which is more readable than C++ and Java's interface. The function can be called in the following way:

1	path, rname, rtype: String;
2	(path, rname, rtype) := decompose(url_path);

4.7 Summary

Unifying object references into the concept of object, Transframe gets two major benefits:

The following diagram is a brief comparison in the concept of object references among languages of Transframe, Java, and C++.

Transframe Java C++
Garbage Collection supported with alternatives supported and enforced
Static Allocation possible for user-defined classes for primitive types only possible for all types/classes
User-defined Reference supported
Ref. Operator Override supported supported
Copied Ref. Assignmnet supported
CTupple Assignmnet supported
Simple in use simple simple complicated
Safety safe for all resources safe for memory not safe

Chapter 5. Classes & Functions


5.1 The Concept

The class is the fundamental concept for object-oriented programming.

C++, Java, and Transframe share the common concpet: a class is a template for creating objects. Objects are described by their class in terms of their data structure, functionality, and the process of creation and termination as shown in the following table:

Transframe Java C++
Data Structure member object name declarations fields data member
Functionality member classes methods member functions
Creation constructor (enter) constructor constructor
Termination destructor (exit) destructor (finalize) destructor

By unification, Transframe's class concept is more general than C++'s and Java's. It covers concepts such as C++ functions, Java methods, C++ template classes and functions, Java's primitive types, arrays, and input/output interfaces.

Unification not only provides a wider coverage, but also simplifies the concept as well as the language implementation. The fixed and built-in part of the language framework becomes smaller while the user-definable part becomes larger so that the frameworks are flexible enough to build various high-level application models.

5.2 Functions

A C++ function is a subroutine that has an input/output interface and a piece of code that is to be executed when the function is called.

In Transframe, the C++ function concept is replaced with the concept of class. What follows is a translation table that maps a C++ function into a Transframe class:

C++ Transframe
Input formal parameters input interface for object instantiation
Output the returned object the result of the object instantiation
Code function body code for constructor

There are two kinds of object instantiations in Transframe. Given a class C, a structural instantiation shall create an object of the class C by executing the execution body of a constructor with a number of given actual parameters, and when the instantiation is done, the object of the class C is returned. A functional instantiation shall create an object of the class C by executing the execution body of a constructor with a number of given actual parameters, and when the instantiation is done, the object of the class C is deleted and a new object (if any) of the type specified by the output is returned.

C++ and Java classes only support structural instantiations. They create objects that remain active until they are deleted explicitly or they are no longer used. They are used for creating objects that will last to interact with the rest of the system, for examples, a file server, an object container, and an information collector.

But not all objects should be created to remain active in the system. Activities such as a thread to perform a scientific calculation, an event processing procedure, and a job to print a file are created in an operating system as objects to perform some function. When they finished their performance, their duties are done and they should be deleted. And at the same time, they may generate some output as the result of their work. This is the concept of Transframe's functional instantiations.

C++ function is just a specific case of the Transframe's functional object instantiation. Transframe has a class named function to support the equivalent C++ function call protocol-an efficient and sequential object instantiation protocol using the same stack frame technique.

Consider a Transframe function:

1	function print (object_list: ...)
2	{
3		foreach (obj in object_list) print_obj(obj);
4	}

which defines a class print whose superclass is function (note that function is not a keyword.) It can be written in the following equivalent code:

1	class print is function
2	{
3		enter (object_list: ...)
4		{
5			foreach (obj in object_list) print_obj(obj);
6		}
7	}

The following expression:

8	print ("this is the result", x+y, "./n");

will use the object instantionation protocol built in the function class to create a print activity. It is equivalent to a C++ function call.

Transframe's functional instantiations are not limited to sequential instantionations. User can define other functional classes such as processes and remote procedures to implement other functional object instantionation protocols. For example:

1	process print (object_list: ...)
2	{
3		foreach (obj in object_list) print_obj(obj);
4	}

It defines a class print whose superclass is a user defined class-process. Calling print shall created a concurrent process to do the print job.

5.3 Multiple Input Interfaces vs. Function Overloading

The input interface describes what inputs are required to created an object or to call an function. The output describes what kind of object is returned after the creation or the function call.

Both C++ and Java support multiple interfaces for class constructors: a class may have more than one constructors that have different input interfaces.

But C++ and Java does not support multiple input interfaces for functions (C++) or methods(Java). Instead, they use a different concept: name overloading. For example, functions

1	void print (int);
2	void print (char[]);
3	void print (double);
are three different functions using the same name.

Name overloading is considered problematic. Two entities with the same name may cause confusion. This is especially true in C++ without a support of modularity.

Name overloading also complicated the language when it is combined with other object-oriented features such as override (redefinition) and inheritance. Given a C++ member function or a Java method with a name that has been used, it is rather confusing for us to determine whether it overloads to, or it redefines, or it has nothing to do with the previously defined. Consider an example:

1	class base
2	{
3		public:
4			virtual void print (base*);
5			void print (char[]);
6	};
7	class derived: public base
8	{
9		public:
10			void print (base*)
11			void print (char[]);
12	};

The print function at line 5 overloads to the print declared at line 4. They are considered two different functions. However, the function at line 10 redefines the implementation of the print at line 4. It is an implementation override and should be considered the same function at the interface level. The function at line 11 overloads to the function declared at line 10, but has nothing to do with the function declared at line 5.

By the unification of class and function, the name overload mechanism is no longer required in Transframe. Both classes and functions can have multiple input interfaces. Consider a print function, in Transframe, it can be written as follows:

1	class print is function
2	{
3		public:
4			enter (int) {...};
5			enter (char[]) {...};
6			enter (double) {...};
7	};

Even though the Transframe syntax sugar allows people to write the above function in the following separate way:

1	function print (int);
2	function print (char[]);
3	function print (double);

they are not considered three different functions with the same name but a single function with multiple interfaces.

5.4 Class and Function Pre-Declaration

C++ and Java's class pre-declaration is just a name declaration. For example:

1	class NamedNode;

This predeclared class name can only be used for pointer or object reference declarartion. For example (in C++):

2	NamedNode *n;

NamedNode cannot be used to instantiate objects unless it is fully defined. Therefore,

3	n = new NamedNode();

is invalid.

On the other hand, C++ function predeclaration can define the interface of the function:

4	void print (char[]);

The function can be called without being fully defined. Transframe's class predeclaration can define the class parameters, superclasses, and object instantiation interfaces. A class can be incrementally pre-declared. For example:

1	class NamedNode;
2	class NamedNode is TreeNode;
3	class NamedNode is TreeNode 
4			(name: char[]), 
5			(name: char[]; parent: NamedNode);

The last predeclaration declares two object instantiation interfaces., and any subclass of NamedNode is then required to implement the two interfaces. Now, the class NamedNode is ready for object instantiation:

1	function CreateNamedTree (parent: char[]; chidlren: char[]...):NamedNode
2	{
3		root: NamedNode(parent);
4		foreach (name in children) NamedNode(name, root);
5		return root;
6	}

5.5 Function Type

C++ function type is for function variables; so that functions can be passed as arguments to another function. For example:

1	void sort 
2		( 	ComparableNode* node_list, 
3			int (NodeCompareFunction(ComparableNode*, ComparableNode*))
4		);

The second formal parameter is a function which has an interface:

	int (ComparableNode*, ComparableNode*)

The syntax to declare a function parameter is rather confusing. It is very difficult even for an experienced C++ programmers to guess the real meaning behind the lines. Just imagine to read the following code:

1	int (*g())[]
2	int (*h(z))[]
3	int (*(*d(w))[])()

The C++ function type has been unified into the class concept in Transframe. This unification obtains not only a more powerful mechanism, but also a simpler concept. A function type is just an abstract class with object instantiation interface defined but not implemented. For example, the class

1	class NodeCompareFunction is
2			function (ComparableNode, ComparableNode): int;

is an abstract class with an interface (ComparableNode, ComparableNode):int. The sort function can declare a class variable to accept a congruity function (class) for node comparison:

1	function sort 
2		( 	node_list: ComparableNode...; 
3			f: type of NodeCompareFunction
4		);

A concrete function for node comparison can be defined as a subclass of NodeCompareFunction:

1	NodeCompareFunction MyNodeCompareFunction
2				(ComparableNode, ComparableNode):int { ... };

or:

1	class MyNodeCompareFunction is NodeCompareFunction
2				(ComparableNode, ComparableNode):int { ... };

This concrete function then can be submitted to the sort function:

3	sort (a_node_list, MyNodeCompareFunction);

Note that Transframe's abstract class are not limited to functions. This mechanism can be used for any class. That is, any class can be passed as an argument into another class' instantiation interface for object instantiations. For example:

1	function CreateNamedTree 
2		(	NodeType: type of NamedNode;
3			parent: char[];
4			chidlren: char[]...
5		):NamedNode
6	{
7		root: NodeType(parent);
8		foreach (name in children) NodeType(name, root);
9		return root;
10	}

Let WidgetNode be a subclass of NamedNode, then,

11	CreateNamedTree 
12		(	WidgetNode,
13			"ManiMenu",
14			"File", "Edit", "Views", "Tools", "Windows", "Help"
15		);

will create a tree of WidgetNode with a root named MainMenu.

5.6 Nested Classes

Nested classes, functions, or modules have been provided in Simula, Algol, Beta, Ada, and many other languages.

The C programming language excluded the nested structure for simplicity, but C++ has chosen to implement a nested structure for classes.

C++ nested class has a basic conflict with the spirit of object-oriented composition. The only purpose to have nested classes in C++ is to provide a local class that is valid within the entity where the nested class is defined. The nested class has nothing to do with the enclosing object, i.e., it is different from a member function which is tied up to the object of the enclosing class. It is only a mean of name hiding and is of little practical value.

This inconsistency also adds complexity to name resolution rules. Exceptional name rules must be considered for nested classes. For example, a name defined in the out scope, if not hidden, can be accessed in the inner scope, but a nested class has no access to members defined in its enclosing class. When nested classes are combined with the ambiguity caused by multiple inheritance, using the scope resolutor "::" is not sufficient to determine whether a nested class or a base class is referred. For example:

1	class A { class N {}; };
2	class B { class N {}; };
3	class C: public A, public B
4	{	public:
5			class B
6			{	public:
7					class N {};
8			};
9	};

If we have declaration:

10	C::B::N x;

which N is used? It can be the N nested in the nested class B, or the N nested in the base class B. Specific rule must be set to determine how the name should be resolved.

The real value of a nested class is the role of a member function, the spirit of object oriented programming: the nested class must be related to the object of the enclosing class. This is unfortunately not supported by C++ nested class.

Let us consider an pattern: an object is composed of a number of specific component objects which cannot live outside of their enclosing object. In this pattern, it is not only anti-intuitive but also dagerous to define the class of the component object outside the class of the enclosing object.

Suppose we have an LCD screen object which is composed of a number of LCD screen cells, and each cell is composed of a number of LCD segments. The LCD screen cells must be created as a child (component) of an LCD screen. By defineing Cell a member class of LCDScreen and Segment, a memebr class of Cell:

1	class LCDScreen is Widget
2	{
3		public:
4			class Cell is Widget
5			{
6				public:
7					class Segment is Widget
8					{
9						...
10					};
11					...
12			};
13			...
14	};

we can prevent users from putting an LCD cell into a wrong place, which may cause a run-time error. To create an LCD cell, just as we call a member function, we must provide an object of LCD screen, and the cell will be created within the screen as a component object. To create a LCD segment, we must provide a LCD cell, and the segment is created as a component of the cell. For example:

1	camera_lcd: 				LCDScreen();
2	indicator_cell:				camera_lcd.Cell();
3	auto_zoom:				indicator_cell.Segment();
4	self_timer:				indicator_cell.Segment();

This nested structure is especially useful for creating singleton classes within subclasses where the enclosing object can be omitted. For example,

1	class CameraLCD is LCDScreen
2	{
3			object IndicatorCell is Cell
4			{
5				object AutoZoom is Segment {};
6				object self_timer is Segment {};
7				object BatteryCharge is Segment {};
8				...
9			};
10			object FlashCell is Cell
11			{
12				object Auto is segment {};
13				object Fill is segment {};
14				object Night is segment {};
15				...
16			};
17			...
18	};

The omitted enclosing object is by default self, which is equivalent to this in C++. The above code is a natural description of a hierarchical system.

Transframe unifies the concept of member functions and nested classes into the concept of member class. Transframe's member class is a true nested class that has the same semantics as a member function. Thus the above code, the demonstration of the only meaningful usage of nested classes, is supported by Transframe.

5.7 Constructors

to be completed

5.8 Destrcutors

to be completed


Chapter 6. Expressions


Summary

To be completed. Here is the summary:

Transframe Java C++
Syntax C-like C-like C-like
Semantics for operators uniform rules, simple individual rules, medium individual rules, complicated
Operator override in class supported supported
User-defined Operators supported
User-defined operator format supported
Domain-specific Syntax supported

For detail, refer to The Expressive Function of a Language.


Chapter 7. Parameterization


7.1 The Necessity of Parameterization

Many people view parameterization as an alternative means for reuse and polymorphism. But there is another important usage that cannot be replaced by other features such as inheritance and polymorphic variables. It is the specification of type dependency.

Consider a polymorphic C++ function interface:

1	Animal* propagate (Animal *x, Animal *y);

where Animal is a class. The function can take two references that refers to objects of two different types (as long as they are Animal's subclasses.)

But if the function requires that the two inputs are in the same type and the output type must be the same type of the input, the above function interface is at least not precise, if not wrong, which would make wrong type combination possible, for example propagate(tiger, cow), and hence raise a run-time type error.

Parameterizing the function interface with a class parameter will give a more precise interface:

1	template < class AnimalType > 
2	AnimalType* propagate (AnimalType *x,AnimalType *y);

With this interface, the function call propagate(tiger, cow) will be invalid.

Let's consider another example:

1	class Vehicle;
2	class Limo : public Vehicle;
3	class Airplane : public Vehicle;
4	class Operator { void operate(Vehicle)=0; };
5	class Chauffeur : public Operator { void operate(Limo); };
6	class Pilot : public Operator { void operate(Airplane); };

The operate function interface defined in the superclass Operator fails to reflect the fact that a concrete operator cannot drive any type of vehicle. That is, the type of the vehicle that an operator can drive depends on the type of the operator. This imprecision causes subclasses of Operator unable to inherit the interface and they have to correct the interface by covariant redefinition which is impossible in C++.

It is recommended by many experienced C++ programmers that parameterized classes should be used for this kind of dependency:

1	template < class VehicleType > 
2	class Operator 
3	{ 
4		void operate(VehicleType);
5	};
6	class Chauffeur: public Operator<Limo>
7	class Pilot: public Operator<Airplane>

The operate function interface is redefined automatically in classes Chauffeur and Pilot via the class parameter VehicleType.

Parameterization is a missing part in Java, which is the major imperfection in language design.

7.2 Class Parameter

Class parameters are unconstrained in C++ templates. The error of using a class parameter is not detected at the time the template is designed. This is the major defect from the viewpoint of software engineering. When templates are delivered to customers, the designer of the templates cannot guarantee that there is no usage errors regarding to class parameters, and the template user must dig into the template source to find where the error is.

Class parameters are constrained in Transframe; so that the error of using a class parameter can be detected at the time the parameterized class is designed.

7.3 The Concept of Parameterization

C++ templates are overwhelmingly complicated, especially when they are combined with nested classes and template member functions. The Standard Template Library (STL) designed for C++ users are so complicated that most current C++ compilers cannot compile it. The specification of C++ templates itself suffers from defects that are still being repaired.

Transframe unifies the concept of parameterized class into the concept of class. A parameterized class has no difference from a non-parameterized class in language concept. Unlike traditional approaches, Transframe's parameterized class is a real class. For example:

1	class Operator #( VehicleType: type of Vehicle )
2	{
3			function operate(VehicleType);
4	};

No keyword like template or generic is necessary because Operator is just a class. VehicleType is the class parameter, which is constrained by the class Vehicle.

7.4 Polymorphism and Dynamic Binding

Since C++ templates are not real classes, they cannot be used to declare polymorphic variables without the binding of class parameters at compile time. For example, to declare an Operator variable, you must give the vehicle type:

5	Operator < Limo >  x;

Declaring a polymorphic Operator variable in C++ is impossible. This defect makes C++ templates in many ways pretty poor compared to the real polymorphism implemented by polymorphic variables with inheritance.

Transframe's parameterized class is a real class, it can be used to declare a polymorphic variable:

6	x: Operator;

with the class parameter VehicleType unbound. The class parameter can be bound later at runtime. For example:

7	x:= Operator#(Limo)();

Therefore, Transframe's parameterized classes support the real polymorphism: class parameters can be bound at run-time. F

urther, Transframe integrates parameterization and inheritance into one subclass hierarchy: reconstraining class parameter or binding class parameters generates subclasses. Therefore, class Operator#(Limo) is a subclass of Operator.

7.5 Heterogeneous Structure

C++ templates cannot support heterogeneous data structure,

Let us consider an example first. Given a heterogeneous array:

1		int number_of_animals;
2		Animal * animals[];

The task is to sort these animals into several animal houses which are objects of the following parameterized class:

3		template class AnimalHouse < class AnimalType, int size > 
4		{
5			private:
6				AnimalType animals[size];
7			public:
8				// public method interface of the house
9		};

Suppose that we have got the following information from the animal list:

and we have put these information in the following variable:

10		int number_of_species;
11		int number_of_each_species[];
12		Animal* representative[];

It is impossible to do the following code:

13	AnimalHouse* houses[number_of_species];	
14	for (int i=0; i < number_of_species; i++)
15		houses[i] = 
16			new AnimalHouse 
17			< typeof(representative[i]), number_of_each_species[i] > ;

Firstly, at line 13, we cannot use template class AnimaHouse to declare a heterogeneous animal house array. Secondly, at line 15, we cannot create an animal house by biding a run-time value to class parameters.

Transframe's parameterized classes support heterogeneous data structure by allowing polymorphic variables and dynamic binding. The following Transframe code is valid:

18	houses: AnimaHouse...number_of_species;	
19	for (int i=0; i < number_of_species; i++)
20		houses[i] := 
21	          	AnimalHouse
22	          	#( typeof(representative[i]), number_of_each_species[i] )();

7.6 Interface Parameterization

Interface parameterization is supported in C++ through template functions. As specified in C++ X3J16 document, a template function specifies how individual functions can be constructed. A family of sort functions, for examples, might be declared in a template function:

1	template < class T >  void sort( vector < T >  x);

The actual type parameter bound to T is deduced from the actual input parameter bound to x. For example,

2	vector < complex >  vc;
3	vector < double >  vd;
4	sort(vc);	
5	sort(vi);

At line 4, the actual function called is sort, which is the concrete function instantiated from the template function sort by a syntactic expansion at compile time.

C++ template function is not a real function, it cannot be called directly without static syntactic expansion. Therefore, if the actual type bound to T cannot be deduced at compile-time, the template function cannot be used.

Interface parameterization is also supported in Transframe but in a different concept: parameterized interface. Consider the same function and function calls in Transframe:

1	function sort < T >  (x: vector#(T));
2	vc: vector#(complex);
3	vd: vector#(double);
4	sort(vc);	
5	sort(vi);

In Transframe, sort is a function, a real function that can be called directly. It has a parameterized interface. T is the type parameter, which can be deduced from the actual type of the input. For example, at line 4, T is bound to complex, and at line 5, T is bound to double. The advantage of Transframe's approach over C++'s is that Transframe's parameterized function interface support dynamic binding: the actual type bound to type parameters are not necessarily to be deduced at compile time. For example:

6	v: vector;
7	if (some_condition) v:= vector#(complex)(); else v:= vector#(double)();
8	sort(v);	
At line 8, the actual type bound to T is the element type of v, which is bound at run-time. Since Transframe's function concept has been unified into class, the input interface is actually the interface for constructor, i.e., the object instantiation interface. Therefore, every constructor interface can be parameterized. This is not allowed in C++. Consider an example:
1	class Corporation #( OrganizationType: type of Organization)
2	{
3			group: 		OrganizationType;
4		public:
5			enter < T: type of OrganizationMember >  ( head: T; memebrs: T... )
6			{
7				group:= OrganizationType(head, members);
8			};
9	};

The class Corporation is a group of members organized by OrganizationType. OrganizationType is a class parameter which determines the structure how the corporation to form its members. The constructor interface is parameterized by another parameter: the member type of the corporation. Inside the corporation, the group is a reference to an organization whose member type is given by the constructor's interface.

7.7 Type Dependency

In the first section of this chapter, we discussed the important usage of parameterization: specifying the type dependency.

As shown in the examples given in the first section, C++ templates can be used to specify the type dependency among inputs of a function and among classes and their subclasses. But C++ templates can only specify a shallow type dependency.

Class parameters in Transframe can be used to specify a more delicate type dependency. Consider an example. Let's assume that Animal be a class parameterized with FoodType and AnimaHouse be a class parameterized with AnimalType:

1	class Animal #( FoodType: type of Food )
2	{
3		public: function eat(FoodType);
4	};
5	class AnimalHouse # (AnimalType: type of Animal )
6	{
7		public:
8			// operators required by foreach statement:
9			function operator first(): AnimalType;
10			function operator next(): AnimalType;
11	};

We need a function to feed animals in an animal house. The function interface must specify the type diapedeses: the food supplied must be edible to animals in the house. In Transframe, such delicate dependency can easily be written by using class parameters:

12	function feed 
13		( house: AnimalHouse; food: AnimalHouse.AnimaType.FoodType )
14	{
15			foreach (animal in AnimalHouse) animal.eat(food);
16	}

7.8 Recursive Parameterization

Classes are often mutually dependent. For example:

1	class Vehicle #( OperatorType: type of Operator )
2	{
3			function operated_by(OperatorType);
4	};
1	class Operator #( VehicleType: type of Vehicle )
2	{
3			function operate(VehicleType);
4	};

The vehicle type is dependent on the operator type and the operator type is dependent on the vehicle type.

Due to the nature of static expansion, C++ templates requires the class used to bind the class parameter to be fully defined. Consider the following code:

1	template < class VehicleType >  class Operator 
2	{ 
3		void operate(VehicleType);
4	};
5	template < class OperatorType >  class Vehicle 
6	{ 
7		void operated_by(OperatorType);
8	};
9	class Limo;
10	class Chauffeur: public Operator<Limo> {};
11	class Limo: public Vehicle<Chauffeur> {};

is invalid in C++. The error is at line 10: Limo is used to bind VehicleType while it has not been fully defined. Mutual dependency cannot be modeled by C++ templates.

Transframe's class parameter dynamic binding makes recursive parameterization possible. The class used to bind class parameters may not be fully defined. Consider the following Transframe code:

1	class Operator;
2	class Vehicle #( OperatorType: type of Operator )
3	{
4			function operated_by(OperatorType);
5	};
6	class Operator #( VehicleType: type of Vehicle )
7	{
8			function operate(VehicleType);
9	};
10	class Limo is Vehicle;
11	class Chauffeur is Operator#(Limo) {};
12	class Limo is Vehicle#(Chauffeur) {};

At line 10, Limo is predefined as a subclass of Vehicle, and then, it is used to bind the class parameter VehicleType.

7.9 Summary

Java does not support parameterization, which is the major imperfection in language design. The following diagram is a brief comparison in the concept of parameterization among languages of Transframe, Java, and C++.

Transframe Java C++
Concept unified, simple hybrid, complicated
Class parameterization supported supported
Interface parameterization supported supported
Type Dependency Specification fully supported superficially supported
Constrained Parameterization yes no
Design Error Isolation yes no
Dynamic Binding yes no
Able to define variable yes no
Support Heterogeneous data structure yes no
Support mutual parameterization yes no

Chapter 8. Inheritance


8.1 Default Override Policy

A member function declared in superclass (base class) can be overridden in a subclass (derived class).

C++ has the essential difference to Java and Transframe that member functions are by default not to be overridden. By C++, only member functions declared as virtual can be overridden in a derived class. In Java and Transframe, member functions are by default to be overridable. To prevent a member function being overridden in a derived class, the member function must be declared as final.

Consider an example:

1	class Base
2	{
3		public:
4			virtual void foo1();
5			void foo2();
6	};
7	class Derived : public Base
8	{
9		public:
10			virtual void foo1();
11			virtual void foo2();
12	};

The member function foo1 declared in the class Derived overrides the member function foo1 declared in Base; however, the member function foo2 declared in Derived does not override foo2 declare in Base. This example is equivalent to the following Transframe code:

1	class Base
2	{
3		public:
4			function foo1();
5			final function foo2();
6	};
7	class Derived is Base
8	{
9		public:
10			function foo1();
11			function foo2();
12	};

8.2 Pure Virtual Function

A pure virtual function in C++ is an member function that has no implementation. It is equivalent to Java's abstract method, or Transframes's deferred member function.

The term abstract and deferred are difference in Transframe:

Consider an example:

1	class Server
2	{
3		public:
4			abstract class Service {...};
5			deferred class SelectService is function ():Service {...};
6	};
7	class DistributeServer is Server
8	{
9		public:
10			class RemoteService is Service {...};
11			class SelectService is function ():Service {...};
12	};

The member class Service is abstract. Therefore, the following code:

13	x: Server = DistributedServer();
14	y: x.Service = x.Service();

is invalid because the abstract class Service cannot be used to created a service object. However,

15	y: x.Service = x.SelectService();

is valid. Though the implementation is not given when it is declared in Server, it creates a select-service activity (object) via a run-time binding to the actual implementation provided in DistributedServer.

The term abstract used in Java has different meanings that must be distinguished by differences in the context. When it is used with a class, it means non-instantiatable; when it is used with a method, it means a delayed implementation.

The concept of method is integrated into the class concept in Transframe. It is impossible to use one word to denote two different meanings: a class can be either abstract or deferred.

8.3 Multiple Inheritance

With the belief that multiple inheritance generates more problems than benefits, the designers of Java discarded multiple inheritance. The desirable feature of multiple inheritance is provided by multiple interface implementation. A Java interface is a class that has nothing but abstract methods (pure virtual functions) and constants. A subclass can be extended from a single base (super) class as well as implement more than one interfaces.

The designer of Transframe hold a similar belief but provided a rather different approach which avoided all the problems with multiple inheritance and result a more powerful feature than Java's multiple interface implementation.

To Java's interface class, Transframe has an equivalent concept in which only deferred member classes and meta members can be defined. A subclass can be derived from more than one superclasses, but only the first superclass can be non-interface class. The first super class is called primary superclass.

Because interface classes cannot have implemented member functions (member classes), multiple inheritance based on interface does not cause the problem of the base duplication in C++. However, it is sometimes desired to be able to inherit implemented member functions from more than one superclasses.

Consider a C++ example that uses the Observer pattern (this example is originally given by Robert Martin in his article: Java and C++: A Critical Comparison):

1	class Observer
2	{
3		public: virtual void Update() =0;
4	};
5	
6	class Observable
7	{
8		private:	 Set myObservers;
9		public:	  void Register(Observer& x) { myObservers.Add(&x); };
10			    void Notify()
11				{
12					for (Iterator x(myObservers); x; x++)
13						(*x)->Update(); 
14				};
15	};
16	
17	class Clock
18	{
19		private:		Time myTime;
20		public:		virtual void Tick() { myTime++; };
21	};
22	
23	class ObservableClock: public Clock, public Observable
24	{
25		public:		void Tick{} { Clock::Tick(); Notify(); };
26	};

The class ObervedClock is a combination of a clock and an observed object. We would like to use multiple inheritance to obatain the implemented methods from both the Clock and Observable classes.

With Java's multiple interface inheritance, one have to simulate this by aggregation, which is inconvenient and anti-intuitive:

1	public interface Observer
2	{
3		void Update();
4	};
5	
6	public interface Observable
7	{
8		void Register(Observer x);
9		void Notify();
10	};
11	
12	class ObservableImp extends Observable
13	{
14		private 		Vector myObservers;
15		public		void Register(Observer x) { myObservers.AddElement(x); };
16		public		void Notify()
17				{
18				 	Enumeration e = myObservers.elements();
19					while (e.hasMoreElements())
20					{
21						Observer x = (Observer) (e.nextElement());
22						x.Update();
23					} 
24				};
25	};
26	
27	class Clock
28	{
29		private		Time myTime;
30		public	 	void Tick() { myTime.increaseOneSecond(); };
31	};
32	
33	class ObservableClock extends Clock implements Observable
34	{
35		private		ObservableImp myObservableImp;
36		public 		void Register(Observer x) { myObservableImp.Register(x);};
37		public 		void Notify() { myObservableImp.Notify(); };
38		public		void Tick{} { super.Tick(); Notify(); };
39	};

Though Transframe's interface class cannot have implemented member classes, an interface class can be inherited with a delegation to its default implementation. A delegation is a subclass of an interface class. It implements the member class declared in the interface class and is used for the default implementation of the member class declared in the interface class used as a non-primary superclass. When a delegation is provided for a non-primary superclass, the subclass shall have an implicitly declared member object of the type of the delegation. This member object is called delegate member. The member class declared in the non-primary superclass is implemented by a member class declared in the delegation. The member class declared in delegation is called delegate member class. The member class in the non-primary superclass is inherited as if it were overridden by the delegate member class. However, its owner shall be automatically delegated to the delegate object when the member class is used for object instantiation.

Consider the Transframe's version of the ObservableClock example:

1	interface class Observer
2	{
3		public: function Update();
4	};
5	
6	interface class Observable
7	{
8		public	:	function Register(Observer);
9				function Notify();
10	};
11	
12	class ObservableImp is Observable
13	{
14		private:	 myObservers: list of Observer;
15		public:	  function Register(x: Observer) { myObservers.Add(x); };
16			    function Notify(){ foreach (x in myObservers) x.Update(); };
17	};
18	
19	class Clock
20	{
21		private:		myTime: Time;
22		public:		function Tick() { myTime++; };
23	};
24	
25	class ObservableClock is Clock, Observable by ObservableImp
26	{
27		public:		function Tick{} { Clock::Tick(); Notify(); };
28	};

A call to the member function Notify or Register declared in the interface Observable via an observable clock will be delegated automatically to the implementation provided in the class ObservableImp via delegate object, which is an instance of ObservableImp, declared implicitly as a member object of the observed clock.

Note that ObservableClock is a subclass of Observable, but not a subclass of ObservableImp.

8.4 Override with Multiple Inheritance

With multiple inheritance, it is possible for a subclass to inherit more than one member functions (classes) with the same name and the same input interface type (signature).

Consider the following C++ example:

1	class Watchable
2	{
3		public:		virtual SizeType Size () =0;
4				virtual void DrawSelf () =0;
5	};
6	
1	class Locatable
2	{
3		public:		virtual PositionType Position () =0;
4				virtual SizeType Size () =0;
5	};

Both the class Watchable and Locatable has a member function Size. If a subclass is derived from them and declares the member function:

6	class DisplayableWidget: public Watchable, public Locatable
7	{
8		public:		SizeType Size() { // return the size of the Widget; }
9	};

This member function will implement the Size function defined in both Watchable and Locatable.

Java and Transframe has the same override mechanism as C++ in this case. However, the following Java example will show the difference from C++:

1	interface Watchable
2	{
3		SizeType Size ();
4		void DrawSelf ();
5	};
6	
1	interface Locatable
2	{
3		PositionType Position ();
4		SizeType Size ();
5	};
6	
7	class Widget
8	{
9		public:		SizeType Size () { // return the size of the Widget; }
10	};
11	
12	class DisplayableWidget extends Widget implements Watchable, Locatable
13	{
14	};

Although the member function Size is not declared in the class DisplayableWidget, it is implemented in the class Widget which is inherited by DisplayableWidget. Therefore, the implementation provided in the class Widget is automatically used to implement the Size method declared in both Watchable and Locatable.

With C++, DisplayableWidget must explicitly declare the member function Size in order to implement the Size function declared in these two interface classes:

15	class DisplayableWidget: 
16			public Widget, public Watchable, public Locatable
17	{
18		public:		SizeType Size () { return Widget::Size(); }
19	};

Transframe has the same override mechanism as Java in the second case. However, the following Transframe example will show the difference from Java and C++:

1	interface class Watchable
2	{
3		public:		function Size (): (float;float);
4				function DrawSelf ();
5	};
6	
1	interface class Locatable
2	{
3		public:		function Position (): (float;float);
4				function Size (): (float, float);
5	};
6	
1	interface class Containable
2	{
3		public:		function GetContents (): Widget...;
4				function Size (): (float, float);
5	};
6	
7	class Widget
8	{
9		public:		SizeType Size () { // return the size of the Widget; }
10	};
11	
12	class DisplayableWidget is Widget, Watchable, Locatable, Containable
13	{
14		public:
15				function Containable::Size(): (float;float)
16				{
17					widgets: Widget... = GetContents();
18					size: (w: float; h: float) = (0,0);
19					widget_size: (w: float; h:float);
20					foreach (widget in widgets)
21					{
22						widget_size:=widget.Size();
23						size.w := max (size.w, widget_size.w);
24						size.h := max (size.h, widget_size.h);
25					}
26					return size;
27				}
28	};

Like Java, the implementation provided in the class Widget automatically implements the Size function declared in both Watchable and Locatable. However, the Size function declared in the class Containable is specifically overridden in the subclass.

This specifically overridden function must be called by using the class scope resolutor. Consider:

1	x: DisplayableWidget();
2	x.Size();
3	x.Containable::Size();

The first Size function call uses the implementation provided in the class Widget; and the second uses the one implemented in the class DisplayableWidget specifically for the Containable size.

Without a renaming feature, we feel that this specific override mechanism is necessary. When interface classes are designed separately in different domains, we cannot guarantee that all the member classes (functions) with the same name and the same input signature will have the same semantics.

8.5 Member Class Inheritance

The Transframe's inheritance mechanism is more general than C++ and Java because the concept of member function is generalized to member classes.

A member class can inherit another member class as shown in the following figure:

Suppose we want to develop a task model which is a set of resources shared by a number of concurrent threads. Intuitively, we would like to have Thread defined as a member class of the Task class:

1	 class Task
2	{
3		public:
4			class Thread
5			{
6				public:
7					auto enter ()
8					{	...
9						Append(self);
10						...
11					}	
12			};
13			function Append(Thread);
14	};

Using this high level task model, one can develop well-structured tasks as shown in the following example:

15	class BallFactory is Task
16	{
17		private:
18			ball_storage: Ball[];
19		public:
20			class Produce is Thread
21			{
22				public:
23					enter (produce_speed: int) { ... };
24			};
25			class Consume: public Thread
26			{
27				public:
28					enter (consume_speed: int) { ... };;
29			};
30	};

Member classes Produce and Consume are specific threads derived from Thread. They are members of BallFactory and can be used in the same way as an C++ member function is used:

31		factory: BallFactory(); // a ball factory is created
32		factory.Produce(5); // a produce thread is created with a rate 5 balls per sec.
33		factory.Produce(5); // another produce thread is created with a rate 5 balls per sec.
34		factory.Produce(10); // a consume thread is created with a rate 10 balls per sec.

In C++, a class may also be defined within another class. This class is called a nested class. The C++ nested class is a pseudo member class because it cannot use any members except for type names and static names declared in its enclosing class. It is not a real member because it cannot be used in the same way as other members (e.g. member functions) are used.

Therefore, the above task model cannot be written simply in C++. The nested class Thread has no access to member functions defined in Task. As result, a task reference must be provided explicitly when a thread is created:

1	class Task
2	{
3		public:
4			class Thread
5			{
6				public:
7					Thread (Task* task)
8					{	...
9						task->Append(this);
10						...
11					}	
12			};
13		...
14	};
15		
16	 class BallFactory: public Task
17	{
18		public:
19			class Produce: public Task::Thread
20			{
21				public:
22					Produce (BallFactory* factory, int produce_speed):
23						Task::Thread(factory)
24					{
25						...
26					}	
27			};
28		...
29	};
30	
31		BallFactory factory;
32		BallFactory::Produce(factory, 5);
33		...

Nested classes are only syntax sugar in C++ and they are actually treated as global classes. Note that at line 19, the base class Thread must be qualified by the class name Task as if it were not inherited. At line 23, the thread's constructor must be called explicily c++ while Transframe provides automatic constructors that subclasses are not required to call it explicitly. This inheritance pattern in Transframe is often used by high-level model design, for examples: distributed server with remote services, active object with communication ports, and databases with transactions.

C++ nested classes does not support overriding as discussed in the nest section. Java does not support nested classes.

8.6 Member Class' Constructor Overriding

The Transframe's overriding mechanism is more general than C++ and Java. In general, the implementation of a member class (not necessarily to be limited to member functions) can be overridden. Consider the following example:

1	class ListWindow is Window (name: char[])
2	{
3		class Item is Window(name: char[])
4		{
5			enter (name:char[]): Window (name, ListWindow::self) {};
6		};
7	};

where Item is a member class of ListWindow. Now, we consider a subclass:

1	class IconList is ListWindow (char[])
2	{
3		class Item is Window(name: char[])
4		{
5			enter (name: char[]): Window (name, IconList::self)
6			{
7				display (DefaultIcon(name));
8			};
9		};
10	};

where the member class Item's constructor body with the interface (char[]) has been overridden. Consider the following usage:

1	function CreateListWindow
2		(LWT:type of ListWindow; wn:char[]; name_list:char[]...):LWT;
3	{
4		list_window: LWT(wn); // create a parent list window
5		foreach (name in name_list)
6			list_window.Item(name); // create a child window
7		return list_window;
8	}

If we have a function call:

1	CreateListWindow(IconList,"File List","a.txt","b.jpg","c.ps");
The actual constructor (enter body) executed for the object instantiation by the expression list_window.Item(name) is the one defined in the class IconList. As a result, three items will be created as child windows with their default icons displayed.

When another specific list window (subclass of list window) is developed later, say, BrowserList, the generic function CreateListWindow can be used without any modification or recompilation:

1	CreateListWindow(BrowserList,"File List","a.txt","b.jpg","c.ps");

8.7 Information Hiding

A con against inheritance is the that inheritance exposes the information to the users who does not have the right to use them, which violates the principle of information hiding, and increases the complexity of dependency among software components.

Though C++ and Java provide accessibility control such as private and protect to prohibit unauthorized users from using the private or protected features, it is still an annoying fact that each class must present a complete specification in order to be inherited by subclasses, though most of the specification may be useless to users.

This defect not only complicates the interface of class with a bunch of useless information, but also adds unecessary dependencies that makes plug-ins or dynamic software components impossible. It also adds a heavy burden making the support of dynamic programming (scripting and dynamic design) efficiently imppossible: the dynamic programming environment must load a complete semantic description for all existing classes in use.

Transframe's interface mix-in plus implementation delegation forms a perfect information hiding mechanism. The interface defines only the methods that are publically accessible. The class that implements the interface class are not necessarily to be fully defined before it can be used in delegation (In C++ and Java, a class must be fully defined before it can be used in inheritance.)

Back to the Observer pattern. The implementation of Observable can be hidden in a separate package and never exposed to users. What follows is the interface of the Observer pattern presented in a Transframe's definition package:

     def object ObservePattern  // a definition package (singleton)
     {
	 interface class Observer
 	 {
 	      function Update();
 	 }
 	
 	 interface class Observable
 	 {
 	      function Register(Observer);
  	      function Notify();
 	 }
 	
 	 Observable ObservableSingle ();
 	 Observable ObservableMultiple (number_of_observers: int);
     }	 

Within the pattern, two implementations of Observable are provided: ObservableSingle is an implementation that supports only one observer; and ObservableMultiple is a subclass of Observable that can be observed by more than one observers. The actual implementations will be presented in a separate implementation package:

     imp object ObservePattern  // a implementation module (singleton)
     {
	 class ObservableSingle is Observable ()
 	 {
 	      private:	myObserver: Observer = null;
 	      public:	function Register(x: Observer) { myObserver:=x; };
 			function Notify(){ if (myObserver) myObserver.Update(); };
 	 }
	 class ObservableMultiple is Observable (number_of_observers: int)
 	 {
 	      private:	myObservers: Observer...;
 	      public:	function Register(x: Observer) { myObservers.Add(x); };
 			function Notify(){ foreach (x in myObservers) x.Update(); };
			enter (number_of_observers: int)
			{
			      myObservers := (Observer...number_of_observers)();
			}
 	 }
     }	 

Users are presented with the definition package only. They can select one of the implementations for a subclass construction without knowing the detail of the implementation:

    with ObservePattern use all;
    object MyPackage
    {
	class Clock
	{
	      private:	myTime: Time;
	      public:	function Tick() { myTime++; };
	}
	
	class ObservableClock is Clock, Observable by ObservableMultiple
	{
	      public:	function Tick{} { Clock::Tick(); Notify(); };
			enter (): ObservableMultiple(5) {};
	}
    }

Applications are not subjected to change by the modification of the implementation package. This provides a good mechanism for plug-in software components.

8.8 Summary

The following diagram illustrates the difference in the concept of inheritance among languages of Transframe, Java, and C++.

Transframe Java C++
Information Hiding genuine pretended pretended
Member Override overridable by default overridable by default overridable only when specified
Member Unimplemented deferred member class abstract method pure virtual
Multiple Inheritance simple simple base duplication conflict
Mix-ins interface & implementation mix-ins interface mix-ins only interface & implementation mix-ins
Member Class Inheritance true member class inheritance pseudo nested class inheritance
Member Class Override supported

Chapter 9. Aggregate Types


9.1 The Concept of Array

All three languages provide array constructs to group a number of objects which can be accessed via an integer index. C++ array is a hybrid concept. Array type is not a class, and arrays are not objects. Java tries to integrate the array type into the class hierarchy, but the try is not very successful.