Devoxx 2014

Im Jahr 2014 habe ich die Devoxx in Antwerpen besucht.

Hier sind ein paar Notizen dazu:

Was ist Devoxx?

  • Devoxx ist eine Konferenz der belgischen Java-User-Group
  • Belgien ist dreisprachig, Konferenz aber 100% in Englisch
  • Lokation in riesigem Kinokomplex guter Sound, gute Sitze, gute Projektoren, „cool“
  • 8 tracks, bei Keynotes „overflow“
  • Gut organisiert (dies Jahr sicher), unterhaltsamer als andere Konferenzen…
  • Schwesterkonferenzen:
  • Devoxx FR
  • Devoxx PL
  • Devoxx UK
  • Voxxed (Berlin, Ticino,….)

Themen & Hauptsponsoren

  • Java / Oracle
  • Android / Oracle
  • Startups, Business, IT in Großfirmen / ING-Bank
  • Java-Server, JBoss, Deployment / Redhat
  • JVM-Sprachen
  • Web
  • SW-Architektur
  • Security
  • Was sonst noch so ins Umfeld passt und sich jemand traut, vorzutragen…

Scala und Java8

  • Viele Features von Scala sind mit java8-Lambdas auch in Java verfügbar
  • Problem: verschiedene Implementierung, Interoperabilität
  • Bestrebung Scala weiterzuentwickeln damit lambdas von Java und von Scala besser miteinander interoperabel sind.

Monads

  • Konzept aus der Kategorientheorie (5% der Mathematiker machen Algebra, 5% der Algebraiker machen Kategorientheorie, aber für funktionale Programmiersprachen plötzlich relevant)
  • Monoid (+, *, concat,…)
  • Funktor
  • Monad (de: Monade)
    Wikipedia de
    Wikipedia en
  • (T, \eta, \mu)
  • Beispiel List mit einem Functor F: (A\rightarrow B)\rightarrow (List[A]\rightarrow List[B])
    \mu ist flatten: List[List[A]]\rightarrow List[A]; \eta: A\rightarrow List[A]

Probability & Decisions

  • Thema: Software für automatische Steuerung
  • Heuristik auf Wahrscheinlichkeitstheorie
  • False positives / false negatives: was tut weh? (meistens beide…)
  • Wahrscheinlichkeitstheorie und Verwendung gut erklärt

Clojure

  • Clojure ist eine andere JVM-Sprache
  • Ein Lisp-Dialekt, zu erkennen daran, dass der Quelltext überwiegend aus runden Klammern besteht (+ (* 3 4) (* 5 6))…
  • Funktionales Paradigma stark unterstützt
  • Dynamische Typisierung (für uns: Alles als „Object“ deklariert, implizite Casts bei Methodenaufrufen)
  • Nach Java selbst, Scala, Groovy und Javascript die mir am fünfthäufigsten begegnende JVM-Sprache

MapReduce

  • „No one at Google uses MapReduce anymore“
  • Google hat das verallgemeinert
  • Optimierungen: spare Schritte, fasse zusammen etc.
  • Als Cloud-Service angeboten (Cloud Dataflow)

Key Note ING

  • ING versteht sich als „open bank“
  • Nicht gemeint, dass das Geld „offen“ rumliegt, aber offen für neue Ideen
  • Schnittstelle zur Bank ist Mobile-App
  • IT hat viel Einfluss („IT driven business“)
  • Man schaut, was gut machbar ist
  • Agile Prozesse (Scrum) vs. Enterprise IT
  • Umbau der IT auf diese agilen Prozesse schrittweise
  • „Enterprise IT is what does not work“

Material Design

  • Vortrag über GUI-Design mit Android und Web
    Material Design
  • Visuelle Ideen für beide Plattformen verfügbar
  • Polymerdesign für Web

SW-Architektur mit Spring

  • Spring 4.1
  • „works with WebSphere“
  • DRY
  • Lambda aus Java8 kann viele APIs vereinfachen statt anonymen inneren Klassen mit einer Methode
  • Generic Messaging Interface (war JMS nicht schon das???)
  • Caching, Achtung beim Testen abschaltbar
  • Testen auf http://start.spring.io/
  • Spring funktioniert mit Java gut, mit Groovy auch (aus demselben Haus…) mit Scala „experimentell“

Lambda_behave

  • High-Level testing-Framework
  • Verwendet Java8-Features (Lambda etc)
  • Beschreibung in natürlicher Sprache
  • Failure-Meldungen lesbar
  • Wie Cucumber…
  • Zufallsquelle konfigurierbar (extrem wichtig für manche Tests!!)

