Tim Jansen's blog


2004/02/22
Playing with the Switch and Foreach Statements
Playing with the Switch and Foreach Statements I’ve been thinking about C’s control statements (if, while, switch, for etc) for a little while, and I think there’s some room for improvements in the later two… switch The C# guys have made two interesting modifications to C’s switch statement: fall-through is only allowed when there’s no statement after the case (thus if a statement follows a case, there must be a break or similar jump statement). And it added the ‘goto case’ statement that can be used for fall-through effects. Here’s a C# snippet:
	void test(int a) {
		switch (a) {
		case 0:
			Console.WriteLine("blabla");
			break;
		case 1:
			Console.Write("More ");
			goto case 0;
		case 2:
		case 3:
			Console.WriteLine("lalala");
			break;
		}
	}
I think forbidding implicit fall-through is a good idea, it avoids a common source of error. But if you forbid it, why is it still necessary to write the redundant break statements for each case? Especially long switch statements look much better without them:
	void test(int a) {
		switch (a) {
		case 0:
			Console.WriteLine("blabla");
		case 1:
			Console.WriteLine("More");
			goto case 0;
		case 2:
		case 3:
			Console.WriteLine("lalala");
		}
	}
Something that I am missing all the time is a way to describe value ranges in a switch statement. The constraint syntax from my last entry offers a neat solution for it:
	void test(int a) {
		switch (a) {
		case 0, <=100:
			System.out.println("a is between 1 and 100");
		case <200:
		case >500, <600:
			System.out.println("a is >100 and <200, or >500 and <600");
		default:
			System.out.println("a has some other value");
		}
	}
Allowing comma-separated lists of operator plus constant makes switch much more powerful for many purposes. foreach Over the last years most C-based languages got a foreach statement to iterate over collections and arrays, but every language got its own syntax. This is what it looks like in C#:
	int sum = 0;
	foreach (int i in someArray)
		sum += i;
… and ECMAScript (JavaScript):
	var sum = 0;
	for (var i in someArray)
		sum += i;
… and Java 1.5:
	int sum = 0;
	for (int i: someArray)
		sum += i;
I definitely prefer Java’s syntax as ‘for’ is shorter than ‘foreach’ (without reducing readability) and I don’t like making in a keyword.
Unfortunately neither Java nor C# exploit their foreach as a nicer alternative for regular for loops. The common C pattern
	for (int i = 0; i < 100; i++)
		System.out.println("Bla");
can be expressed with a foreach loop and a special object that provides a sequence of integers, similar to Python’s range() function:
	for (int i: new Range(100))
		System.out.println("Bla");
It’s looking nicer without the new keyword, as shown here:
	for (int i: Range(100))
		System.out.println("Bla");
Using other constructors it would be possible to specify at start value, to go backwards or use bigger steps:
	for (int i: Range(50, 10, -1))
		System.out.println("i=" + i);
As for loops are pretty common, it may make sense to have a special operator for simple ranges, like Ruby does and Project Alpha proposes:
	for (int i: 0...99)
		System.out.println("i=" + i);
The expression ‘a…b’ is short for ‘(a<=b)?Range(a, b+1):Range(b, a-1, -1)’. Ranges can also be used for other purposes, for instance as function arguments. With Range and an overloaded indexer iterating through the first ten members of a list would be as easy as:
	void print10Numbers(List numberList) {
		for (int i: numberList[0...9])
			System.out.println("i=" + i);
	}
Unlike regular methods for slicing collections such as Java’s List.subList() this syntax gives more flexibility. You could get the first ten numbers in reverse order (numberList[9…0]), skip every second number (numberList[Range(0, numberList.size(), 2)]) and so on.



