F# - funktionales Pendant zu C#

F# ist im Gegensatz zu C# eine funktionale Sprache. Was zunächst als Spezialsprache für mathematische Algorithmen erscheint, erweist sich schnell als flexible und gut geeignete Sprache für andere Anwendungsgebiete.

In Pocket speichern vorlesen Druckansicht
Lesezeit: 17 Min.
Von
  • Golo Roden
Inhaltsverzeichnis

F# ist im Gegensatz zu C# eine funktionale Sprache. Was zunächst als Spezialsprache für mathematische Algorithmen erscheint, erweist sich schnell als flexible und gut geeignete Sprache für andere Anwendungsgebiete.

Bei Programmiersprachen unterscheidet man prinzipiell zwischen Sprachen für einen speziellen Zweck und Sprachen zur Bewältigung jeglicher Aufgaben. Letztere sind als so genannte "General Purpose Languages" bekannt. Als bekannten Vertreter der ersten Sorte kann man beispielsweise Fortran heranziehen, das auf numerische Berechnungen spezialisiert ist. Das zeigt sich bereits im Namen, denn der Begriff stellt ursprünglich eine Zusammensetzung der Begriffe "Formula" und "Translation" dar.

Ein bekannter Vertreter des zweiten Sprachtyps ist Java, das genau genommen auf kein Anwendungsgebiet besonders spezialisiert ist. Zwar gibt es Aufgaben, die sich mit Java besser oder schlechter lösen lassen, grundsätzlich ist Java aber für jeden beliebigen Zweck einsetzbar. Auch C# zählt zur Gattung der "General Purpose Languages".

Seit Visual Studio 2010 gehört F# zu den in die IDE integrierten Sprachen.

Die Sprache F# gehört seit der Version 2010 zum festen Lieferumfang von Visual Studio. In der deutschen Wikipedia heißt esvlediglich, dass es sich bei F# um eine funktionale Sprache handele, die zusätzlich objektorientierte und imperative Konstrukte enthalte und eine gewisse Verwandtschaft zu den Sprachen OCaml und ML aufweise. Die englische Wikipedia gibt sich zwar auskunftsfreudiger, aber auch sie behandelt das Thema "General Purpose Language" nicht weiter.

Auffällig ist zunächst, dass F# in der Regel als Multiparadigmensprache gilt, die sowohl funktionale als auch objektorientierte und imperative Programmierung ermöglicht. Der Schwerpunkt liegt jedoch stets auf der funktionalen Programmierung. Da es sich bei den meisten objektorientierten Sprachen um "General Purpose Languages" handelt, stellt sich die Frage eher, wie es sich in der Hinsicht mit funktionalen Sprachen verhält. Zur Beantwortung ist es erforderlich, die Konzepte funktionaler Programmierung zu analysieren. Prinzipiell gibt es deren vier – nämlich Unveränderlichkeit, Typsicherheit, deklarative Entwicklung und
Rekursion.

In C# ist ein Typ standardmäßig veränderlich. Unveränderliche, so genannte "Immutable Types", sind eher die Ausnahme als die Regel. Das bekannteste Beispiel hierfür stellt die Klasse System.String dar:

string foo = "Hallo ";
string bar = "Welt!";

string foobar = String.Concat(foo, bar);

Auch Delegates sind unveränderlich, was in der Regel beim Thread-sicheren Auslösen von Ereignissen zum Tragen kommt:

public class Foo
{
public Action EventHandler Bar;

protected virtual void OnBar()
{
Action bar = this.Bar;
if(bar != null)
{
bar();
}
}
}

In der funktionalen Programmierung – und damit auch in F# – ist das genau umgekehrt: Typen sind per se unveränderlich, es sei denn, man markiert sie explizit als veränderlich. Das geschieht bei F# mit dem Schlüsselwort mutable:

let mutable x = 1
x <- x + 1

Das Vorgehen entspricht der Variante, Klassen in C# als reine Read-only-Klassen zu implementieren und das Setzen der Werte nur initial im Konstruktor zu erlauben.

public class Immutable
{
private readonly int _foo;
private readonly int _bar;

public Immutable(int foo, int bar)
{
this._foo = foo;
this._bar = bar;
}

public Immutable Multiply(int factor)
{
return
new Immutable(
this._foo * factor,
this._bar * factor);
}
}

Obwohl das Prinzip zunächst ungewohnt erscheint, schließlich ist für jede Veränderung eines Typs stets eine neue Instanz mit den neuen Werten zu erzeugen, bietet es einen gravierenden Vorteil: Da alle Methoden den Zustand des Objekts nach außen sichtbar ändern, können keine unbeabsichtigten Zustandsänderungen auftreten. Das Objekt lässt sich schlichtweg nicht manipulieren.

Der Vorteil zeigt sich beispielsweise in der Parallelprogrammierung, bei der gleichzeitige Zustandsänderungen eines Objekts aus Threads heraus leicht zu Data Races und ähnlichen Problemen führen können. In der funktionalen Programmierung ist das von vornherein ausgeschlossen.