Ein Blick auf F#

Funktionale Programmierung hat mich bereits während des Studiums beschäftigt, allerdings eher am Rande in Lehrveranstaltungen wie „Objektorientierte Konzepte“, wo in den Übungen Haskell gepredigt wurde. Teils verwirrt, teils fasziniert von dieser Art zu denken und zu programmieren hat mich das Thema nie richtig losgelassen und erlebt gerade einen persönlichen Aufschwung durch F#. F# ist Microsoft’s funktionale .NET Programmiersprache, die wie viele Innovationen Microsoft Research entsprungen ist. Es ist explizit hervorzuheben, dass F# das .NET-Framework als Basis nutzt. Demzufolge stehen auch alle Bibliotheken zur Verfügung, welche .NET so mächtig machen. Doch genug der Vorrede, los geht es mit F#. Ein kleiner Praxistest soll an dieser Stelle erste Eindrücke vermitteln…

Los geht’s…

Die aktuelle F# CTP kann auf der zugehörigen MSDN-Seite herunter geladen werden. Hier stehen auch weitere Ressourcen zur Verfügung, die beim Einstieg in die neue Programmiersprache behilflich sind. Flux installiert lässt sich im Visual Studio bei der Erstellung eines neuen Projekts der Punkt „Visual F#“ wählen. Die ersten Zeilen Code sind bekanntlich die schwersten in jeder Programmiersprache, und doch halfen mir bereits hier meine ersten Erfahrungen in Haskell. Der große Unterschied: F# ist keine rein funktionale Programmiersprache wie Haskell, sie enthält ebenfalls imperative Elemente, welche die Ausdruckfähigkeit an einigen Stellen vereinfachen sollen. Erste Schritte waren also schnell gemacht. Mit „let“ werden Variablen deklariert und definiert, wobei es sich hierbei auch um Funktionen handeln darf. „let twice x = 2*x“ erzeugt z.B. eine Funktion „twice“, welche die Signatur „int -> int“ besitzt und zu einem übergebenen Argument seinen zweifachen Wert zurückgibt. Diese Funktion darf wieder an weitere Funktionen übergeben werden, Funktionen höherer Ordnung sind also ohne Probleme möglich. F# inferiert dabei den Typ von Variablen, d.h. ihre Signatur wird aus den angegebenen Definitionen automatisch ermittelt und so z.B. auch bei der Fehlerprüfung verwendet. Als Beispiel für eine Signatur sei „List.map“ angegeben. Deren Signatur lautet: „val map: (‚a -> ‚b) -> ‚a list -> ‚b list“. What the heck? Eigentlich ist es ganz einfach. „->“ gibt jeweils die Abgrenzung zweier Parameter an. Der Ausdruck nach dem letzten „->“ stellt den Rückgabetyp dar. Nehmen wir C# als Hilfestellung und schauen uns die Methode „Math.Pow“ an, so hat diese in C# die Signatur „double Pow(double x, double y)“ und übersetzt auf unser Schema die Signatur „double -> double -> double“. Sie würde also 2 double-Werte als Argumente nehmen und einen double-Wert zurückliefern. Aber zurück zu List.map… (‚a -> ‚b) besagt: „das erste Argument der Funktion List.map ist eine Funktion, welche ein Element des Typs a entgegennimmt und ein Element des Typs b zurückliefert“. Das zweite Argument „‚a list“ besagt, dass eine Liste mit Elementen vom Typ a erwartet wird. Der Rückgabetyp von List.map ist ‚b list, was als letzter Term der Signatur ausgewiesen ist. Was macht List.map jetzt eigentlich? Es „mapped“ Elemente einer übergebenen Liste mit Hilfe einer ebenfalls als Parameter übergebenen Funktion. Dazu wird auf jedes Listenelement des Typs a die Funktion angewendet, welche ein Element des Typs b zurückliefert. Am Ende ergibt dies eine Liste von Elementen des Typs b, welche zurückgegeben wird. Auch Lambda-Ausdrücke sind in F# kein Problem. „List.map (fun x -> 2*x) numbers“ würde z.B. von allen Elementen der Liste „numbers“ den doppelten Wert erzeugen und dies als Liste zurückliefern. Klar erkennbar sind bereits hier u.a. das funktionale Paradigma, Typsicherheit und Polymorphismus, auch wird bereits hier die Ausdrucksmächtigkeit erkennbar, wenn man bereit ist sich auf funktionale Programmierung einzulassen…

Quicksort in F#

