Podcast

Scala

Was die objektorientierte, funktionale JVM-Sprache in der Praxis bringt

In dieser Folge diskutiert Tobias Neef mit Stefan Tilkov über Vorteile von und Vorurteile gegen Scala. Tobias gibt einen kurzen Überblick über einige wichtige Features und sowie deren Nutzen in der Praxis.
Weitere Episoden anhören

Shownotes & Links

Transkript

Transkript ausklappen / einklappen

Tobias Neef: Ja, hallo Stefan.

Stefan Tilkov: Tobias, stell dich doch kurz vor. Was machst du bei uns bei innoQ.

Tobias Neef: Ja, mein Name ist Tobias Neef. Ich bin hier Entwickler und Berater und bin da momentan hauptsächlich tätig bei Web-Unternehmen, also Firmen, die sich primär durch ihre Webseite ausmachen und zudem rede ich auch ganz gern über Scala. Und das habe ich mittlerweile so weit getrieben, das ich auch Leuten über Scala in Form vom Schulungen erzähle. Jedenfalls denen, die sich dafür interessieren.

Stefan Tilkov: Gut, wir haben schon einmal eine Episode zu Clojure gemacht, deswegen ist es schön, dass wir jetzt Scala so ein bisschen als Konkurrenzprodukt mit dabei haben. Die meisten unserer Zuhörer werden Scala wahrscheinlich sehr viel besser kennen, als z.B. ich. Trotzdem ist es vielleicht nicht schlecht, wenn wir mal kurz starten und eine kurze Einführung machen. Kannst du uns so die wesentlichen Charakteristika von Scala nochmal näher bringen?

Tobias Neef: Ja gerne. Also Scala ist auch eine Sprache, die auf der JVM als Host-Plattform läuft. Sie ist im Gegensatz zu Clojure statisch typisiert und sie verbindet sowohl das funktionale als auch das objektorientierte Paradigma innerhalb einer Sprache.

Stefan Tilkov: Andere Charakteristika, die vielleicht neben der statischen Typisierung noch erwähnenswert sind und Scala von anderen JVM-Sprachen unterscheidet?

Tobias Neef: Ja es kommt immer ein bisschen drauf an, welche Features man da raus pickt, die einem besonders am Herzen liegen. Ich merke allgemein an Scala, weil sich das über die Sprache zieht, dass alles, was man in Scala schreibt und was syntaktisch korrekt ist, eine Expression ist, die ausgewertet werden kann. Also alles, was man schreibt – z.B. ein if-then- else-Statement oder eine Schleife oder ein try-catch-Block – führen alle zu einem Wert im Endeffekt, der auch genutzt werden kann als Argument für weitere Funktionen. Und das führt dazu, dass Variablen eigentlich nur noch optionale Dinge sind, die verwendet werden, um den Code besser lesbar zu machen, um irgendwelche Zwischenergebnisse darzustellen, aber man braucht sie nicht an allen Stellen, um den Code zum laufen zu bringen und das führt dazu, dass trotz der statischen Typisierung Scala-Programme relativ kurz sind im Vergleich zu Java-Programmen.

Stefan Tilkov: Das ist aber ein interessanter Punkt. Also ich kenne diesen “Everything ist an Expression”-Ansatz aus anderen Sprachen, z.B. aus Ruby, aus Perl, was auch immer. Also alles gibt irgendwie einen Wert, aber wie geht das mit der statischen Typisierung zusammen? Gibt das nicht Konflikte oder rede ich mir da ein Problem herbei, das es nicht gibt?

Tobias Neef: Ja also die statische Typisierung macht insofern Probleme, wenn man Polymorphie mit ins Spiel bringt. Also wir haben eine Vererbungshierarchie beispielsweise und wir haben jetzt ein if-else-Block. Und der if-Teil von diesem Block würde jetzt einen Apfel zurück geben und der else-Teil dieses Blockes eine Birne. Und wenn man jetzt sich anschaut “Was ist denn der Return-Value von dieser Expression?”, weiß man “Ok, auf der einen Seite ist es ein Apfel, auf der anderen Seite eine Birne.” Aber in der statisch typisierten Programmiersprache muss man ja auch eine Aussage darüber tätigen können, “Welchen Typ hat jetzt dieses Ergebnis?” Und jetzt kommt es drauf an, wie die Klassen zueinander in Verbindung stehen. Wenn keine Beziehung zwischen Äpfel und Birnen besteht, wird sozusagen der kleinste gemeinsame Typ genommen, das ist in Scala AnyRef. Das ist quasi das Äquivalent zu Object in Java. Wenn jetzt jedoch eine Vererbungsbeziehung zwischen diesen beiden Objekten besteht, also das beides z.B. eine Frucht ist, wäre das Ergebnis entsprechend auch dieser kleinste gemeinsame Typ, die Frucht.

Stefan Tilkov: Ok. Da muss man sich nicht wundern, wenn der Compiler da manchmal ein bisschen länger braucht. Was er da macht ist ja schon relativ beeindruckt, wenn er das herausfinden muss.

Tobias Neef: Genau, also diese Code-Analyse ist an einigen Stellen recht aufwändig, deswegen ist auch einer der Nachteile einer Scala-Umgebung durchaus, dass der Compiler etwas mehr Zeit in Anspruch nimmt. Allerdings versucht da die Scala-Community einiges zu leisten, um diese Zeiten zu reduzieren. Kommen wir vielleicht später nochmal zu, wenn wir über Tools im Scala-Umfeld reden.

Stefan Tilkov: Gut, weitere Charakteristika von Scala?

Tobias Neef: Ja. Also was ich noch gut finde an Scala, ist die Mischung aus Objektorientierung und funktionaler Programmierung. Man kann das ganze so sehen, dass es funktionale Programmierung und Objektorientierung einfach gibt und die beide irgendwie nebeneinander stehen, aber eigentlich ist Scala aus dem Kontext entstanden, dass man erforschen wollte, wie kann man denn diese beiden Konzepte miteinander vereinen. Und daraus ist letztendlich ein eigener Style geworden, ein eigener Ansatz, wie man Programme designt. Und nach einiger Zeit, wenn man mit Scala etwas mehr in Berührung gekommen ist, neigt man dazu, Objektorientierung dafür zu verwenden, für was es eigentlich gedacht iStefan Tilkov: die Komposition im Großen, wenn man von Komponenten redet, kann man auf der Ebene mit Objektorientierung arbeiten. Und auf der kleineren Ebene, wo es eher darum geht, die funktionale Seite des Systems zu entwickeln, also das was das System wirklich macht, dafür ist funktionale Programmierung wirklich ein sehr gutes Werkzeug, mit dem man viel erreichen kann.

Stefan Tilkov: Das würde ich gerne nachher noch ein bisschen hinterfragen, aber das sparen wir uns vielleicht mal auf bis wir den Überblick hinter uns haben. Wenn du noch weitere Features rausgreifen würdest, was ist noch interessant? Vielleicht für jemanden, der Java kennt und sich das erste Mal mit Scala beschäftigt.

Tobias Neef: Also wenn ich ein Feature raus greifen wollte, was wirklich Spaß macht, würde ich sagen, ist es das Pattern-Matching. Pattern-Matching ist ein Mechanismus, der in der Sprache an einigen Stellen integriert ist. Aber im Kern funktioniert das so: wir haben einen Wert – z.B. eine Liste – und jetzt wollen wir einige Werte aus dieser Liste raus extrahieren und wollen darauf aufbauend irgendwelche Logik hinterlegen. Wir wissen z.B., dass das erste Element der Liste der Vorname ist und das zweite Element eben der Nachname. Und wenn man jetzt mit einer Sprache ohne Pattern-Matching an dieses Problem ran geht, müsste man gucken:

  • Ist diese Liste null?
  • Hat es einen ersten Wert?
  • Hol mir den ersten Wert raus.
  • Schreibe diesen Wert in die entsprechende Variable Vorname.
  • usw.

Das Ziel von Pattern-Matching ist es zu sagen: wir beschreiben auf einer Seite, wie diese Datenstruktur aussehen soll. Also in dem Fall: erstes Argument, zweites Argument, Rest. Das ist das, was uns an der Liste interessiert. Und wenn dieses Pattern matcht, was wir auf der linken Seite definiert haben, dann können wir den Inhalt dieses Patterns als Variable nutzen.

Stefan Tilkov: Ok. Gut, also wir könnten wahrscheinlich noch ein paar Stunden über die ganzen Sprach-Features reden, die da drin sind. Das waren jetzt mal ein paar davon heraus gegriffen. Was ist denn der aktuelle Status von Scala? Also wie fühlt sich das im Moment gerade an? Welchen Status hat das Ökosystem insgesamt?

Tobias Neef: Ein Aspekt, der irgendwie ganz klar auffällt, ist, dass Scala ganz definitiv keine Hype-Sprache mehr ist. Also vor einem Jahr hatte man noch ein bisschen das Gefühl, dass jeden Tag irgendwelche Blog-Posts und Intros wie Pilze aus dem Boden sprossen.

Stefan Tilkov: Und Podcasts.

Tobias Neef: Podcasts, genau. Mittlerweile ist es selbst bei innoQ angelangt. Nein, Spaß.

Stefan Tilkov: Hehehe, danke. Wobei das ein interessanter Punkt ist. Also die meisten Leute assoziieren das vielleicht nicht mit Scala, aber wir haben eine ganze Menge Scala in verschiedenen Projekten mittlerweile drin.

Tobias Neef: Genau, genau. Definitiv merken wir auch bei unseren Kunden, dass Scala ein immer größeres Thema wird und deswegen kommt es auch immer mehr bei realen Projekten. Oder auch in Form von Schulungen wird es immer öfters angefragt. Und die Entwicklung zeigt so ein bisschen, dass das ganze einen gewissen Reifegrad erreicht hat. Also es ist kein Forschungsprojekt mehr irgendwie aus den 90ern, sondern es ist eine reife Instanz, die viele große Benutzer hat. Die typischen Beispiele, die man immer so hört, sind ja vor allem auf der einen Seite Web-Konzerne wie LinkedIn oder Twitter, die ganz zentral in ihrer Infrastruktur Scala einsetzen. Aber auch eher Enterprise-orientierte Unternehmen, wie jetzt z.B. Siemens, haben nach außen getragen, dass für sie Scala ein Faktor ist. Deswegen kann man auf jeden Fall sagen, dass die Community da eine gewisse reife hat. Ein Askpekt, der auch noch dazu beigetragen hat, ist, dass sich die ganze Szene aus dem akademischen Bereich raus entwickelt hat und diverse Firmen in diesem Umfeld entstanden sind, die dann auch entsprechende Support-Angebote um Scala herum bereitstellen, die Entwicklern bereit stellen um an den IDEs zu arbeiten – primär an Eclipse und IntelliJ IDEA zu arbeiten. Und das hat natürlich sehr zur Reife des Ökosystems beigetragen.

Stefan Tilkov: Ich habe es ein paar Mal nur gelesen und keine persönlichen Erfahrungen, aber ich habe mal gelesen, dass es Kritik daran gab, dass immer so viel an inkompatiblen Änderungen von Binary-Release zu Binary-Release gemacht wurden. Ist das immernoch so oder hat sich das auch beruhigt?

Tobias Neef: Ja. Also in den frühen Versionen von Scala war das eigentlich so, dass man mit jeder neuen Scala-Version, die raus gekommen ist, die anhängigen Scala-Librarys nicht mehr nutzen konnte. Das hat sich mittlerweile verbessert und man hat von Seiten der Scala-Entwicklung auch Garantien, wie es mit dieser Binär-Kompatibilität aussieht. Also innerhalb der Minor-Version sollte es keine Binär-Kompatibilitätsprobleme mehr geben und außerhalb der Minor-Version – also die Major-Releases – können diese durchaus behalten. Da wurde aber auch schon angekündigt, dass auf Grund der Reife der Sprache das auch auf ein Minimum reduziert werden muss. Aber auf der anderen Seite will man auch nicht die Route von Java gehen, dass man sagt: “Ok, wir behalten in jedem Fall alles drin.”, sondern da geht man auch eher in die Richtung und sagt: “Wir wollen uns weiter entwickeln und die Sprache sauber halten.”

Stefan Tilkov: Wo wir gerade so bei Kritikpunkten sind, vielleicht bringen wir das kurz hinter uns. Wir wollen Scala jetzt nicht negativ einfärben, aber ein paar Dinge müssen wir doch mal ganz kurz andiskutieren. Also ein häufiger Kritikpunkt an Scala, den ich selber auch gelegentlich von mir gebe, ist, dass die statische Typisierung wild geworden und Amok gelaufen ist, also das nicht so statisch und so extrem typisiert ist, wie Scala und dass es für jemanden, der dynamische Sprachen gewohnt ist, relativ schwer zu absorbieren ist. Was sagst du dazu?

Tobias Neef: Ja, also ich glaube dieser Vorwurf hat zwei Seiten. Zum einen hört man diesen Vorwurf natürlich gerne aus dem Camp der Leute, die schon mal Berührungspunkte mit Java hatten und aus der Erfahrung von Java raus ein schlechtes Bild über statische Typisierung haben. Das ist sicherlich ein großer Punkt. Und dann muss man sich mal überlegen, für was man statische Typisierung nutzen möchte. Statische Typisierung – aus meiner Sichtweise – nutzt man zum einen, weil man will, dass eine gewisse Klasse von Fehlern von einem Compiler abgefangen werden. Man will aber auch, dass während des Entwicklungsprozesses der Compiler gewisse Unterstützung leistet. Also man möchte, dass verschiedenste Refactorings möglich sind, man möchte aber auch zur Laufzeit eine gewisse Geschwindigkeitssteigerung haben, weil eben der Compiler weiß, dass es sich um einen int handelt und es dann entsprechend auch auf der Maschine optimiert werden kann. Was man jedoch nicht will, ist, möglichst viel Zeit darein zu investieren, den Compiler das beizubringen. Also man möchte nicht andauernd irgendwelche Typ-Annotationen zehnfach wiederholen, wie man das in Java tut, sondern man möchte präzise die Typen da verwenden, wo es sein muss und woanders nicht. Und in Scala versucht man das auch, indem es eine sehr gute Typ-Inferenz gibt. Also man schreibt Typen nur einmal in der Signatur von Methoden im Regelfall und dann wird diese Typ-Deklaration auch überall weiter verwendet, sodass in vielen Teilen des Programms Scala gar nicht so anders aussieht, wie eine nicht-typisierte Sprache. Das würde ich sagen ist der eine Bereich. Der andere Bereich, was die vielen Features der statischen Typisierung angeht, das sehe ich ein bisschen so, dass wenn man Frameworks entwickelt, wenn man gute APIs für seine Nutzer bereit stellen will, hat man immer eine gewisse Komplexität in dem, was man abstrahieren will. In Java ist das mittlerweile so, dass wenn man eine neue Abstraktion einbaut, meistens eine neue Annotation definiert wird, die diese Abstraktion beschreibt. Z.B. irgendwie ein @Transactional, um einen Datenbank-Tranksaktions-Commit-Rollback-Workflow zu kapseln. Und darauf aufbauend baut man dann einen Annotationsprozessor, der dann zur Laufzeit irgendwelchen Bytecode generiert um das ganze zu kapseln. Das ist mittlerweile eigentlich de facto Teil der Java-Framework Entwicklung. Aber man muss sich hier halt auf sehr low-Level Primitiven, wie die Bytecode Manipulierung zurückziehen. In Scala hingegen muss man auch komplizierte Abstraktionen bauen, hat da aber ein größeres Set an Features, die durchaus teilweise kompliziert sind, es einem aber auch ermöglichen innerhalb der Sprache dieser Abstraktion zu bauen.

Stefan Tilkov: Kompliziertheit ist vielleicht das nächste Stichwort. Was sagst du zu dem Vorwurf, dass Scala das Super-Set aller Features aller nur denkbaren Programmiersprachen enthält?

Tobias Neef: Also auf jeden Fall enthält Scala sehr viele Features, das ist keine Frage. Aber das Gute ist der Ansatz, wie mit diesen Features umgegangen wird. Also grundsätzlich gibt es einen formalisierten Prozess, wie neue Features in Scala rein kommen, der SIP, also der Scala Improvement Process. Und da werden Vorschläge rein gegeben, welches Feature denn in Scala rein kommen soll. Einer der Vorschläge war z.B. String-Interpolation. Das ist ein Feature, was man aus vielen Sprachen kennt und es wäre jetzt logisch gewesen: “Ok, String-Interpolation gibt’s hier und da, machen wir das genauso.” Der Martin Odersky, der Sprach-Schaffer, macht da eine ganz gute Arbeit, bestehende Features, die es auch in anderen Sprachen gibt, gut in die Scala-Welt zu integrieren. Da gibt es halt mehrere Herausforderungen, zum einen: wie bekommt man das hin, das Ganze mit diesem objekt-funktionalem Mix gut zu integrieren? Aber auch die Frage des Typsystems ist eine schwierige. Denn es gibt Features wie Makros z.B., die es schon in dynamisch typisierten Sprachen in verschiedenen Formen gibt. Aber wie man das jetzt hinbekommt, die auch statisch typisiert ist, ist halt eine durchaus schwierige Frage. Die Konsequenz ist, dass man halt auch einige Features hat, die aber auch gut intergriert sind in die Sprache und sie auch nur auf Schichten angewendet werden müssen. Also um mit Scala produktiv zu arbeiten, muss man mit dem oberen Drittel der Sprach-Features nicht unbedingt in Berührung kommen. Typisches Beispiel ist die Collection Library. Die ist in Scala sehr umfangreich. Man hat viele Standard-Typen, die man so erwartet, wie Maps und Listen und Spezialisierungen davon. Und man hat auf diesen Typen etwa 50 Operationen definiert. Das Schöne ist auch, dass wenn man diese Operationen ausführt, bekommt man immer den konkreten Typ zurück, den man erwartet. Also man bekommt, wenn man auf einer Liste ein map ausführt, keine Sequenz zurück, sondern man behält den konkreten Typ bei. Man kann auch einfach neue Collection erstellen, die diese Funktionalität auch mit benutzen. Und um das bereit zu stellen, müssen einige Features in Scala vorhanden sein, die man aber so als Endnutzer nicht unbedingt braucht. Von daher finde ich das auch eine aktzeptable Entscheidung zu sagen: Wir nehmen diese Features rein., anstatt zu sagen: Wir sind zu minimalistisch und müssen dann sehr schnell auf Primitiven, wie “Bytecode umschreiben” zurückkommen.

Stefan Tilkov: D.h., du würdest diese These, die der Odersky auch schon häufiger mal gesagt hat, nämlich dass man wirklich als Bibliotheksentwickler einfach ein ganz anderes Feature-Set benutzt als als Bibliotheksnutzer, das würdest du auch unterschreiben. Das ist eine Scala-typische Vorgehensweise.

Tobias Neef: Ja, definitiv. Ich würde nicht zustimmen bei der Aussage, man muss überhaupt nichts von den Features wissen. Also man muss sicherlich wissen, dass es da ist, diese Funktionalität und ein bisschen einen Plan davon haben. Aber das kann auch sehr oberflächlich sein. Also ich sehe irgendwas in der Typsignatur und kann davon ausgehen, dass es mir folgendes bringt, ohne dass ich im Detail das Feature und die Mechanismen dahinter verstehen muss.

Stefan Tilkov: Ok. Lass uns doch nochmal auf diese Objektorientiertheit und funktionale Programmierung zurück kommen. Du hast gesagt, dass das eines der zentralen Features ist. Mir fallen auch so viele andere Programmiersprachen nicht ein, die sowas tun. Vielleicht F# oder so, wobei ich das auch nicht näher kenne. Wie geht man denn in der Praxis damit um, also wie entscheidest du, wann du etwas funktional machst und wann du etwas objektorientiert machst? Oder ist das einfach ein Lernprozess? Am Anfang macht man noch Objektorientierung, weil man vorher Java gemacht und wenn man eine Weile funktionale Programmierung ausprobiert hat, dann lässt man die Objektorientierung hinter sich. Wie ist das da in der Scala-Welt?

Tobias Neef: Ja, das ist eigentlich eine ganz gute Sichtweise. Es kommt ein bisschen auf den Kontext an, von wo man kommt. Also die meisten Leute kommen sicherlich eher aus dem objektorientierten Kontext und übertragen dann ihren Java-Code oder ihren Ruby-Code oder was auch immer erstmal auf Scala. Es gibt aber auch andere Leute, die kommen eher aus einer funktionalen Welt, die machen viel Haskell oder mögen es, die haben dann aber auch irgendwie gemerkt, dass es mit Haskell-Projekten schwierig aussieht.

Stefan Tilkov: Dummerweise, ja.

Tobias Neef: Deswegen versuchen sie dann Scala und das sieht auch ganz anders aus, als der Scala-Code, den ein früherer Java-Entwickler schreiben würde. Deswegen ist es da auch wichtig innerhalb des Projektes passend auf den Wissensstand der Projektmitglieder einen Style zu finden, auf den man sich einigen kann, weil natürlich diese zwei Paradigmen einem relativ viele Freiheiten lassen, wie man Dinge miteinander verbindet. Was sich jetzt innerhalb der Community über die Jahre bewährt hat, würde ich sagen, ist, dass man funktionale Programmierung sehr gut bei der Beschreibung von Algorithmen oder von Programmlogik im Kleinen einsetzen kann. Denn was macht man typischerweise in einem Programm? Man nimmt irgendwie eine Menge von Daten, zieht sie aus einer Datenbank oder aus irgendeiner Web-Form, bringt sie zueinander in Verbindung, tansformiert sie in irgendein anderes Format – z.B. JSON – oder schreibt es in die Datenbank. Man hat also viele Transformierungsschritte, die meistens auch in Form von Listen und primitiven Datenstrukturen beschrieben sind. Und auf der Ebene eignet sich funktionale Programmierung sehr gut, weil man nicht so viel beschreiben muss, wie man etwas erreicht, muss vor allem nicht die gleichen Patterns, wie “Ich übertrage Liste 1 in Liste 2”, jedes Mal zum 50sten Mal neu beschreiben, sondern die sind gekapselt und ich kann sie entsprechend wieder verwenden, was das ganze auch deutlich lesbarer macht. Und der Anwendungsumgebung auch mehr Spielraum lässt, Optimierungen für einen durchzuführen. Wenn man z.B. mit einer Liste arbeitet und möchte, dass Operationen auf der Liste parallel ausgeführt werden, reicht es in Scala einfach als Zwischenoperation ein .par einzufügen und damit ist es eine parallele Collection, die dann auch parallel abgearbeitet werden kann. Aber die Semantik bleibt die gleiche und das ist eine große Stärke der funktionalen Programmierung. Wo funktionale Programmiersprachen bisher ihre Schwächen hatten, ist in Form der Modularisierung. Also es gibt auch in Sprachen wie Haskell kein wirkliches Modul-System, mit dem man arbeiten kann. Und da bedient man sich in Scala eben der objektorientierten Programmierfähigkeiten. Scala ist mehr objektorientiert, wie es Java ist, im Sinne davon, dass nicht nur die einfachen Klassen Objekte sind, sondern auch Packages. Packages sind auch normale Objekte in Scala, die eben auch eigene Logik miteinander beinhalten können. Zudem steht in Scala auch das Konzept der Trades. Trades sind im Grunde genommen Interfaces, die Implementierungen beinhalten können und aus Komposition mehrerer Trades entstehen größere Bausteine. Also das ist eigentlich das, was man sich eigentlich ursprünglich in der Objektorientierung gewünscht hat, sodass wir Komponenten bauen und ineinander zusammenstecken können. Das Problem ist, dass die Sprachen meistens auf dieser Ebene sich ein Mittel bereit gestellt haben. Wenn man das in der Java-Welt macht, nutzt man meistens sowas wie Spring, als quasi Sprach-Zusatz, um solche Komponenten-Zusammenhänge aufzustellen. In Scala bietet die Sprache Mechanismen, um Anhängigkeiten zwischen Komponenten und Sichtbarkeiten innerhalb der Komponenten und außerhalb der Komponenten zu beschreiben.

Stefan Tilkov: Es war ganz interessant, in dem Clojure Podcast, den ich mit Philipp zusammen aufgenommen habe, hat er sehr viel Wert darauf gelegt über diese generischen Datenstrukturen zu sprechen. Also das war für ihn ein sehr wichtiger Punkt, dass man eben nicht für jeden Mist eine Klasse definiert, sondern sehr viel mit diesen generischen Clojure-Datenstrukturen arbeiten kann. Wie ist das in Scala? Arbeitet man in Scala, wenn man eine fachliche Domäne präsentieren möchte, typischerweise eher mit den generischen Collection-Klassen oder arbeitet man mit konkreten Klassen, die man ausprägt oder ist das mal so mal so, je nach Geschmack?

Tobias Neef: Sicherlich kann man das rein von dem, was die Sprache zur Verfügung stellt, je nach Geschmack tun. Dennoch sehe ich persönlich einen Vorteil darin, Dinge, die explizit in meiner Domäne bestehen, wie ein Kunde oder anderes, auch explizit als Klasse zu beschreiben. Die Frage ist nur, wie viel Overhead bringt dieses explizite Beschreiben der Klasse in meine Programm-Logik? Da muss man sagen, das wird meistens sehr leichtgewichtig in Scala behandelt. Z.B. wenn man einen Kunden definiert, der eine Summe von Attributen hat, kann das relativ leicht über eine sogenannte Case-Klasse beschrieben werden. Eine Case-Klasse ist im Kern das, was man von einer Java-Klasse erwarten würde. Sie hat Attribute und sie hat aber auch ein sinnvolles Verhalten über equals, hashCode und toString. Das sind also Dinge, die man normalerweise als 200 Zeilen Boiler-Plate in jeder Entity- Java-Klasse drin hat. Und wenn man da eben eine Case-Klasse implementiert, bekommt man das quasi als Standardset. Das ist der eine Aspekt, der die Case-Klasse von Java unterscheidet. Der andere ist, dass man hier die Domänen-Modelle auch gerne immutable hält. Also ein Kunde ist erstmal unveränderlich, nachdem er instanziiert wurde. Wenn man einen neuen Kunden definiert, ist das eine Kopie – zumindest aus Sicht des Benutzers. Und hier kommen dann auch wieder die generischen Datenstrukturen zum Einsatz. Denn um das Ganze umzusetzen, werden natürlich Kunden intern auch wieder mit Listen ausgestattet, um daran anhängende Objekte zu verwalten. Also es ist sicherlich einer Kombination aus beidem.

Stefan Tilkov: Wie verhält es sich dann mit den generischen Funktionen, die auf diesen Dingen arbeiten? Also ein Argument ist ja, dass man Wiederverwendung haben möchte und man es eben wiederverwenden kann, wenn man verschiedene Dinge benutzt. Wie funktioniert das da, also wenn ich eine konkrete Datenstruktur habe, wie ist die Brücke zwischen sehr konkreten domänenspezifisch ausgeprägten Datenstrukturen und den generischen Funktion, z.B. aus der Collection-Bibliothek? Also das geht natürlich alles über den generischen Typmechanismus, aber all diese Collection-Operationen arbeiten natürlich auf typmäßig parametrisierten Klassen?

Tobias Neef: Genau, genau. Aber was auf jeden Fall nicht funktioniert, ist jetzt einfach eine Klasse wie eine Map zu verwenden.

Stefan Tilkov: Klar, das wäre dann wirklich das gleiche.

Tobias Neef: Ja genau, dann wäre es wirklich analog wie sowas wie Clojure. Ne, also das ist dann wirklich auch ein expliziter Unterschied zwischen den generischen Typen und den Domänen-Objekten, ja.

Stefan Tilkov: Und der Mechanismus um die Abhängigkeit zwischen einer Funktion, die irgendwas bestimmtes erwartet, und einer einer konkreten Klasse minimal zu halten, würde man das dann wie in Java über Interfaces machen? Also ich würde ich meiner Funktion sagen, welches Interface meine Klassen implementieren müssen, die ich als Parameter da rein reiche und dann müssten die das eben entsprechend tun. Oder gibt es noch einen Mechanismus, der sowas wie strukturelle Gleichheit spezifiziert – also wenn ich eine Funktion habe, die ein Objekt erwartet, dass die zwei Methoden x und y implementiert – oder muss ich dazu ein Interface haben, das xAndY heißt, damit ich sowas machen kann?

Tobias Neef: Zu beidem: ja. Also man kann sowohl über Trades einen Contract definieren, den Objekte einzuhalten haben, wenn sie den implementieren. Das ist erstmal ein Mechanismus…

Stefan Tilkov: Das ist so wie in Java.

Tobias Neef: Genau. Nur halt mit dem Unterschied, dass Trades eben eine Implementierung enthalten können und – ein zweiter wichtiger Unterschied – sie bestimmen können, wie ihr eigener Typ definiert ist. Das klingt erstmal seltsam, aber man kann die this-Referenz innerhalb eines Trades typisieren. Der Effekt ist ein ganz einfacher: man kann sagen, dass das User-Repository von einem Trade des Typs UserDAO abhängig ist. Damit kann bei der Komposition von diesen Objekten sichergestellt werden, dass ein User-Repository erst als Klasse instanziiert werden kann, wenn auch ein User-DAO mit in die Komponente eingemixt ist. Und da die User-DAO jetzt auch wieder unterschiedliche Implementierungen haben kann, kann man so das Zusammenstecken abhängiger Komponenten beschreiben.

Stefan Tilkov: Ok, und die zweite Variante? Das Strukturelle?

Tobias Neef: Ah, ja. Die zweite Variante, das Strukturelle ist auch da, wird aber zur Laufzeit über Reflection umgesetzt. Von daher, wenn bestehende Libraries existieren, die eben dieses Verhalten haben, ist es eine Möglichkeit diese strukturelle Abhängigkeit zu beschreiben, aber es ist jetzt kein Feature, welches sonderlich oft verwendet wird.

Stefan Tilkov: Verstehe, ok. Gut, wie sieht deine Arbeitsumgebung aus, was für eine Toolchain hast du, um mit Scala zu arbeiten?

Tobias Neef: Ja. Meine Basis-Toolchain ist erstmal das Build-Tool. Das bevorzugte Build-Tool in Scala heißt sbt. sbt ist eine interaktive Umgebung, wie man mit Scala-Programmen interagieren kann. Das ist schon mal eine ganz nette Sache, weil man in das Programm rein gehen, sich mit dem aktuellen Classpath einfach mal eine Umgebung starten kann und damit auf einer REPL – also einem Commandline-Interpreter – die Möglichkeit hat, mit Scala zu interagieren und Libraries auszutesten. Man hat auch auf Ebene dieser REPL Vervollständigkeitsfunktionalität. Also man kann gucken, welche Methoden bietet ein gegebenes Objekt, um dort einfach mit neuen Libraries rum zu spielen und die Sprache besser kennen zu lernen. Also das ist oft ein Punkt, gerade wenn man sich mit neuen Code-Ebenen beschäftigen will. Dann ladet man sich das aktuelle Projekt in sbt rein und führt es über die REPL aus. Und man kann damit ein bisschen mit dem Programm interagieren. Also das ist sicherlich ein guter erster Schritt. Auf der zweiten Ebene verwende ich IntelliJ als IDE, die mir dann letztendlich ein bisschen Unterstützung liefert, welche sich das Typsystem zu Nutze macht und mir irgendwelche Refactorings anbietet und Syntax-Highlighting. Also Dinge, die man von einer IDE erwartet. Allerdings ist es nicht unbedingt ein “must-have” innerhalb der Community. Also es gibt da auch Leute, die IDEs gar nicht mögen und lieber mit sowas wie Emacs arbeiten. Da gibt’s auch Plugins, um dann mit Emacs in Kombination mit der sbt-Shell Scala-Programme zu entwickeln. Also es ist so ein bisschen Geschmackssache, ich mag IDEs und verwende daher IntelliJ.

Stefan Tilkov: Was würdest du empfehlen, wenn man starten möchte? Was ist ein guter Einstieg in Scala, auch vom Tooling her? Macht man mit sbt auf der Kommandozeile rum oder womit fängt man am besten an?

Tobias Neef: Also mit sbt auf der Kommandozeile rum machen ist sicherlich eine nette Sache, was allerdings die aller niedrigste Einstiegshürde von allen bereit stellt, ist ein kleines Tool, was es seit ein paar Monaten gibt. Es heißt Typesafe Activator. Das ist im Prinzip eine zip-Datei, die man sich runter lädt und entpackt, auf dem Executable seines Betriebssystems doppelklickt und dann startet ein kleiner Server hoch und eine Seite öffnet sich im Browser. Und innerhalb des Browsers gibt es dann – Entwicklungsentwicklung ist vielleicht übertrieben gesagt – ein kleines Interface zu Scala, in dem man Text eingeben kann, in dem man Syntax- Highlighting bekommt, in dem man diverse Tutorials aufrufen kann und in dem eben auch das, was man eintippt, im Browser direkt evaluiert und ausgewertet wird. Also das ist sicherlich ein nettes Tool, welches man nehmen kann, um sich mit Scala zu beschäftigen. Ein zweites nützliches Tool wurde als Teil der “functional programming”-Vorlesung auf Coursera entwickelt, die der Martin Odersky gehalten hat. Das ist das sogenannte “Scale Worksheet” und das Spannenden daran ist, dass man ein Scala-Programm ganz normal runter schreiben und es als Worksheet ausführen kann. Der Unterschied ist dann, dass sich das Scala-Programm so ein bisschen wie eine Excel-Datei verhält. Also man schreibt das Programm und sieht bei der Auswertung die ganzen Zwischenergebnisse dieser Auswertung. Wenn man irgendwas am Programm ändert, verändern sich halt alle abhängigen Zwischenauswertungen und so hat man eine ganz nette Möglichkeit mit dem Verhalten von Scala umzugehen und es kennen zu lernen.

Stefan Tilkov: So ein bisschen wie Light Table, falls du das kennst, für Clojure.

Tobias Neef: Ich hab’s schon gehört, aber habe mich bisher noch nicht mit beschäftigt.

Stefan Tilkov: Ja, interessant. Ok. Literatur oder andere Starthilfen, was würdest du jemandem empfehlen, der mit Scala starten möchte und die richtige Einstiegshilfe braucht?

Tobias Neef: Also es gibt sowohl Bücher, als auch Online-Ressourcen. Das klassische Buch, was immer zu empfehlen ist, ist das “Odersky Scala Buch”, das “Stairway”-Buch, wie es in der Community genannt wird. Das ist so ein bisschen wie “Java ist eine Insel”, so vom Umfang her – es beschreibt im Grunde genommen die Sprache als solches und den Umgang mit den Sprachen und den Funktionen. Das ist auf jeden Fall ein breiter Einsteig in die Sprache. Und dann gibt es noch diverse eher praxis-orientierte Einstiege. Vor allem gibt es ein Konglomerat von Tutorials und Übungen, die von Twitter bereit gestellt werden, auf deren Github IO Account. Dies wurde nämlich mit dem Hintergrund erstellt, dass halt Twitter relativ viel mit Scala macht und auch neue Software-Ingenieure in Scala ausgebildet werden müssen und deswegen hat man dann so einen pragmatischen Einstieg in viele Scala-Themen geschaffen, die man sich halt da online anschauen kann. Also das ist sicherlich, wenn man eher einen praktisch orientierten Einstieg haben will, auch eine tolle Sache.

Stefan Tilkov: Alles klar, gut. Deine berühmten letzten Worte zu Scala? Was würdest du den Hörern mitgeben?

Tobias Neef: Ja, probiert es einfach mal aus, werft alle Vorurteile, die ihr gegen statische Typisierung habt und gegen Dinge, die man so in irgendwelchen Blogposts von Leuten mit Vorurteilen ließt, über Bord, beschäftigt euch einfach mal mit der Sprache, bildet euch ein eigenes Urteil und ja.

Stefan Tilkov: Alles klar. Vielen Dank Tobias und bis zum nächsten Mal.

Tobias Neef: Danke Stefan, ciao.

In Memoriam ∞ CEO & Principal Consultant

Stefan Tilkov war Geschäftsführer und Principal Consultant bei INNOQ Deutschland, wo er sich vorwiegend mit der strategischen Beratung von Kunden im Umfeld von Softwarearchitekturen beschäftigte. Er war Autor des Buchs “REST und HTTP”, Mitherausgeber von “SOA-Expertenwissen” (beide dpunkt.verlag), Autor zahlreicher Fachartikel und häufiger Sprecher auf internationalen Konferenzen.

Wir trauern um Stefan.

Alumnus

Tobias Neef war bis Dezember 2020 als Entwickler und Senior Consultant bei INNOQ tätig. Sein Fokus liegt in der Konzeption und Implementierung von verteilten Software-Systemen. Aktuell beschäftigt er sich damit funktionale und reaktive Technologien wie Scala, Play und Akka bei Web-Dienstleistern und Großunternehmen zu verbreiten.