Experimentation Stage

Casting

Lense does not support an explicit cast operator like C or Java do. Lense supports flow sensitive casting instead. This means the compiler can use flow directives like if or switch to understand the type of the variable.

public collectLeafs( root : Node , leafs : List<Node>) {

	if ( obj is LeafNode){
	   leafs.add(obj);
	} else if (obj is BranchNode){

		// obj.children is visible because the compiler knows obj is a BranchNode
		for (var child in obj.children){
			collectLeafs(child, leafs);
		}
	} else {
		throw new Exception("Unrecognised node");
	}

}

The is operator returns true if the variable holds an object of the specified type. The compiler then knows that all references to that variable inside the if really refer to that type, and so all operations on that type are allowed.

Flow sensitive typing simplifies writing the check and cast idiom, very common when overriding equals. In other languages, like Java, you need to test for type and then cast:

// java
public class Person {

	public long id;

	public equals(Object other){
		return (other instanceof Person) && ((Person)other).Id == this.Id;
	}

}

in Lense the compiler is smart to understand that if tested, then the variable is of that type.

public class Person {

	public Id : Natural;

	public equals(other : Any){
		return other is Person && other.Id == this.Id;
	}

}

You can also use other flow directives like switch :

public collectLeafs( root : Node , leafs : List<Node>) {

	switch(obj){
		case is LeafNode {
		    leafs.add(obj);
		}
		case is BranchNode {
			for (var child in obj.children){
				collectLeafs(child, leafs);
			}
		}
		default{
		 	throw new Exception("Unrecognised node");
		}
	}
}

and assert:

var node : Node  = ... // some node

assert( node is BranchNode);

return node.children.size; // at this line the compiler knows node is a BranchNode because otherwise and exception would have been thrown earlier.