Beim Stöbern bin ich auf eine F#-Implementierung des Sortieralgorithmus Quicksort gestoßen, welche ich euch nicht vorenthalten wollte:

let lt  x = List.filter (fun xi -> xi < x)
let geq x = List.filter (fun xi -> xi >= x)
 
let rec qs = function
             | []    -> []
             | x::xs -> (xs |> (lt x >> qs)) @ [x] @ (xs |> (geq x >> qs))

That’s it… Über z.B. „printfn „sorted: %A“ (qs [3;1;9;5;2;10;8;6;7;4])“ kann die Sortierung getestet werden. Zunächst werden zwei Funktionen „lt“ und „geq“ definiert, welche jeweils eine Element x übergeben bekommen. Sie liefern genau die Funktionen zurück, welche für einen weiteren übergebenen Wert xi berechnen, ob x kleiner oder größer gleich xi ist. „let rec qs …“ definiert eine rekursive Funktion qs, welche eine Liste als Argument übergeben bekommt. Mit „|“ wird der Wert der Liste geprüft und anhand dessen der Funktionswert bestimmt. Ist die Liste leer („[ ]“), so wird auch nur eine leere Liste zurückgeliefert. Ansonsten wird die Argumentliste mittels „x::xs“ in den Kopf x und die Restliste xs zerlegt und mit diesen beiden Elementen wird der folgende Term definiert. Dieser ist zunächst in 2 Teile aufgeteilt, die mit „@“ voneinander getrennt sind. Im mittleren steht mit [x] eine Einerliste, welche nur das Pivotelement des Quicksort-Durchlaufs enthält. Links davon wird die Teilliste von xs erzeugt, welche alle Elemente kleiner als x enthält, rechts davon entsprechend die Teilliste mit allen Elementen größer gleich xs. Mit „(xs |> (lt x >> qs))“ werden über den Pipe-Operator |> die Elemente aus xs ausgewählt, die kleiner als x sind (die Liste xs wird mittels Pipe weitergeleitet an lt und qs) und für diese Teilliste wird qs rekursiv aufgerufen (>> stellt die Verkettung zweier Funktionsaufrufe dar). Da mit jedem Aufruf von qs die Liste um mind. 1 Element kleiner wird, terminiert der Algorithmus in endlich vielen Schritten. Ok, an die Syntax muss man sich erst einmal gewöhnen, aber mal ehrlich… wie viele Zeilen hättet ihr in C# gebraucht?

Ja und?

Jetzt könnte man sagen: ja fein, F# ist eine neue funktionale Programmiersprache, hat das .NET Framework unter sich liegen und lässt sich auch recht flexibel verwenden… doch warum in aller Welt sollte mich das kümmern? Funktionale Programmiersprachen gibt es schon seit Ewigkeiten und mit C# komme ich gut zurecht, was will ich mit F#? Die Antwort auf diese Frage ist nicht leicht und zudem recht komplex. Zunächst einmal gibt es derzeit keine sehr guten Gründe, um F# wirklich intensiv zu lernen. Im Arbeitsleben ist diese Sprache noch nicht angelangt und somit eher eine Spielwiese zum Lernen funktionaler Programmierung und zum Erfreuen von enthusiastischen Programmierern. Es ist nicht abzusehen und auch nicht das Ziel von Microsoft, dass F# bestehende Sprachen wie C# ablöst. Ein großer Aspekt für die funktionale Programmierung ist das Problem der parallelen Programmierung. Viele Jahrzehnte lang haben wir sequentiell programmiert und imperative Programmiersprachen eigneten sich hierzu hervorragend. Doch mit dem Aufkommen von Multicore-Prozessoren wird die Notwendigkeit nach der Parallelisierung von Aufgaben immer größer und hier stoßen imperative Programmiersprachen an ihre Grenzen. Funktionale Programmiersprachen hingegen sind zunächst nebenwirkungsfrei und damit grundlegend für eine Parallelisierung geeignet. Variablen sind immutable (unveränderlich), womit Programmteile ohne Probleme verteilt bearbeitet werden können. Funktionale Programmierung kann damit einen Ausweg aus dem Dilemma der Parallelisierung von Programmen weisen. Wichtig dabei ist meiner Meinung nach, dass kein kompletter Paradigmenwechsel vollzogen wird. Funktionale Programmierung ist schwer und teilweise kompliziert, wenn man lange Zeit imperativ programmiert hat. Imperative Programmiersprachen geben einem Programmierer mehr Flexibilität mit dem Nachteil, dass es Wechselwirkungen geben kann, was gerade bei komplexen Softwareanwendungen häufig zu erheblichen Problemen führt. Aber funktionale Elemente werden meiner Meinung nach Einzug in Sprachen, Bibliotheken und Muster halten und somit bestehende Programmiersprachen wie C# bereichern. F# ist dabei auf einem guten Weg, da es eine hohe Ausdrucksmächtigkeit besitzt und performanter agiert als viele seiner funktionalen Kollegen. Erste Elemente von F# sind bereits in C# eingeflossen, es bleibt abzuwarten welche noch folgen werden…