Builtin Types of Scala and Java

  • In Java gibt es „primitive“ (long, int, byte, char, short, double,…)
  • Probleme bei Arithmetik mit int, long & Co:  Überlauf findet unerkannt statt
  • Mit float und double Rundungsfehler
  • Mit BigInteger, BigDecimal, Complex, Rational fehleranfällige, umständliche und unlesbare Syntax
  • In Scala kann man a=b*c+d*e auch für neu definierte numerische Typen schreiben.
  • Anmerkung dazu: Oracle-Java-Leute finden Idee von Operator-Überladen für numerische Typen inzwischen interessant, solange man nicht Exceptions mit Collections multipliziert und durch Swing-Komponenten dividiert…
  • Spire library

Zukunft von Java (9, 10, …)

Teil I

  • Q&A-Sitzung (Fragen per Twitter schreiben)
  • Numerische Typen sind Thema. Dass primitive Typen sich so verhalten wie heute und quasi der „default“ sind, wird sich nicht ändern
  • Thema Generics und Type-Erasure (wo ist das Problem)?
  • Jigsaw vs. Jars vs. OSGi  noch offen, aber jar bleibt
  • Jigsaw-Repository  Wird man wohl bei maven-Central, Oracle funktioniert nicht so, dass sie jigsaw-Repository für third-Party bei sich hosten

Teil II

  • Benchmarking mit Java schwierig wegen hotspot-Optimierung
  • JMH gutes Tool dafür
  • Neue Ideen immer schwierig umzusetzen, weil man kompatibel zu alten Versionen sein will.
  • Java bekommt vielleicht „repl“

Teil III

  • Collection literals (für Java 7 versprochen!!!) sind für Java8 nicht gekommen, für Java9 unwahrscheinlich
  • Mit Value-Types aber eher zu haben, daher nicht vorher…
  • Für Set und List mittels
    new TreeSet(Arrays.asList(m1, m2, m3,…., mn))
    schon heute einigermaßen darstellbar
  • Für Maps wenn man sowas wie Pair-hätte, was wiederum auf „Tupel“ aufbaut, die kommen werden, wenn es Value-Types gibt

Teil IV

  • Tail-Recursion kann nun optimiert werden
  • Wegen Security-Manager, der Stacktrace analysiert hat, lange nicht möglich
  • C und Lisp können das seit Jahrzehnten…
  • Aussage: Generics sind schwierig, aber wenn man sie mal verstanden hat, sind sie leicht… Also „dranbleiben“
  • Covarianz und Contravarianz (Bei Array falsch gelöst)

Teil V

  • Arrays 2.0: Indizierung mit long möglich, ein bißchen wie List, aber mit Array-Syntax… (Studien und Papers, nicht konkret)
  • Listen haben heute extrem-Implementierung ArrayList und LinkedList. Wir wollen „sophisticated“ List vielleicht Hybrid
  • Checked-Exceptions: kritisches Thema, schwierig mit Generics und mit Lambda. Zu viele Exceptions sind checked. Z.B. bei close()

Semantische Quelltextanalyse

  • Hilfreich für High-Level-Testing-Tools
  • Statische und dynamische Analyse
  • Dataflow-Analyse: ungeprüfte Daten von außen  SQL-injection, aber auch CSS, HTML, JavaScript, JVM-Sprachen/Bytecode

Funktionale Ideen in Java

  • Funktional:
  • Funktionen oder Methoden sind „first class citizen“
  • Higher order Functions (konnte schon C)
  • Closure
  • Immutability (Funktion gibt immer dasselbe raus)
  • Unter der Tischdecke „lazy“-Konstrukte
  • Bei großen Strukturen immer Frage: Immutability vs. Performance
  • Aber Funktional ist Thread-freundlicher

50 new things in Java8

Teil I

  • Lambda (s.u.)
  • Streams (s.u.)
  • Default-Implementierungen in Interfaces
  • Date/Time (wie Joda-Time)
  • Optional (besser als null)
  • Libraries können mit Lambda arbeiten
  • Parallel (mit Vorsicht verwenden)

Teil II

  • String.join()
  • So was wie „find“ in Unix/Linux
  • Comparator schreiben einfacher
  • Maps of Maps, Maps of Collections einfacher
  • Sortieren besser: quicksort statt mergesort, parallelisierbar

Groovy für Android

  • Problem bei JVM-Sprachen außer Java: Bibliothek muss in jeder App mitkommen
  • Lösung: Tool zum jar-optimieren
  • Zweites Problem: dynamische Sprachen müssen auf Device „on demand“ kompiliert werden
  • Lösung: „statisch“ Programmieren, dynamische Features möglich, aber nicht performant

