Skip to main content

Visual J# Design Choices: A Conversation with Pratap Lakshman

On 26 September, Varun Gupta from the Visual J# team met with Pratap Lakshman (Lead Program Manager of the Visual J# team) in his office at Microsoft Hyderabad. In this interview Varun and Pratap discuss some of the design choices Visual J# made.

Varun: In Visual J# 2005 there are several enhancements to the language. What was the philosophy behind these additions?
Pratap
: We looked at 3 aspects: provide language support for common idioms; surface advanced features of the CLR through the language; make it easier to write better and correct code.

Varun: What was the approach taken when making these enhancements?
Pratap
: We tried to make these enhancements without having to sacrifice on performance, or introducing any syntax unfamiliar to the Java-language programmer. In general, every time you add a feature to a language, you increase the total number of text strings that are correct programs. It's important to keep the correct programs at some distance from each other, so that mistakes are less likely to turn one correct program into another correct program that isn't the one you wanted.

Varun: I would like to specifically talk about some of the design decisions made. What is the reason for not allowing methods to be declared on enum types?
Pratap
: We had long discussions on this during the design phase. Ultimately it came down to a question of performance vs. functionality. By limiting enums as we did, we can make them identical to their underlying types as far as all the underlying implementation of the Execution Engine is concerned. If they had methods on them they’d have to have method tables and so forth, and that makes them considerably heavier in implementation costs.

Varun: Although Visual J# supports defining first class properties, it relies on the method call syntax to access properties. Why is that?
Pratap
: Properties represent logical data and should be defined to mimic conventional usage.

Languages like C# do provide convenient syntax to access properties. Properties must not be chosen purely for their lexical appeal, however, as the notational convenience comes at a cost, and the expense of accessing a property must not outweigh this notational convenience. That every used occurrence of a property name in a source program is encoded as a conventional call to the associated method in IL will not be apparent to the casual reader of source code, leading to subtle problems that are difficult to discover. It is for this reason that Visual J# does not provide any special syntax for accessing properties.

Varun: In order that a method receive parameters passed by reference, the /** @ref */ needs to be applied to the parameters in the method signature only. Why is that?
Pratap
: Right from the very first release of Visual J#, we have had the capability to "consume" methods that take parameters by reference. For example, you could have written a CLS-compliant class with a method that accepts parameters by reference in C#, and called it from Visual J# without having to decorate the parameters at the call site. This never led to ambiguity because if the C# class had overload/override methods that differed only in parameters being passed by reference, such a class would not be CLS-Compliant and