So far for now… Matthias

Die Zukunft von C#

Da ich stets in aktuellen Entwicklungen von C# interessiert bin, habe ich ein wenig im Internet recherchiert und fasse nachfolgend einige interessante Hinweise mit den entsprechenden Links zusammen, wo die Reise hin geht. In Zukunft möchte ich diesen Text mit aktuellen Entwicklungen weiter ausbauen, die derzeitige Version ist lediglich als Ausgangspunkt gedacht.
 
Integration von C-Omega Sprachelementen
 
C-Omega ist eine experimentelle Sprache von Microsoft Research und offiziell gibt es keine Pläne für deren Verwendung. Trotzdem fand bereits die Übernahme einiger Sprachelemente in C# statt, wie es auch in einem Interview mit Anders Hejlsberg zur Sprache kommt. Bei C-Omega handelt es sich um eine Spracherweiterung von C#. Sie fokussiert dabei vor allem auf Probleme, welche bei der Erstellung netzwerkartiger, lose gebundener Applikationen entstehen. Diese betreffen die nebenläufige/parallele Programmierung auf der einen Seite und die Integration von relationalen Daten und XML auf der anderen Seite.
 
Beim Thema Nebenläufigkeit werden einige Konzepte eingeführt, die zunächst unter dem Namen „Polyphonic C#“ implementiert wurden und auf die bessere Unterstützung paralleler Abarbeitung (zum Beispiel durch neue Sprachelemente und die bessere Unterstützung von Asynchronität) abzielen. Mit einer besseren Einbindung relationaler Daten sollen weiterhin die bisher nahezu getrennten Welten der Programmiersprachen und der Datenbanken miteinander vereint werden, indem ein leichterer Datenzugriff aus der Programmiersprache heraus stattfindet und dabei für Typsicherheit gesorgt wird. C-Omega setzt diese Idee um mit LINQ wurde dieses Konzept in .NET übernommen. Auch der getypte Zugriff auf XML-Daten bzw. die einfache und typsichere Traversierung eines XML-Baums ist in C-Omega möglich, allerdings fand noch keine direkte Übernahme in C#/.NET statt (abgesehen von der LINQ-Unterstützung für XML-Daten). Hier darf man gespannt sein, ob und wann dies nachgeholt wird.
 
Nebenläufige/Parallele Programmierung
 
In der besseren Unterstützung nebenläufiger Programmierung bzw. der Erstellung paralleler Programme liegt eine große Herausforderung für die weitere Programmiersprachenentwicklung, bei der C# durch seine derzeit noch vorherrschende hohe Dynamik eine Vorreiterrolle spielen kann. Dem Thema Parallelität kommt dabei eine wachsende Bedeutung zu, da sich die Architektur heutiger Rechner und Netzwerke durch die Verbreitung von Multicore-Ansätzen und der lose gekoppelter Netzwerke (Cloud Computing) stetig wandelt. Jedoch wird Parallelität auch von aktuellen Programmiersprachen wie C# oder Java nur über Thread-und-Lock-Ansätze unterstützt, welche in den 1970er Jahren entwickelt wurden und die Programmierung nebenläufiger Applikationen schwer und unhandlich machen.
 
Microsoft hat mit dem Parallel Computing Developer Center einen Bereich geschaffen, der sich dem Thema Parallelität/Nebenläufigkeit widmet und dem einige Hinweise auf Entwicklungen für C#/.NET entnommen werden können. So sind mit den Parallel Extensions eine ganze Reihe von Spracherweiterungen für C#/.NET verfügbar, welche erste Möglichkeiten für die einfache Erstellung paralleler Programme beinhalten. Neben thread-sicheren Collections bietet z.B. die Klasse System.Threading.Parallel statische Methoden „For“, „ForEach“ etc. an, mit denen eine einfache Parallelisierung von Programmkonstrukten erreicht werden kann. Interessant ist ebenfalls die Einbindung eines parallelen LINQ (PLINQ).
 