Lambdas

  • Lambdas sind anonyme Funktionen
  • Idee gegeben: Interface XY mit einer Methode uvw()
  • Statt
    XY xy = new XY() {
    public long uvw(long x) { return x*x }
    };
    neu
    XY xy = x -> x*x;
  • kürzer, lesbarer, wartbarer, man kann Interface oft sparen
  • Closure bedeutet, dass (final-)Variablen aus dem umgebenden Kontext dort eingebunden werden
  • Instanz-Methoden sind eigentlich auch Closure, sie binden die Instanz in der sie definiert sind Closure-mäßig ein.

 Streams

  • Streams sind im Dunstkreis von Collection, Iterable, Iterator, aber was anderes
  • Erlauben Methoden, die „Funktion“ auf alle Elemente loslassen
  • Elegant programmierbar sind Dinge wie
    • Summe
    • Produkt
    • Maximum
    • Erstes / Letztes Elment mit Eigenschaft
    • alle mit Eigenschaft
    • alle transformierten Elemente
  • Sinnvoll: nicht dasselbe wie Iterable, Iterator, Collection,…

 Links

Share Button

Memory bei Java-Zeichenketten

Es hat von Java 1.6 nach Java 1.7 eine für den Memoryverbrauch relevante Änderung gegeben.
Bis Java 1.6 wurde bei substring eine Zeichenkette konstruiert, die das selbe interne Array von Zeichen (char) referenziert und nur andere Werte für Anfang und Länge enthält. Das hatte den Vorteil, dass in vielen Fällen Memory gespart wurde, weil man die Teilzeichenkette nicht nochmal speichern musste. Auch das Umkopieren wurde gespart. Nun war es aber wichtig, in manchen Fällen so etwas wie
String mySubString = new String(origString.substring(3, 5));
statt
String mySubString = origString.substring(3, 5);
zu verwenden, wenn nämlich origString sehr viel länger als mySubString war und sehr viel früher obsolet wurde als mySubString. Wer das vergessen hat, hat so ein Memoryleak gebaut.
Das wurde in Java 1.7 „gefixt“, nun wird das Array mit den Zeichen intern immer kopiert, weil die Entwickler nicht verstanden haben, mit diesen beiden Möglichkeiten richtig umzugehen. Leider hat man nun aber die andere Möglichkeit nicht mehr zur Verfügung.
Was kann man tun, wenn man eine Memory-intensive Applikation schreiben will, in der viele langlebige Zeichenketten vorkommen, die Teil einer längeren, ebenfalls langlebigen Zeichenkette sind?
Die erste Frage ist immer, ob man mit der neue Implementierung nicht doch leben kann.
Da die Zeichenkette sehr tief in Java verankert ist, kann man sie nicht so leicht ersetzen bzw. muss etwas hässlicheren Code schreiben, um explizit die eigene Zeichenkette zu verwenden. Wenn nötig ist das aber machbar: Man nimmt von gnuClasspath java.lang.String und refactored das zu einem anderen Package- und Klassennamen. Daraus lässt sich dann leicht das gewünschte bauen. Wichtig sind Konversionsroutinen nach java.lang.String in beide Richtungen.

Links:

Share Button

Auf GPU rechnen

Heutige Rechner haben bekanntlich sehr leistungsfähige CPUs mit „Quadcore“ und mehr.
Aber auch die Grafikkarten sind ziemlich toll geworden und haben zum Teil auch so große eigene Lüfter wie die CPU.
Sie haben eine Menge eigenes Memory und sie können recht komplizierte Operationen selbsttätig rechnen, natürlich nur für die grafische Darstellung.

Nun stellt sich die Frage, ob man das auch sonst zum Rechnen benutzen kann. Man hört Geschichten, dass die Grafikkarte um Größenordnungen leistungsfähiger als die CPU sei. Warum schaffen das die Grafikkartenhersteller so leicht nebenbei, besser als die CPU in deren Kernaufgabe zu sein und außerdem noch Grafik darzustellen?

Grafikkarten bieten die Möglichkeit, massiv parallel zu rechnen, aber nach dem Prinzip SIMD. Das heißt, dass ein oder sehr wenige Befehlsströme verarbeitet werden und eine große Zahl von Recheneinheiten jeweils den identischen Code mit verschiedenen Daten ausführt. Für Vektoroperationen ist das optimal. Und es erklärt auch die brachiale Leistung der Grafikchips, weil man den aufwändigen Teil, Maschinencode zu interpretieren, nur einmal (oder wenige Male) baut und nur die eigentliche Recheneinheit mehrfach. Man bekommt so mit derselben Siliziummenge mehr Rechenleistung, aber mit der Einschränkung, SIMD (oder fast SIMD) zu sein. Aber man hat diese Geräte in den Rechnern drin. Es gibt sogar „Grafikkarten“, die nur rechnen können, aber bei denen man die eigentlichen Grafikchips weggelassen hat. Und viele Rechner haben den Grafikkern mit auf dem selben Chip wie die CPU-Kerne, so dass man eine einfache Grafik zur Verfügung hat, aber man baut dann doch zusätzlich noch eine externe Grafikkarte ein, die noch besser ist. Selbst ohne die Grafikfähigkeit zu gefährden hat man also eine GPU zum Rechnen übrig.