CLS-consumers (like Visual J#, at the time) would not be required to be able to consume such classes - hence we were able to retain the familiar method call syntax.

When we added the capability to "author" methods that take parameters by reference, we decided that we would continue to use this familiar method call syntax as we wanted to minimize the impact on existing code. We also made sure that in Visual J#, it shall be a compile time error for a class to overload/override methods that differ only in the parameters being passed by reference. If we permit overriding there is no way for us to generate the right code when referring to some derived type through a base type's reference. The only way out would be to disambiguate at the call site too - and we decided not to do that. Such a class would, in any case, not be CLS Compliant too.

Note that such method call syntax is not unprecedented. C++ itself permits you to call a method that takes parameters by reference without having to decorate them at the call site. I must also point out that when using tools like Visual Studio, such annotations are presented through Intellisense, and the Object Browser.

Varun: What kind of variables can be passed by reference?
Pratap
: Primitive types, value types and reference types can be passed by reference. However there are a couple of restrictions: only an lvalue can be passed by reference, and the type of the actual parameter must exactly match the type of the formal parameter on the method.

Varun: Where do you see the need for value types?
Pratap
: Value Types make it possible to author “Light weight classes”. This enables scenarios where you want to use objects similar to primitive types, briefly and in a single location, and you do not want the overhead of such objects hanging around and wait for garbage collection thus adding overhead to the program. These would be useful to represent complex numbers, points, transforms, ranges, etc. - abstractions that support value semantics.

 

Varun: How are the semantics any different from those of conventional classes?
Pratap
: The semantics differ in several ways:

Unlike classes, instances of value types do not require heap allocation; a variable of a value type directly contains the data of the type, whereas a variable of a class type contains a reference to the data (the object); the semantics of '==' are different; casting from/to a value type does boxing/unboxing as appropriate automatically; they cannot be compared to or assigned 'null'.

Varun: Your examples for the use of value types are from the mathematical domain, and yet, Visual J# does not do anything special in the case of strictfp. Why is that?
Pratap
: We don't support strictfp semantics because it would make FP calculations too slow. In general, customers seem not to actually care about that much FP calculations in terms of exact reproducibility, but do care about performance. Rather than doing FP calculations in "strict mode" we do them in "default mode". In practice, most FP calculations done in the so called "default mode" often yield the same result as "strict mode".

Varun: Could you elaborate on this?
Pratap
: strictfp is a keyword introduced with the purpose of linguistically legislating strict reproducibility of FP calculations across VMs. As part of this, Java calls out exactly how many bits are provided in the mantissa and exponent of a "float" and a "double", and specifying exactly how +-*/ work. In practice, this arithmetic specification is not always a perfect match for the FP provided by the hardware. For e.g.:

(1) Signal processors that may provide float and/or float-extended but not double

(2) RISC-based computers that provide 4-byte float and 8-byte double but nothing wider.

(3) Power-PC; MIPS R-10000; H-P 8000: same as (2) with fused multiply-add operation.

(4) Intel x86, Pentium; clones by AMD and Cyrix; Intel 80960KB; new Intel/HP IA-64; and Motorola 680x0 and 88110: the same as (2) plus a 10+-byte long double type.

It is tricky and very slow to do it exactly right on Intel. It was even slower on PowerPC. So for most of these hardware the implementation of strictfp was relaxed to take advantage of the FP hardware anyway. Also, as I just mentioned, in practice, most FP calculations done in the so called "default mode" often yield the same result as "strict mode". Therefore we chose to do all FP calculations in “default” mode.

Varun: Where do you see the need for custom attributes?
Pratap
: The custom attribute facility permits programmers to invent new kinds of declarative information - keywords, if you will - and attach them to various program entities. These can be retrieved at runtime through reflection. Custom attributes make it possible to do powerful data-driven programming in conjunction with various tools, and the runtime itself. In the past you would have already seen attributes being used for serialization, and for specifying the threading model, etc. Custom attributes just generalizes the mechanism for users to attach their own metadata.

Varun: Can you provide an overview of the need for a memory model?
Pratap
: In a programming language, what does it mean to refer to a variable?

For a conventional sequential program, the meaning is relatively simple. Computer systems architects have designed elaborate hardware structures, such as registers, caches, and out-of-order instruction execution, as well as software structures such as optimizing compilers, that deliver greatly improved performance while maintaining the illusion that the behavior of memory locations is simple.

But with the advent of parallel processors with multithreaded programming, the illusion of simplicity has been shattered. If memory is shared, the elaborate under-the-table cleverness that accelerates one thread may be observed by other threads, resulting in program behavior that can astonish the programmer.

It is not adequate for a shared-memory language specification merely to say, as many do, that a parallel program consists of many sequential threads, each behaving as specified by the standard specification for a sequential language. Inappropriate memory behavior can result in violations of a language's type system or in reading values from a variable that were never written.

A memory model specification seeks to find a model of memory behavior that imposes constraints tight enough for programs to have "reasonable" behavior while also loose enough that the by now conventional techniques for acceleration of execution are not prohibited.

Varun: What is the Visual J# memory model?
Pratap
: Visual J# surfaces the CLR memory model as called out in §12.6.7 of the ECMA-335: CLI Partition I - Architecture document.

At a very high level, here are the rules of the memory model: reads and writes from the same processor to the same location cannot cross one another; no memory read (volatile or regular to any memory location), can move before a lock acquire; no memory write (volatile or regular to any memory location), can move after a lock release; all other reads and writes can reorder arbitrarily.

Varun: Visual J# 2005 supports consuming generics. How would you compare .NET Generics to Java Generics?
Pratap
: I don’t want to compare as the design goals were different. On .NET, Generics are just classes, except that they also have a type parameter, and what you get at runtime is a faithful representation of what you had at compile time. Java Generics on the other hand had the goal of retaining backward compatibility as well as migration compatibility with their existing code base.

Varun: In the next version of Visual J#, what are the features you wish to see?
Pratap
: Actually, I would like that question answered by our users. I want our users to tell what they would like to see. We will then design and build the tools to make that happen.