2004/02/06
Argument Constraints in the Function Declaration
Argument Constraints in the Function Declaration The most annoying thing when writing public APIs is that it’s a lot of redundant typing. A good API implementation should check all arguments and return a useful error message if an argument is invalid. Good API documentation should document which arguments values are valid and which return values are possible. Both tasks are work-intensive and annoying for the developer, which is probably one of the reasons why there so many bad APIs. Here is a Java snippet with Sun-style argument handling:
/**
 * Sets the discount as percentage.
 * @param percentage the discount as percentage
 * @throws IllegalArgumentException if percentage < 0 or percentage > 100
 */
void setDiscountPercentage(float percentage) {
	if ((percentage < 0.0) || (percentage > 100.0))
		throw new InvalidArgumentException("percentage must be >=0 and <=100");
	mPercentage = percentage;
}


/**
 * Gets the discount as percentage.
 * @return the discount as percentage. Guaranteed to be positive and not
 *         higher than 100.0
 */
float getDiscountPercentage() {
	assert (mPercentage >= 0.0) && (mPercentage <= 100.0);
	return mPercentage;
}
So is it possible to remove this redundancy and specify the constraints only once? I think so. The obvious solution is to add a list of assertions to each argument. This is an attempt at a very compact syntax:
void setDiscountPercentage(float percentage(>=0.0,<=100.0)) {
	mPercentage = percentage;
}

float(>=0.0,<=100.0) getDiscountPercentage() {
	return mPercentage;
}
Each argument is followed by a list of comma-separated constraints that can be checked before executing the function. To make the syntax more crisp it is allowed to start the expressions directly with a comparison operator. They will then be executed with the function argument as first operand. It’s also possible to use normal boolean expressions, for instance to check the length of a string:
void setId(String id(!=null, id.length() > 0, id.length() <= 10)) {
	//...
}
The syntax looks pretty cryptic though, because the declaration becomes very long and the nesting of parentheses is confusing. An alternative would be to declare the assertions after the normal signature:
void setDiscountPercentage(float percentage) 
	with percentage: >=0.0, <=100.0 {
	mPercentage = percentage;
}

(float percentage) getDiscountPercentage() 
	with percentage: >=0.0, <=100.0 {
	return mPercentage;
}

void setId(String id) 
	with id: !=null, id.length() > 0, id.length() <= 10 {
	// ...
}
It’s a little bit more verbose, and for return value constraints this syntax requires named return values, but I think it is much more readable than the first attempt. Here an example with more arguments and a return value tuple:
(int a, int b, int c) create3PositiveNumbers(int x)
        with a: >0
	with b: >0
	with c: >0
	with x: >0 {
	return (x, x, x);
}
I think it’s quite nice. A few random things:
  • The constraints need to be shown in the API, so they must use only public symbols. Otherwise the user would not be able to understand them. The compiler should enforce this.
  • If a reference argument is null, any operation on it will fail. But quite often null is allowed as an argument, which would make the constraints quite ugly. Imagine a setId() variant that allow nulls:
    void setIdAllowsNull(String id) 
    	with id: (id==null) || (id.length() > 0), (id==null) || (id.length() <= 10) {
    	// ...
    }
    
    An easy solution is the following rule: if an argument is a reference value and it is null, it always passes all tests, unless the first constraint is “!=null”. With this rule the constraints can be simplified to
    void setIdAllowsNull(String id) 
    	with id: id.length() > 0, id.length() <= 10 {
    	// ...
    }
    
  • Overridden virtual functions always inherit the constraints and can not add any new constraints (or even change the original ones)
  • Just like regular assert statements it should be possible to disable the argument checks if performance is critical



 

This blog is my dumping ground for thoughts and ideas about Eek. Someday Eek will be a programming language and system, somewhat comparable to Java in scope. It is my attempt to bring sanity to the world of computing.
At least I hope so. Right now it is far from being finished and I can't guarantee that it ever will be. I am still working on the specification, but I won't release anything before I got my first prototype running. The world does not need more vapourware and unusable beta-software. All publicly available information about Eek is contained in this blog. You can find the latest summary here.
This page is powered by Blogger. Isn't yours? Creative Commons License
This work is licensed under a Creative Commons License.