Nun ist das Programmieren dieser Geräte nicht so einfach. Grundsätzlich muss man sich mit einem völlig neuen Programmiermodell herumschlagen. Viele Algorithmen lassen sich speziell für SIMD entwickeln, aber sie sind anders als was wir gewohnt sind und automatisch lässt sich da nur beschränkt etwas optimieren. In der Regel muss man also mit C das Programm schreiben und kann dabei auf OpenCL bauen. Das ist ein nützlicher Standard, der einiges erleichtert, aber es bleibt schwierig. Für manche Zwecke lohnt es sich aber, den Entwicklungsaufwand zu treiben.

Vor ein paar Tagen habe ich drei Vorträge über Ansätze gehört, F# zu verwenden, um die GPU zu programmieren. Man kann also unter Einhaltung gewisser Regeln F#-Programme schreiben, die sich etwa in OpenCL übersetzen lassen und dann compiliert die GPU nutzen:

  • FSCL: Homogeneous Programming and Execution on Heterogeneous Platforms (by Gabriele Cocco, Pisa University)
  • Professional GPU Development with F# and the Alea Compiler Suite (by Daniel Egloff, QuantAlea)
  • New Abstractions for Radically Simplified GPU Programming in F# and .NET (by Luc Bläser, HSR, based on joint work with QuantAlea)

All diese Ansätze sollten schon heute oder zumindest in zukünftigen Implementierungen auch für Mono laufen und damit für Linux zur Verfügung stehen.

Es würde wohl den Rahmen dieses Artikels sprengen, im Einzelnen darauf einzugehen, aber vielleicht kann ich noch Links bekommen, die auf weiterführendes Material zu den einzelnen Lösungsansätzen führen.

Share Button

Einheiten mitführen

Typische Programme rechnen mit Größen, also mit Zahlwerten, die mit irgendeiner Einheit kombiniert sind. Das können Temperaturen, Längen, Zeitdauern, Geldbeträge und vieles mehr sein. Weil früher jedes Byte teuer war, hat sich etabliert, dass man nur mit den Zahlwerten arbeitet und die Einheiten implizit durch die Programmlogik ins Spiel kommen. Das funktioniert in einfache Fällen, wo die Einheit wirklich klar ist. Wenn aber mehrere Einheiten im Spiel sind, ist es besser, diese auch mitzuführe, außer bei dieser massiv strapazierten inneren Schleife, die man in Wirklichkeit im ganzen Programm doch nicht findet.

Man kann das durch Typisierung erreichen, also etwa Typen wie „KelvinTemperatur“ definieren oder man kann einen Typ „Temperatur“ definieren, der den Betrag und die Einheit enthält. So können bestimmte Fehler zur Laufzeit oder sogar zur Compilezeit entdeckt werden oder sogar passende Umrechnungen verwendet werden, wenn man eine Fahrenheit und eine Kelvintemperatur addiert.

Java bekommt hier einen Minuspunkt, weil man dort nicht vernünftig die elementaren Operationen für solche Typen definieren kann.

Die meisten anderen modernen Programmiersprachen können das natürlich.

Share Button

Non-Blocking I/O

In Posix-Systemen (Linux, Unix, MacOS X,…) basieren die I/O-Operationen hauptsächlich auf den Systemaufrufen read(..) und write(..). Die meisten anderen I/O-Operationen lassen sich darauf zurückführen und auch I/O von anderen Programmiersprachen als C dürfte letztlich indirekt zu read() und write() führen. read() ist eine Funktion, die einen (numerischen) Filedeskriptor, einen Pointer auf einen hinreichend großen Speicherbereich („Buffer“) und eine Größenangabe annimmt und im Idealfall die angegebene Anzahl von Bytes liest. Das funktioniert gemäß dem Unix-Prinzip „everything is a file“ ganz einfach mit Dateien (Files), Geräten (Devices), (Pseudo-)terminals, Pipes (Fifos), TCP-Streams etc. Wenn ein Fehler auftritt, wird ein negativer Wert zurückgegeben und man muss errno abfragen, um den Fehler zu finden. Den negativen Fehlercode direkt zurückzugeben wäre besser gewesen, aber das kann man nun nicht mehr ändern. Eine 0 bedeutet, dass man das Ende erreicht hat. Dies wird nicht durch den Inhalt der übertragenen Daten ausgedrückt, denn jede beliebige Bytesquenz wird akzeptiert und verarbeitet, sondern separat übermittel. Wenn weniger als die gewünschten Bytes da sind, dann wird entsprechend weniger zurückgegeben. Der Rückgabewert gibt an, wieviele Bytes tatsächlich gelesen wurdn. Das sind alles Situationen, in denen das read sofort anfangen kann, auch wenn die eigentliche Operation etwas länger dauern kann. Bei Dateien kann der Fall, dass 0 Bytes gelesen werden, nur am Dateiende und natürlich bei Fehlern auftreten. Bei Pipes und TCP-Streams ist aber möglich, dass man auf Daten wartet und keine da sind. Mit 0 Bytes gibt sich das read normalerweise nicht zufrieden und wwartet stattdessen auf Daten. Das ist eine vernünftige Sache, da es in der Regel das ist, was man will. So vereinfacht sich die Programmierung. Write verhält sich entsprechend, bei Pipes kann man auch nur schreiben, wenn auf der Gegenseite die Daten abgenommen werden. Ein Prinzip das heute in der Scala- und Akka-Welt als die größte Innovation des Jahres gerühmt wird und den Namen „Backpressure“ bekommen hat.

Non-Blocking funktioniert auch mit read(). Mit open() oder nachträglich mit fcntl() wird eingestellt, dass das I/O non-blocking ist. read() versucht nun, Bytes zu lesen und wenn das klappt ist alles wie beim blocking I/O. Wenn das read() keine Daten findet, wartet es nicht auf Daten, sondern kommt sofort mit -1 zurück und in errno steht EAGAIN oder EWOULDBLOCK o.ä. write() entsprechend.

Nun hätte man die Möglichkeit, regelmäßig einen Filedescriptor abzufragen und wenn Daten kommen, darauf zu reagieren und sonst etwas anderes zu tun. Ohne Multithreading…

Interessant ist der Fall, dass man mehrere Filedescriptoren gleichzeitig hat und von diesen Daten lesen oder schreiben will. Man weiß nicht wann welche Zugriffe möglich sind. Das wäre mit einem Thread pro Filedescriptor machbar. Oder mit non-Blocking-I/O und Polling, also einer Schleife, die dauernd alle Filedescriptoren durchprobiert.. Besser ist es aber select() oder pselect() oder poll() zu verwenden, die semantisch sehr ähnlich funktionieren, aber aus historischen Gründen koexistieren. Wer eigenen Code schreibt, kann sich eine der drei Funktionen aussuchen und damit arbeiten, wer aber Code von anderen Leuten lesen und ändern muss, sollte alle drei kennen. Dies ist eine sehr elegante Lösung, weil man noch ein Timeout dazunehmen kann. Die Spezialfälle 0 und unendlich für das Timeout werden auch sinnvoll unterstützt.

Das „non-Blocking-Prinzip“ wird bei der Posix-Systemprogrammierung oft angeboten, z.B. beim Locken von Mutexen, Semaphoren oder beim File-Locking.

Man sollte non-blocking-I/O nicht mit assynchronem I/O verwechseln. Dazu kommt vielleicht ein anderer Artikel..

Share Button

Ist Ruby funktional

Wenn die Liste der funktionalen Sprachen erstellt wird, dann tauchen Haskell, Scala, Erlang, F#, Clojure und einige andere Lisp-Varianten auf.

Wenn man sich anschaut, welche Merkmale funktionale Sprachen auszeichnen, dann stellt sich die Frage, ob das nicht alles mit Ruby auch machbar ist.

Schauen wir einmal was man so typischerweise findet, meist auf Englisch:

  • Functions as „first class citizen“
  • Closures
  • Pure Functions
  • Higher Order functions
  • Everything returns a value
  • Immutability
  • No hidden state
  • Prefer recursion over iteration (immutable loop variable…)

Funktionen existieren in Ruby also losgelöste Objekte, in verschiedenen Formen, z.B. als proc, als lambda, als anonymer Block und durch Referenzierung einer Methode über ihren Namen (Reflection).

Closures werden von Ruby problemlos unterstützt, bei all diesen Formen der Funktionen werden Variablen aus dem definierenden Kontext eingebunden, wo sie referenziert werden. Methoden kann man übrigens als Spezialfall von Closures ansehen, weil sie als Kontext das Objekt einbinden, zu dem sie gehören.

Unter „pure function“ versteht man Funktionen, die den Funktionen aus der Mathematik entsprechen. Sie haben absolut keinen Seiteneffekt, sind reproduzierbar, können in beliebiger Zahl parallel ausgeführt werden und geben bei mehrfachen Aufrufen immer dasselbe Resultat. Sie eignen sich für Memoize, was nun eigentlich wieder ein Seiteneffekt ist, aber sozusagen ein transparenter. Man denke an sort() und sort!() in Ruby. Dies verlangt Disziplin vom Entwickler und gute Dokumentation.

„Higher Order Functions“ sind Funktionen höherer Ordnung, die also selbst Funktionen als Parameter oder Rückgabewert haben. Das ist in der funktionalen Programmierung Routine und nicht so ein spezieller Spezialfall, den man mal alle paar Jahre benutzt, wie Funktionspointer in C. Typische Beispiele sind Methoden wie inject(), map(), group_by()… each() sollte man nur verwenden, wenn man die Seiteneffekte wirklich braucht.

Alle Ausdrücke haben einen Wert. Das ist recht gut in Ruby umgesetzt, z.B. x = if (...) ... else ... end kann man verwenden…

Immutibility (Unveränderbarkeit) ist die Achillesferse. Es wird nicht wirklich gut unterstützt. Man soll Variablen nicht neu zuweisen und auch nicht irgendwas mutieren. Warum nimmt man nicht freeze()? Wir brauchen ein deepfreeze(), aber was bedeutet das? Wie sieht es mit collections aus? Es gibt immer Möglichkeiten, diese unverändert zu lassen und mit jedem Schritt eine neue Collection zu produzieren. Dasselbe gilt für Zeichenketten.

„No hidden State“, also kein versteckter Zustand. Man kann Kontextobjekte haben und herumreichen. Zustand ist unerwünscht und sollte kontrolliert an wenigen Orten gehandhabt werden.

„Recursion instead of Iteration“ steht für Rekursion statt Iteration. Funktionale Sprachen unterstützen Optimierung bei Tailrekursion (Endrekursion). Einige andere Sprachen, z.B. C mit gcc, Scala und viele Lisp-Dialekte, aber nicht Java, unterstützen diese Optimierung und erlauben es, zumindest Endrekursion ohne zu große Furcht einzusetzen. Bei Ruby hängt es von der Version und den Einstellungen ab, ist also mit Vorsicht zu genießen. Der Ruby-Ansatz ist es eher, die Iteratorn zu verwenden.

Fazit: Man kann mit ein paar Einschränkungen in Ruby funktional programmieren, aber es erfordert etwas mehr Disziplin, weil einige Dinge vom Entwickler beachtet werden müssen und nicht von der Sprache unterstützt werden.

Share Button

Zufällige Zeichenkette erzeugen

Oft braucht man so eine zufällige Zeichenkette, die nur aus bestimmten Zeichen bestehen darf.

Hier ist eine einfache Ruby-implementierung dafür:


#!/usr/bin/ruby

arr = ('a'...'z').to_a + ('A'...'Z').to_a + ('0'...'9').to_a + ['.', '/']
val = (0..16).inject("") do |a, x| i = (arr.size() * rand()).to_i;a + arr[i] end
puts val

Es wird eine 16-Zeichen lange Zeichenkette generiert, die aus den Zeichen [a-zA-Z0-9./] besteht.

Share Button

Scala Days in Berlin 2014

English

Am 16., 17. und 18. Juni 2014 war ich bei der Konferenz „Scala Days“ in Berlin. Wie so oft bei diesen Veranstaltungen gibt es einen Haufen Vorträge, in diesem Fall bis auf die jeweilige „Keynote“ jeweils vier gleichzeitig. Das Veranstaltungslokal war wie bei der Devoxx in Antwerpen ein Kino, allerdings in diesem Fall schon lange umgewidmet für andere Zwecke, aber gute Projektoren gab es noch. Große Themen waren Fragen des Compilerbaus und wie man mit der richtigen funktionalen Perspektive aus der relativ einfachen Aufgabe, einen Interpreter zu schreiben, zu der schwierigen Aufgabe kommt, einen Compiler zu schreiben. Das hilft sicher, Sprachkonstrukte in Scala zu verstehen, aber die Idee wurde auch auf andere Felder angewendet, etwa um SQL-Queries zu kompilieren und zu optimieren oder um mit einem aus Compilercode gebauten Programm Quelltexte zu analysieren.

Ein anderes großes Thema waren „Streams“, die für Webservices nützlich sein sollen. Im Gegensatz zu klassischen Webservices, bei denen man den ganzen Request erstmal in Empfang nimmt, wurden auch Konzepte behandelt, um sehr große oder quasi unbegrenzte Requests zu verarbeiten. Dazu muss man diese natürlich schon verarbeiten, sobald eine gewisse nutzbare Datenmenge angekommen ist.

Ein kleines, aber interessantes Thema war die Entwicklung von Android-Apps mit Scala. Bekannt ist als Ansatz dafür natürlich Scaloid, aber hier wurde Macroid, ein alternativer Ansatz vorgestellt. Es sah vielversprechend aus, dass man mit weniger Code gute Android-Apps schreiben kann. Eine große Sorge ist, dass diese Scala-Apps den Speicher sprengen. Weil sie zusätzlich zu den vorinstallierten Java-Libraries noch Scala-Libraries brauchen, die etwa 5 MB groß sind, vergeht einem schnell der Appetit, außer man setzt gerootete Android-Devices voraus, auf denen die Scala-Libraries vorinstalliert sind. Das Thema verliert aber ein bißchen seinen Schrecken, weil der Build-Prozess einen Schritt enthält, in dem unnötige Klassen aussortiert werden, so dass am Ende nur das installiert wird, was man wirklich braucht. Wenn man so weit geht, Akka auf dem Mobiltelefon laufen zu lassen, wird das aber spannend, weil Akka viel Reflection und damit sehr viel fehleranfällige Konfiguration für diesen Optimierungsschritt benötigt.

Interessant war auch das Thema API-Design. Vieles war deckungsgleich mit Dingen, die ich bei einer API-Design-Schulung für Perl von Damian Conway vor ein paar Jahren gehört hatte, aber es gibt natürlich Scala-spezifische Themen, die auch interessant sind. Es ist erstaunlisch schwierig, Binärkompatibilität von Klassen zu erzielen und zwingt auch zu unschönen Kompromissen. Aber die gehören wohl auch in der Scala-Welt zum Leben.

Share Button

Closures III (in C)

English

Geht so etwas überhaupt?

Ein Element sind die Funktionspointer. Es ist immer recht schwierig, die Signatur davon richtig zu treffen, aber ein typedef hilft.

Die nächste Schwierigkeit ist, dass C normalerweise keine inneren Funktionen erlaubt und dass man auch keinen Kontext einbinden kann.

Das lässt sich lösen:

Die Funktion hat einen weiteren Parameter für ein Context-Struct, in dem die Variablen eingebunden werden.

struct closure;
typedef int (*fun_type)(const struct closure *context, const int param);

Das sieht so aus, dass dort die Variable(n) und ein Funktionspointer enthalten sind:

struct closure {
  int x;
  fun_type fun;
};

Nun muss man die Funktionen ja in für Closures gemachten Programmiersprachen trotzem noch hinschreiben, aber anonym und am richtigen Ort.
In C muss man alle möglichen Funktionen regulär, aber mit der fun_type-Signatur von oben definieren. Die Referenzen auf die eingebundenen Variablen müsse vom context-Parameter kommen, statt implizit verfügbar zu bleiben, z.B.:

int f(const struct closure *context, const int param) {
  return (context->x) + param;
}

Die Funktion 2. Ordnung, die die Closure zurückgibt, kann man nun auch definieren, man muss nur die C-Schreibweise akzeptieren und statt die Funktion an Ort und Stelle zu definieren eine der vorher definierten Funktionen referenzieren. Auch das ist äquivalent zu Closures:

struct closure *adder(int x) {
  struct closure *result = malloc(sizeof(struct closure));
  result->x = x;
  result->fun = f;
  return result;
}

Und so kann man das ganze dann verwenden:

int main(int argc, char *argv[]) {
  int retcode;
  if (argc < 2) {     usage(argv[0], "not enough parameters");   }   int x = atoi(argv[1]);   int y = atoi(argv[2]);   struct closure *cl = adder(x);   int i;   for (i = 0; i < y; i++) {     printf("cl(%d)=%d\n", i, cl->fun(cl, i));
  }
}

Das komplette Beispiel ist auf github.

In Scala sieht das natürlich viel kürzer aus:

object Closure {
  def main(args : Array[String]) : Unit = {
    val x : Int = args(0).toInt
    val y : Int = args(1).toInt
    val f : ((Int) => Int) = adder(x);
    val arr = (1 to y).map(f)
    println(arr.toString)
  }

  def adder(x : Int) : ((Int) => Int) = {
    (y => x+y)
  }
}

Auch das ist auf Github

Share Button

Getter und Setter

In der objektorientierten Programmierung gilt es als fortschrittlich, getter und setter zu verwenden, statt auf Attribute direkt zuzugreifen, weil das einem die Flexibilität gibt, später auf berechnete Attribute umzuschwenken. Etwas hässlich ist das, weil die getter und setter, etwas willkürlich den Attributnamen mit so einem vorangestellten „get“ oder „is“ oder „set“ und eventueller Umwandlung der Groß- und Kleinschreibung einzelner Zeichen versehen. Eine subtile Besonderheit ist, dass es verwirrend wird, wenn Attributnamen mit „get“, „is“ oder „set“ beginnen. Gerade Boolean-Attribute ist man versucht mit „hasSomething“, „isSomething“, „doesSomething“, „canSomething“, „mustSomething“,… o.ä. zu benennen, was dann zu dem Getter „getIsSomething()“ oder „isIsSomething()“ führt. Oder man lässt in dem Fall das Präfix weg, aber nur beim Getter…

Schöner ist es, wenn man Getter und Setter natürlich bennen kann, wie das z.B. in C#, Ruby und Scala der Fall ist: Man schreibt den Getter so, als würde man das Attribut public machen und darauf zugreifen, aber hat durch die entsprechenden Sprachkonstrukte die Möglichkeit, die Getter und Setter durch andere Implementierungen zu ersetzen, wenn der Bedarf besteht. Es gibt sicher wichtigeres, aber das ist zumindest schöner, lesbarer und deshalb weniger fehleranfällig. Und sprachlich auch sauberer als diese „halb-magic“-Bedeutung von „get…“, „set…“ und „is…“.

Im Grunde genommen sind aber auch Zugriffe auf Listen und Maps oft eine Art Getter und Setter:
y=a.get(pos) könnte man auch als y=a[pos] schreiben wollen, entsprechend a.put(pos, x) auch als a[pos]=x. Dasselbe gilt für Maps mit u=m.get(k), was schöner und intuitiver als etwas in der Art von u=m[k] wäre. Oder statt m.put(k, v) so etwas wie m[k]=v. Aus genügend abstrakter Sicht ist das nicht so wichtig, aber wenn die Lesbarkeit sich verbessert, macht man weniger Fehler und so hat man pragmatisch gesehen einen kleinen Qualitäts und Effizienzgewinn mit der Zuweisungsschreibweise.

Nun sind aber Setter in Wirklichkeit oft problematisch. Es ist immer gut, Objekte immutable zu haben, weil man sie dann problemloser zwischen Threads herumreichen kann, ohne dass es zu Fehler bei gleichzeitigen Zugriffen kommen kann. Nun stellt sich aber die Frage, wie man dann das Objekt konstruieren soll. Ein Konstruktor mit positionalen Parametern ist zwar möglich, aber oft nicht sehr lesbar, wenn die Parameterliste nicht völlig überschaubar und klar ist. So etwas wie benannte Parameter könnte sehr viel helfen. Ein anderes Muster ist es, ein temporäres Objekt mit Settern aufzubauen und dann daraus das eigenteliche unveränderliche (immutable) Objekt zu generieren. Man kann dafür spezielle Setter nehmen, die jeweils das veränderte Objekt zurückgeben und das etwa so etwas schreiben wie
SomethingImmutable s = new SomethingTemp().setX(x).setY(y).setZ(z).toSomething(),
was nicht superschön ist, aber wenn man auf Java Wert legt, doch eine Möglichkeit.

Hier zeigt sich auch, warum es so schön ist, wenn man Listen und Maps und vielleicht andere Collections einfach mit allen Elementen konstruieren und dann gleich immutable machen kann. In Java geht das für Listen immerhin schon mit
Collections.immutableList(Arrays.asList(a, b, c, d, e, f, g, h))
machen. Wobei diese Konstruktion relativ neu ist und wegen der Konstruktionsphase Collections nicht defaultmäßig immutable sein können. Immerhin könnte man ein
Arrays.asImmutableList(T..t)
definieren. Oder eine Methode auf List
.immutable().
Schöner (klarer, lesbarer, weniger fehleranfällig) wäre es aber, wenn man das als
[a, b, c, d, e, f, g, h]
schreiben könnte. Für die Ausgabe von Listen mittels toString() wird so etwas ja schon verstanden. Für das Konstruieren von Maps gibt es in anderen Programmiersprachen auch Schreibweisen, die etwa so aussehen wie
m = { k1 => v1, k2 => v2, k3 => v3, k4 => v4}.
Will man sich normalerweise dafür interessieren, welche Map-Implementierung jetzt genommen wird? So etwas ließe sich als
m = new TreeMap{k1 => v1, k2 => v2, k3 => v3, k4 => v4}
schreiben. Solche Dinge waren für Java 8 vorgesehen, sind aber wohl in letzter Minute rausgeflogen oder auf Java 9 verschoben worden.

Share Button