Functional Programming in Smalltalk?

[Update]Be sure to also read the comments to this article. There’s a lot of good stuff being discussed there – you’d miss the best part[/Update]

Functional Programming Languages like Scala, F# or Clojure have gained quite some momentum in the IT world over the last few years. The main reasons for this may be the fact that functional programming makes multithreading easier than classical programming models. Unfortunately, this leads some people to think that multithreading is much easier with a functional programming language that in others. But this is not completely true. When it comes to synchronisation between threads, you face the same problems as in other languages. But that’s not what I want to write about today.

You may find it interesting that Smalltalk provides many of the concepts of functional programming languages. While Smalltalk surely cannot be classified as a “purely functional language”, it supports the main constructs for functional programming.

Let’s take a look at the two most important concepts of functional programming languages:

  • Higher Order Functions (Closures)
  • Immutability of Data

On Wikipedia, we learn that

Higher-order functions are functions that can either take other functions as arguments or return them as results

Which is exactly what Smalltalk Blocks are: Functions in the form of an object. By implementing a Block like [:arg1 | Transcript show: arg1], we implement a function in Smalltalk. This Block can be passed to a method as an argument. So if you implement a method like this:

evaluateFunction: aFunction

^aFunction value: ‘Hello world’

You can hand in any one-argument block to this function and have it evaluated with the argument ‘Hello World’. So the result of handing the above-mentioned Block into the evaluateFunction: method will be the result of doing a Transcript show:.

Smalltalk’s mighty class library is in fact built heavily on the use of Blocks (Functions). Even the simplest language constructs like ifTrue:/IfFalse: or the mighty Collection classese are making heavy use of Functional Programming.

If you look at this expression:

#(c b a t q) asSortedCollection: [:a :b| a < b]

what you see is the use of a function to sort a list of characters. The function will be repeatedly applied to two neighboring elements of the list until the function returns true for all elements.

The second concept to look at is Immutability. Immutability means that a purely functional language does not support variables. There are only values and results of functions that use values to create new values, which in turn can be used in other functions, until some value is the desired result. In a functional language, you cannot change the value of a variable, because there are no variables. Most functional languages do not follow this rule, because for many programs the complete lack of variables would make things extremely hard.

Smalltalk (or at least most Smalltalk implementations) also follow the immutability rule to a certain extent: you typically cannot assign a new value to method or Block arguments, but if a Block argument is a Collection, nobody keeps you from adding to or removing from it. So even though Smalltalk supports Closures very well, it completely fails on being a purely functional language in this discipline.

With Higher-Order Functions and Immutability in place, it is relatively easy to make a program multithreading-enabled. If the data within a Closure is absolutely independent of the world around it, most of the synchronisation problems in typical multithreading applications become irrelevant. When a function is called, it works on values that it will under no circumstances change, and therefor nobody needs to protect herself from changes to values from another thread. So it’s easy to kick of a function and have it run in its own thread and come back with a new result value whenever it may have finished with what it does. It is guaranteed that a function will not modify any data that is not its own (or its own copy). This is what functional programmers mean if they speak of side-effects: A function does create ne values, but not modify any.

This immediately brings up the question whether a functional programming language can help make every program thread-safe at no-cost.

Well, you may have guessed it already: the answer is no.

Just by using Scala or F#, your program is not magically thread-safe and can run on all four cores of your machine at light-speed. If your problem is not dividable into completely separable threads that operate on their own set of objects with no need to synchronize with other threads, a functional language can still help a lot, but it will not take the burden of finding ways of synchronizing your data. The problem itself will still be hard.

Coming back to Smalltalk, the fact that a Block can access any data and modify it (with the exception of Block arguments) , disqualifies it for the term “purely functional language”.  But still Blocks make Smalltalk a very powerful and expressive language, in which you can achieve a lot with very little code. If you write Blocks that do not modify any objects, you have all the benefits of functional programming. The only problem here is that the language doesn’t really support you in writing Blocks that are free of side-effects. While functional languages keep you from modifying data outside of the function, Smalltalk lets you do that and will not even warn you.

So is it better to use a functional language or Smalltalk?

I don’t really know. I’d say the real hard problem is to make your domain problem dividable into functions that can operate completely independent of each other.  If you know exactly how to formulate a Function or Block that will not have any side-effects, you can surely do so in both Smalltalk and Scala.

