Designing a new general-purpose language

Arithmetic if revisited

Fortran has an interesting (and somewhat unusual) construct known as "arithmetic if".   It looks something like this:

IF (arithmetic expression) label1, label2, label3

If the arithmetic expression evaluates to less than zero, control jumps to label1, equal to zero to label2, and greater than zero to label3.  This construct is most often used to compare two numbers, in which case the arithmetic expression is of the form (x-y).

Arithmetic if fell out of favor with the advent of structured programming and is hardly ever used now.  Since it is a disguised goto, all of the arguments against goto apply to arithmetic if as well.  In addition, the syntax is considered by many modern programmers to be one of Fortran's particularly arcane constructs.

I don't think there is anything wrong with the idea of arithmetic if, however.  Think of how often you write something like the following:

if (x < y) { ... } else if (x == y) { ... } else { ... }

This has the advantage of being structured and of using standard control flow constructs, but I don't think it is any more readable than the old arithmetic if.  Here are some issues:

  1. Without reading the complex statement in its entirety, it is not clear that this is the equivalent of an arithmetic if.
  2. From a readability standpoint, there isn't a clear way to handle the final else statement.  Consider for the moment integer comparisons, where <, ==, and > cover all the bases.  You could write the final else as written above, in which case it stands out as different from the others in not having an if comparison (and because of this, you have to read the entire construct to know that this handles the > case).  Or you could write the final else as "else if (x >y)", which at first glance may appear to be letting something slip through the cracks, as there is no final unqualified else.
  3. For floating point comparisons, these two solutions are not equivalent.

Besides readability, there are a couple other problems:

  1. There isn't a simple way to verify that there are no overlapping cases or missing cases.  This concern is biggest for floating point comparisons, where there exist several not mutually exclusive comparison predicates.
  2. This construct is particularly well suited to optimization.  However, as an if...else if...else construct, the optimizer needs to look harder to find it.

I want to resurrect arithmetic if in a more readable form.  The most reasonable approach seems to be a special type of switch statement:

switch (x ? y)

{

case <:

  ...

case ==:

  ...

case >:

  ...

}

(I apologize for the spacing; it appears this blog software is not very friendly to code)

This approach has the additional benefit of being able to take advantage of the default case policy of the language (I personally like D's implicit assert on default for missing default statements).

This construct could be more flexible than the example I have given above.  There is no reason, for example, that there couldn't be "case <=", "case !=", "case unordered", etc., as long as the cases don't overlap.

Published Friday, May 19, 2006 9:15 PM by hartwil

Comments

 

jachymko said:

Is that really neccessary? I'd rather implement a solid pattern matching than trying to teach the old switch new tricks. and the arithmetic if can easily be replaced with a CompareTo call, at least in .net..

swtich(x.CompareTo(y)) {
-1 { .... }
0 { ... }
1 { .... }
}
May 26, 2006 11:01 AM
 

hartwil said:

First of all, your solution doesn't always work in .NET.  The description of the CompareTo method says it returns an int that is less than zero if x < y, zero if x == y, and greater than zero if x > y.  Unless you know for certain that the implementation of the CompareTo method you are calling returns -1, 0, or 1, you shouldn't make that assumption (and for maintainability, it's probably not a good idea to do this anyway).
I admit that this problem is not insurmountable.  For example, you could define the CompareTo method to, instead of returning an int, return an enum with the possible values LESS_THAN, EQUAL_TO, GREATER_THAN, or something of the like.  You could even capture floating point comparison semantics fairly easily, and given a few other language features, make this fit nicely into an OO style.  For example, you could have an interface PartialOrdered that defines a CompareTo method, and an interface Ordered that extends PartialOrdered.  Floating point classes would implement PartialOrdered and integer classes would define Ordered, for example.  If the language has built-in design by contract, you could specify that Ordered's CompareTo never returns UNORDERED.  There is perhaps an even better solution, which requires a "restricted" style of inheritance (as "restrict" in XML Schema, but also somewhat similar to bounded types in Ada), and convariant return types: PartialOrdered's CompareTo returns the enum PartialOrdering and Ordered returns the enum Ordering, where Ordering restricts PartialOrdering.  Throw generics and or implicit casts into the mix and you have a solution that covers comparing floating point with integers.  This is to me an elegant and attractive solution, and the features that it relies upon are all reasonable things to put into a modern language.
However, to answer your question, no, it is not necessary, but it seems to me to be desirable.  First, in pseudocode, you often see things like:
case 1: x < y
case 2: x > y
...
Not that real code should always be written like pseudocode, but in many cases I think it is a good heuristic for readability.  Second, since this is a very common type of control structure (in whatever form it takes), I don't see any reason why it can't have it's own syntax.  Third, although the two solutions are semantically equivalent, there is a trade-off involved.  Although the standard switch statement solution has the advantage of not adding anything new to the syntax of the language, and thus makes that part of the compiler simpler, it creates more work for the optimizer if you wish to output the same code.  That is of course, unless the intermediate code generation treats a switch on a comparison result for primitive types differently than other switches, but if this is case, why not reflect this in the original language?  I can't make any solid claims about which would compile faster in the general case (my experience writing compilers is very limited), but it seems reasonable to design a language such that the front end does as much of the work as possible.
June 2, 2006 6:49 PM
Anonymous comments are disabled

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker