Friday, April 07, 2006

Inferred Types (Kontextsensitiv)

Bei statisch getypten Sprachen, ist die Syntax ja oft durch Typinformationen aufgebläht.
In Java schreibt man z.B.

public String sayHello(String name) {
String hello = "Hello ";
return hello+name;
}

In C# gibt es neuerdings (3.0) sog. inferred Types (abgeleitete Typen). Da sieht obiges Beispiel dann so aus:

public String sayHello(String name) {
var hello = "Hello ";
return hello+name;
}

Hilft nicht soo viel, oder?
(C# 3.0 macht übrigens in anderen Bereichen (Lambda Expressions, Anonymous Types) wirklich coole type inference, dazu aber ein anderes mal mehr)
Dem Compiler würde eigentlich folgendes reichen, um die selben Informationen zu bekommen:

public sayHello(String name) {
hello = "Hello ";
return hello+name;
}

Die Typinformationen könnten aus den Ausdrücken abgeleitet werden:
- Ein Stringliteral ("Hello ") ist vom typ String
- eine String concatenation (hello+name) ist vom typ String

Die openArchitectureWare4 Sprachen (Xpand, Extend, Check) sind ebenfalls statisch getypt.
Bei der Entwicklung dieser Sprachen hab ich aber versucht eine möglichst wenig geschwätzige Syntax zu finden.
Obiges Beispiel als Extension (das sind im Prinzip statische Funktionen) sähe z.B. so aus:

sayHello(String name) :
"Hello "+name;

Der Rückgabewert wird aus der Expression abgeleitet. Das ist erstmal nichts ungewöhnliches.
Extend geht hier aber noch einen Schritt weiter, und zwar wird der Typ kontextsensitiv abgeleitet.
Beispiel:

singletonList(Object o) :
{o};

Es wird eine Liste erzeugt, die das übergebene Objekt enthält und zurückgegeben.
Der Typ wäre hier normalerweise List[Object] (in Java Syntax: List<Object>).
Tatsächlich hängt es aber eben vom tatsächlichen Parametertyp ab:

doStuff() :
singletonList("Test"); // ReturnTyp ist List[String]

doStuff1() :
singletonList(4711); // ReturnTyp ist List[Integer]

doStuff2() :
singletonList(true); // ReturnTyp ist List[Boolean]

Das ist wirklich sehr nützlich. Ein weiters Beispiel:

slice(List l, Integer start,Integer end) :
l.select(e|l.indexOf(e)>=start && l.indexOf(e)<=end);

Mit der obigen Extension kann ich subListen bekommen, und verliere nicht die statische Typinformationen:


{1,2,3}.slice(1,3) // statischer returnType ist List[Integer]
{"1","2","3"}.slice(1,3) // statischer returnType ist List[String]


Das Problem könnte übrigen auch mit Generics gelöst werden.
In den oAW Expressions sind aber nur Collections (sind first-class Konzepte der Sprache) mit Typen parametrisierbar.