A disadvantage of Smalltalk may be that it doesn’t support multithreading on the operating system level. While its green thread model is very lightweight and performant, it’s still running in one single OS process, so its scalability is somewhat limited as compared to OS threads when it comes to multicore processors etc.

On the other hand, the problems you run into when you need side-effects or synchronization between threads the use of a functional language will probably not give you much benefit over other languages. I am not saying that a functional language will do any harm in that case.

But if you have a problem that can be formulated in functions, there is nothing keeping you from using a functional programming style in Smalltalk. The most important building Blocks (what a nice word play ;-) ) are right there in the class library and have been in use for decades.

Michael Wiedekings Frust mit Scala – und der meine

Auf seinem heise developer-Blog Babel-Bulletin schreibt Michael Wiedeking den Beitrag “Runterskaliert” über die funktionale Programmiersprache Scala und seine Probleme damit. Auch wenn dieser Blog-Beitrag schon fast angestaubt ist (einen Monat alt), konnte ich mich in einigen seiner Kritikpunkte wiederfinden, und auch meine Faszination für Scala und die damit umgesetzten Konzepte aus der Welt der Funktionalen Programmierung findet sich in seinem Beitrag wieder.

Und trotzdem hinterlässt der Beitrag einen etwas schalen Nachgeschmack.

Aber erst mal schön der Reihe nach.

Wiedeking zählt zunächst einige der Eigenschaften von Scala auf, die ihm an Scala erwähnenswert erscheinen:

Bei Scala gefällt mir der Ansatz, dass wirklich ausnahmslos alles ein Objekt ist…

Diesen Umstand findet ein Smalltalk-Liebhaber natürlich auch sehr vertraut und attraktiv. Schliesslich ist neben Typecasting und viel syntaktischem Grundrauschen vor allem die seltsam anmutende Tatsache, dass es in (sagen wir mal) Java für z.B. Ganzzahlen zwei Typen existieren, denen man auch erst kurz nach der Pubertät beibrachte, dass sie sich automatisch ineinander umwandeln können (Autoboxing), immer wieder ein Kopfschütteln wert, wenn man das nicht als ein unumstössliches Faktum mit der Muttermilch aufgenommen hat.

…wie schön einige der syntaktischen Probleme gelöst werden…

da der Autor nicht weiter darauf eingeht, was er hier meint, lese ich das als “mir hat die Syntax gut gefallen”. Auch hier finde ich mich wieder, Scala liest sich weitaus angenehmer als Ruby (sic!) oder eine Sprache aus dem C-Dunstkreis. Aber das ist natürlich reine Geschmacks- und Gewöhnungssache. Smalltalk haftet ja auch eine Aura seltsamer Syntax an.

und – nicht zuletzt – dass ich die komplette Java-Bibliothek zur Verfügung habe

Das ist natürlich ein Riesen Vorteil, den es nicht zu verachten gilt. Zwar gibt es einige Beschränkungen in der Scala-Java-Interoperabilität, aber diese betreffen in erster Linie den umgekehrten Weg, also die Nutzung von Scala aus Java heraus. Die Tatsache aber, dass ein Scala-Programm auf so ziemlich jeder (aktuellen) Java-Laufzeitumgebung aufgesetzt werden kann, und dass eine riesige Auswahl an Bibliotheken aus der Java-Welt zur Verfügung stehen, ist ein sehr gewichtiges und stechendes Argument. So ist es denkbar, eine Scala-Anwendung zu schreiben, und die Oberflächen in SWT oder Swing zu implementieren. Dafür stehen dann auch bereits bewährte Werkzeuge wie SWT Designer oder WindowBuilder zur Verfügung. Auch die komplette RCP-Umgebung von Eclipse steht dem Scala-Entwickler offen, um vertraut wirkende Oberflächen bereitzustellen. Bestechend ist auch die Idee, die Persistenz der Scala-Objekte in einem Java-Framework zu implementieren, und damit auf Bewährtes und gut Dokumentiertes zurückzugreifen.

Gerade hier wird aber auch klar, dass es in Scala meist nicht ganz ohne Java gehen wird. Das muss kein Nachteil sein, kann aber.

Schliesslich wird man um ein gerüttelt Mass an Java-Kenntnissen nicht herumkommen, und letztlich in der Wartungsphase eines Projektes immer beide Technologien vorhalten müssen. Ein Mix aus zwei Sprachen, wie nahe sie auch immer beieinander sein mögen, bedeutet immer auch, dass man bei der Käferjagd über Grenzen springen muss. Steckt man mitten im Debugger und ist an einer Stelle angekommen, an der aus Scala heraus Java-Methoden aufgerufen werden (oder umgekehrt), ist erst mal ein Moment des Innehaltens und Kontextumschaltens angesagt. Selbst, wenn ein Debugger zur Verfügung stünde, der den Übergang nahtlos darstellen könnte, befände man sich auf einem neuen Planeten: hier bin ich nicht mehr in der funktionalen Welt, und die Syntax hier vor mir ist Java, nicht Scala…

Ich will das Thema nun aber nicht überstrapazieren, der geneigte Leser kann sich sicher sein Bild davon machen, was ich meine: spätestens bei der Bearbeitung von Troubletickets ist “die andere Seite” nicht mehr als Black Box akzeptabel, auch wenn sich das ganze beim Entwickeln noch so anfühlen mag. Ich bin, wenn ich an einem Java-Debugger sitze, immer wieder irritiert, wenn dieser mir Quelltext vorenthalten will, aber das ist eine andere Geschichte…

Als Smalltalker kann ich auch das Folgende sehr gut nachvollziehen:

Beispielhaft sei hier nur das Operator Overloading erwähnt. Es hatte mich schon bei C++ begeistert, da ich schon immer fand, dass

ab + c

deutlich einfacher als

a.mul(b).add(c)

zu lesen ist

Denn in Smalltalk gibt es ebenfalls keine Operatoren, sondern nur Methoden, und es ist gar kein Problem, die Methode + zu überschreiben. Natürlich gibt es Punkte, an denen man wissen sollte, was man tut, wenn man es denn tut, aber das sollte kein Argument gegen Operator Overloading sein. In diesem Kontext sei ein kleines Beispiel aus grauer Vorzeit bemüht:

Kaum war der Jahrtausendwende-Spuk vorüber, und wir waren alle knapp dem Weltuntergang entronnen, versank die IT-Welt in der nächsten Katastrophe: Die Euro-Umstellung (manch einer mag sich nun wieder an lange Nächte, Schlaflosigkeit und vor allem eine gewisse Unruhe im Management erinnern, die zu vielen unerquicklichen Reports und Status Meetings führte) . Millionenbeträge wurden investiert, um Programme fit für die neuen Umrechnungs- und Rundungsregeln sowie die Umstellung in zwei Phasen fit zu machen. In einem Smalltalk-Projekt konnten wir hier mit relativ wenig Aufwand eine Klasse für Geldbeträge einführen, die sich nach aussen hin mit dem API einer Zahl präsentierte, und intern den gesamten Verwaltungskram abhandelte (in welcher Europhase sind wir, bin ich eine Summe aus Beträgen in zweierlei Währungen etc.). Für sämtliche Stellen, die also vorher zwei Zahlen unter Verwendung der Methode + addierten, änderte sich gar nichts. Es musste lediglich dafür gesorgt werden, dass alles, was einen Betrag erzeugte, eben nicht mehr nur eine Zahl instanziierte, sondern ein Objekt der Klasse EuroBetrag. Der Rest des Systems blieb unangetastet. So war der ganze Spuk in ein paar Wochen erledigt (inklusive Tests), und wir konnten neue Features implementieren, während andere Projekte enormen Aufwand treiben mussten.

Also, kurz gefasst: Operator Overloading kann extrem mächtig sein, und enorme Produktivitätsvorteile bieten, wenn man sich sparen kann, jeden einzelnen +-Operator darauf zu untersuchen, ob er nun ein Aufruf der add-Methode der neuen EuroBetrag-Klasse sein muss oder nicht.

Jetzt aber kommt der erste Stolperstein für meine positive Grundeinstellung gegenüber Herrn Wiedekings Artikel, in dem er die aus seiner Sicht großen Vorteile des Operator-Overloadings darstellt. So sieht er es als zusätzlichen Vorteil, dass Scala es erlaubt,

beliebige Unicode-Zeichen als Operatoren zu definieren. So konnte ich beispielsweise beim Herumexperimentieren einen regulären Ausdruck der folgenden Art schaffen:

NUMBER = LPAR ∙ (SP↯) ∙ (ALPHANUM ↦ 0) ∙ (SP↯) ∙ RPAR ∙ (SUBNUM ↦ 1)

Ich muss zugeben, dass meine Kenntnisse in der Erfassung von Unicode-Zeichen über die Tastatur eines heute gebräuchlichen PCs oder Macs sicher verbesserungsfähig sind, aber irgendwie kann ich mich des Gedankens nicht erwehren, dass ich hier verloren ging. Nicht nur, weil ich mathematisch nicht mehr mithalten kann, sondern auch, weil ich hier fast glaube, dass diese Unicode-Sache für Operatoren dann doch etwas zu weit geht.
Wobei das vielleicht nicht für Anwendungen im mathematischen oder Engineering-Bereich gilt. Letztendlich liest sich der Code für einen Mathematiker dann doch sehr gut.
Ich fand das Beispiel allerdings sehr esoterisch und nicht wirklich ein gelungenes Plädoyer für Operator Overloading oder Scala.

Nun kommt Wiedeking aber zu einem sehr interessanten Punkt:

So habe ich also Scala schätzen und nutzen gelernt, habe mich aber trotzdem gegen einen professionellen Einsatz von Scala entscheiden müssen. Das ist insofern bedauerlich, weil Scala einen deutlich höheren Wiederverwendungsgrad hat als beispielsweise Java.

So ging es mir auch: Scala gefällt mir gut, und die Möglichkeiten durch die Java-Basis sind sehr bestechend, aber so recht zu Scala konvertiert bin ich auch (noch?) nicht.Während wir gleich noch auf Wiedekings Argumente gegen Scala eingehen, hier ein paar Gedanken von mir:

Ein Aspekt auf den Wiedeking in seinem Artikel nicht wirklich eingeht, sind die funktionalen Ansätze in Scala, die in vielen Problembereichen sehr mächtige Vorteile sein können. So ist es durch die Nutzung von Konstanten (also vals) anstatt Variablen, und durch die Freiheit von Seiteneffekten bei der Bearbeitung von Konstanten sehr schön möglich, sehr gut lesbaren Code zu schreiben, und vor allem, Funktionen so zu formulieren, dass sie gut in einem eigenen Thread ablaufen können. Scala hilft bei der Parallelisierung von Aufgaben und kann so einen wichtigen Beitrag zur effizienteren Nutzung von Multicore-Prozessoren leisten. Wohlgemerkt, solange man Scala als funktionale Programmiersprache nutzt. Nun sind in meiner Projektpraxis ab und an Aufgaben auszumachen, bei denen man sich eine einfach zu implementierende multithreading-Fähigkeit wünschen würde, aber die Regel ist dies nicht. So wäre es manchmal schön, mitten in einem Smalltalk- oder Java-Programm eine solche Funktion zu formulieren und sie parallel ausführen zu lassen. Aber meist lässt sich dies auch mit den Bordmitteln der Sprache auch schon lösen, wenn auch möglicherweise mit etwas mehr Aufwand. Das halte ich für Vertretbar, wenn es um wenige Tasks in einem Programm geht. Smalltalk-Blöcke sind hier sicher ein hervorragender Ersatz für Funtionen in Scala, da diese in ihrer Eigenschaft als Objekte auch herumgereicht und jederzeit mit anderen Inhalten auswerten kann. Sonderlich weit ist ein Smalltalk-Block von einer Funktion in Scala nicht entfernt. Eines jedoch ist ein Vorteil von Scala: eine Funktion kann als nativer Thread ausgeführt werden. In Smalltalks Green Threads – Modell geht dies nicht. Hier sind noch Ansätze gefragt, die auch eine Unterstützung von Multicores in Smalltalk bringt.

Nun aber zurück zu Wiedekings Argumentation:

Der Grund ist ein ganz anderer: Es ist die Entwicklungsumgebung. Ich gehöre eigentlich zu denen, die, bis es nicht mehr anders ging, mit dem “vi”, “emacs” oder vergleichbaren Editoren gearbeitet haben. Und eine Zeit lang war das auch vollkommen ausreichend. Allerdings hatte sich das schlagartig mit dem Einzug der Refactoring-Fähigkeiten moderner IDEs geändert.

Ja, hier wird es düster um Scala (wie auch um andere der aktuell sehr beliebten Sprachen, etwa Ruby, erlang o.ä.). Zwar gibt es ein Eclipse-Plugin, das einge der heute üblichen Werkzeuge, wie man sie aus Eclipse, IntelliJ oder modernen Smalltalk-Entwicklungen kennt, bietet, aber eben leider keinen guten Eindruck hinterlässt. Ich habe es bisher auf den meisten Eclipse-Installationen nicht (oder nicht vollständig) zum Laufen gebracht, auf denen ich es versucht habe. Und die Tools sind noch weit davon entfernt, den Umfang eines Eclipse-Debuggers oder Smalltalk-Inspectors zu erreichen. Geschweige denn deren Stabilität. Das ist nun irgendwo auch nicht verwunderlich, denn die Tools, mit denen ich das Scala-Plugin vergeliche, sind von einem Heer von bezahlten Programmierern erstellt worden, und nicht von einigen wenigen Enthusiasten, Diplomanden und Doktoranden. Insofern ist es ein unfairer Vergleich zwischen Äpfeln und Birnen. Und dennoch zieht es der Mensch eben vor, ein funktionierendes Werkzeug zu nutzen.

So habe auch ich vorerst das selbe Fazit gezogen wie Wiedeking:

Nichtsdestoweniger werde ich die Entwicklungsumgebungen von Scala im Auge behalten. Und sobald sie meinen Ansprüchen genügen, werde ich nie wieder in Java programmieren müssen.

Nur mit dem Unterschied, dass ich mit Smalltalk deutlich weniger unzufrieden bin, als er mit Java ;-)

So stimme ich Herrn Wiedeking zu, dass Scala als Ersatz für unsere jetzige Umgebung nicht in Frage kommt, aber wir beide beurteilen die Sprache Scala in völlig unfairer Weise über die für sie verfügbaren Tools, anstatt uns mit den Fragen auseinander zu setzen, welche konkreten Vorteile die Sprachmittel von Scala uns gegenüber unserer aktuellen Lieblingssprache bietet. Ich glaube aber, wir sind da nicht die einzigen…

ESE 2009: Don Syme über F# und Funktionale Programmierung

Hier in Ludwigsburg findet aktuell ja das Eclipse Summit Europe statt. Es sind rund 400 Leute dabei, und es gibt eine Menge zu lernen über Eclipse, einige der wichtigsten Eclipse-Projekte und die neuesten Entwicklungen zu Eclipse4 (kurz e4), dem noch etwas vagen und für manch einen auch sehr esoterischen nächsten großen Meilenstein in Eclipse.

Zwar ist mir noch niemand über den Weg gelaufen, der mir mit meinem DataBinding-Problem weiterhelfen kann, aber dafür gibt es eine Menge an Informationen und Eindrücke zu bewältigen.

Als Business Partner von Instantiations sind wir natürlich auch dam um Dan, Jaime und Nick zu unterstützen und Interessenten über die Neuerungen der Instantiations-Tools zu informieren. Aber das ist eine andere Geschichte…

Die heutige Keynote war bemerkenswert. Nicht nur, weil Don Syme für Microsoft arbeitet, sondern auch, weil er nicht über Java, sondern über F#, eine funktionale Programmiersprache von MS, und dabei für seine Demo nicht Eclipse, sondern VisualStudio benutzt hat. Und trotzdem wurde den ganzen Tag über an den Tischen und in den Kaffeegrüppchen darüber gesprochen, und nicht etwa, um ein Urteil über seinen dreifachen Frevel zu fällen, sondern um über funktionale Programmiersprachen, Type Inferencing und Scala zu diskutieren.

Und in der Tat war seine Keynote ein Highlight. Er zeigte einige sehr eindrucksvolle Beispiele, wieviel kompakter und verständlicher F# Code sein kann, verglichen etwa mit C#. Neben einem kurzen Schweinsgalopp durch die wichtigsten Sprachkonstrukte von F# und die Kernkonzepte funktionaler Programmierung brachte er auch einige gewichtige Argumente für funktionaler Programmierung zur Sprache:

  • Die Lästigkeit des vielen Syntax-Rauschens in Java oder C# verglichen mit FP
  • Die ökonomischen Effekte durch wesentlich niedrigere Fehlerraten (bekannter Weise sind die Codezeilen, die nie geschrieben wurden, die am wenigsten fehleranfälligen)
  • Den Spass am Programmieren, der in C# oder Java leider allzu oft verloren geht

Als Smalltalker fühlte man sich immer wieder an die eigene Umgebung erinnert, nicht zuletzt, als er in F#interactive in der Manier eines Workspaces Code-Schnipsel für Codeschnipsel mit der Maus selektierte und per “Execute”-Menü bzw. Tastenkürzel ausführte.

Die ersten Code-Beispiele waren dann auch noch sehr “normal” und da man in F# (wie übrigens auch in Scala) keine Typeninformationen eintippen muss, fühlt sich das ganze für einen Smalltalker sehr vertraut an.

Aber FP ist noch deutlich mehr: die FP ermöglicht es, Funktionen zu definieren und praktisch als Objekte immer wieder zu verwenden. Ähnlich vielleicht wie Smalltalk-Blocks, aber eben irgendwie auch anders. Durch Definition einer Funktion kann Code extrem kompakt gehalten werden (man ist ab und an im Hinterkopf den Begriff Makro zu bemühen, aber ein Makro ist ja nur ein Precompiler-Trick um Schreibarbeit zu sparen, und ist nicht der richtige Vergleich).

Die Trends, die aktuell in OO-Sprachen zu beobachten sind, so Syme, sind genau die Stärken der FP:

  • Immutable Data
  • Abfragesprachen in der Programmiersprache eingebettet
  • Lambda-Funktionen
  • Pattern Matching als Sprachelement
  • Einfache Syntax
  • Reduktion von Seiteneffekten

Genau diese sieht Syme in F# realisiert. Die Funktionale Programmierung wird aktuell ja als einer der vielversprechendsten Ansätze gesehen, um die Umsetzung von Programmen für Multiprozessor- und Multi-Core-Architekturen zu schreiben. Eine Funktion ist in sich abgeschlossen, operiert auf isolierten, unveränderlichen Daten und erzeugt neue, und kann daher isoliert auf einem Prozessor oder Core parallel ausgeführt werden.

Die Sprache F# hat zwei wesentliche Urpsünge: OCaml als Ideenlieferant für die Syntax und C# wegen des gemeinsamen Objektmodells.

Ein sehr wichtiger Aspekt, den Syme anspricht, ist die Tatsache, dass ein F#-Programm nahtlos in ein .Net-Umfeld eingebettet werden kann. Jede andere .Net-Sprache kann auf F#-Komponenten zugreifen, und umgekehrt natürlich auch. Dasselbe gikt für Scala, das ja nahtlos mit Java interoperieren kann.

So macht es möglicherweise Sinn, den GUI-Code in C# (Im Sinn: Java/Eclipse) zu schreiben, und die Business-Logik in F# (behalte Scala) umzusetzen.

FP hat das Zeug dazu, die Objekttechnologie abzulösen bzw. einen Schritt weiter zu bringen. Java, C# etc. sind Sprachen, in denen sehr viel technisch motivierter Krimskrams programmiert werden muss, den man sich in der FP sparen kann. Die Ansätze in JavaScript, Scala und F# dazu sind vielversprechend. Vielleicht ist Jave wirklich bald mehr eine Plattform als eine Programmiersprache, und vielleicht bewegen wir uns bald wieder auf einem neuen Terrain, der funktionalen Programmierung.

Ich wäre dabei. Die ganze Umgebung und die Denkweise in Scala oder meinetwegen F# ist einem Smalltalk-Entwickler sehr vertraut. Und doch gibt es einige neue Ideen und Konzepte zu entdecken und zu nutzen.

Achja: F# hat auch etwas, das mich doch sehr verwundert hat: Whitespace matters. Das heisst, es ist für Zeilen in einer Funktionsdefinition zwingend erforderlich, dass sie mit einem Tab eingerückt sind. Ich musste unweigerlich an Cobol und PL/1 denken, und habe innerlich den Kopf geschüttelt. Aber vielleicht gibt es ja eine gute Erklärung für diese Design-Entscheidung… Zumindest gibt es so keine Glaubenskriege über Einrückungen oder den richtigen Platz für schliessende Klammern… ;-)