Channels ▼

Walter Bright

Dr. Dobb's Bloggers

In A Module Far, Far Away Part 1

July 08, 2009

In working on the design of the D programming language, a topic that fascinates me is how the language can help a user find and eliminate programming bugs. The tug of war is between the programmer adding in loads of explicit annotations that can be checked by the compiler, and the higher productive method of throwing the code and relying on runtime testing to sort out problems. If there's too much of the former, the language gets characterized as "bondage and discipline" and will only be used if one's contract requires it, and if too little then an awful lot of time is lost in debugging.

My job as a language designer is to find ways for the language to help without requiring B & D annotations, or at least to approach that sweet spot in trading off between those goalposts.

Here we'll talk about what happens when there's a declaration of X in module A, and X is used far, far away in module B, and then the declaration of X is changed. What happens in B? In order of preference, these can happen:

1. B adapts to the changes and works correctly
2. The compiler complains when compiling B that changes need to be made
3. The program fails in B at runtime with a reasonable message
4. The program crashes
5. The program launches nuclear missiles

The further up that hierarchy we can push things, the better off things are (better meaning higher productivity). Relying on manual code reviews isn't a great solution, because A and B will likely be reviewed independently, missing the dependency between them. (I call this kind of issue a non-local bug, as opposed to a local bug which is entirely contained in one module.)

To illustrate with a simple C example, suppose X is an array declaration in module A:

 float X[10];

and in module B we have the loop:

 for (int i = 0; i < 10; i++) {
    float s = X[i];
    ...
 }

Later, the declaration in A is changed to:

 float X[5];

and now B fails at runtime. The solution is:

 for (int i = 0; i < sizeof(X)/sizeof(X[0]); i++) {
    float s = X[i];
    ...
 }

and B seamlessly adapts to any changes in the length of the array. But now let's change the type of X as:

 double X[10];

In B, the declaration:

 float s = X[i];

causes an implicit conversion from double to float, which may produce quite unintended results (after all, the type was changed from float to double for a reason). In C, we can deal with this with a typedef:

 typedef float X_element_t;
 X_element_t X[10];

 for (int i = 0; i < sizeof(X)/sizeof(X[0]); i++) {
    X_element_t s = X[i];
    ...
 }

This works, but it relies on the programmer to have the discipline to use the convention consistently, and it's extra work. The compiler really isn't very helpful here. In D, we can write it as:

 float X[10];

 for (int i = 0; i < X.length; i++) {
    auto s = X[i];
    ...
 }

The auto declaration tells the compiler to infer the type from the type of the initializer expression. (The dimension of the X array is picked up by the convenient .length property.)

So far, our module B is doing a lovely job of adapting to changes in the declaration of X. But wait! What if a function is called in module B?

 void foo(float e) { ... }
 ...
 foo(X[i]);

We're back to that doggone conversion to float. Can we get the parameter type to be inferred from the argument type as well? Sure, make the type a parameter:

 void foo(T)(T e) { ... }

Functions can have two parameter lists, the first is the types which are inferred from the argument types supplied to the parameters in the second list.

Where else can we infer types (and hence adapt to changes) rather than specify them?

Where ever a type is needed, a type can be inferred from the type of any expression using the typeof construct:

 short x,y;
 typeof(x + y) f; // f is of type int

(Due to type conversions in expressions, the type of an addition may be different from the type of either of its operands.)

The type of a function return can be accessed:

int foo()
{
    typeof(return) e;  // e is of type int
}

Even the return type of a function can be inferred:

auto foo()
{
    return 3.0;  // return type of foo() is double
}

These capabilities of D go a long way to reducing the errors in module B if the declarations in A change. In part 2 of this, we'll look at some more features along these lines.

If you want to learn more about how real compilers work, I am hosting a seminar in the fall on compiler construction.

Thanks to David Held, Bartosz Milewski, and Jason House for reviewing this.

 

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 


Video