Zukünftig dürfte es nicht bei einer Spracherweiterung bleiben. Die Parallel Extensions werden meiner Meinung nach bald in neue Releases von C# bzw. das .NET-Framework selbst Einzug halten und so die parallele Programmierung vereinfachen. Doch das dürfte erst die Spitze des Eisberges sein. Mittelfristig ist ein kompletter Paradigmenwechsel zur parallelen Programmierung hin nötig, was allein über Spracherweiterungen schwer erreichbar erscheint. Auf jeden Fall sind neue Sprachkonzepte erforderlich, welche die Übersichtlichkeit bei der Erstellung großer paralleler Applikationen wahren und wiederkehrende Muster in eigene Konstrukte kapseln. Eine Art Katalog an „Parallel Design Patterns“ wäre hierbei sicherlich hilfreich.
 
Was Microsoft zu dem Thema zu sagen hat, wird auf der PDC 2008 öffentlich werden. Mit einem separaten Pre-Conference-Day und 4 Konferenz-Sessions auf der PDC wird der Parallelisierung genügend Raum geboten.
 
C# 4
 
In einem Interview mit den C#-Designern auf Channel 9 werden erste Details geliefert, in welche Richtung sich C# 4 entwickeln könnte. Unter anderem wird über die Notwendigkeit von C# gesprochen, mit „dynamischen“ Sprachen wie IronPython oder IronRuby zu interagieren und diese besser einzubinden. In diesem Zusammenhang könnte es auch zu einem Ausbau der Möglichkeiten für das Meta-Programming kommen. In dem Interview wird ebenfalls eine Vereinfachung konkurrierender Aufrufe angekündigt.
Insgesamt stellt sich die Frage, wie weit die Sprachdefinition selbst mit neuen Features aufgebläht werden sollte, ohne sie zu unübersichtlich zu machen und Entwickler (vor allem Neueinsteiger) abzuschrecken.
 
Diese anderen Sharp’s…
 
Ein Blick auf Microsoft Research lohnt immer, da Forschungs-Projekte zunächst zwar losgelöst sind von konkreten Applikationen, jedoch Microsoft auch einen Nutzen bringen sollen und damit früher oder später Auswirkungen auf bestehende Elemente wie C# und .NET haben.
 
Spec# stellt beispielsweise eine Spracherweiterung für C# dar, welche die Erstellung von Qualitätssoftware vereinfachen soll. Das grundlegende Prinzip ist dabei programming by contract. Der Programmierer gibt vor, wie sich beispielsweise eine Methode zu verhalten hat (Vor- und Nachbedingungen), was in der Spec#-Syntax geschieht. Zur Compilezeit wird geprüft, ob die Methode diesen „Vertrag“ einhält. Programming by contract ist auch für die Parallelisierung von Programmen interessant, da hierüber ausgesagt werden kann, wie sich eine Funktion verhält, z.B. ob sie interne Zustände einer Klasse ändert oder andere Seiteneffekte hat. Der Compiler kann dann anhand dieser Aussagen und ihrer Prüfung entscheiden, wie eine Parallelisierung anzustreben ist.
 
Dass sich gewisse Dinge in einer funktionalen Programmiersprache einfacher und schneller implementieren lassen, ist hinlänglich bekannt. Quicksort lässt sich z.B. in Haskell elegant in 3 Zeilen Code abhandeln. F# stellt eine Implementierung des funktionalen Programmierparadigmas dar, auch wenn es nicht „rein“ funktional ist. Gewisse Konstrukte wie die Lambda-Ausdrücke wurden bereits in C# integriert, andere Anleihen dürften folgen. Auch anderen Paradigmen wie der deklarativen Programmierung sollten man ein Augenmerk schenken, hier wurde mit WPF/XAML ja auch schon ein erster Beitrag geleistet. Funktionale Programmierung ist für das Sprach-Design-Team bei Microsoft besonders interessant, da funktionale Programme per se frei sind von Seiteneffekten. Sie lassen sich daher von Haus aus gut parallelisieren. Es wird zwar kein kompletter Paradigmenwechsel angestrebt (dafür sind funktionale Programme zu ineffektiv), aber es werden „Inseln“ in der funktionalen Programmierung gesucht, mit welchen sich die aktuellen Probleme imperativer Programmiersprachen wie C# elegant handhaben lassen.
 
Weitere Links