SlideShare ist ein Scribd-Unternehmen logo
Scalaz Einführung
für Java Programmierer
Bernhard Huemer | IRIAN
bernhard.huemer@irian.at
@bhuemer
Agenda
Keine Einführung in Scala
Keine Einführung in Scalaz
("Haskell für Scala")
Motivation
Transaktionen mit Monads
Type classes bzw. Ad-hoc polymorphismus
ev. Arbeiten mit der Datenbank ohne* Seiteneffekte
ev. Dependency Injection
Funktionale APIs gestalten
* Fast ohne
Referential transparency
"An expression is said to be referentially transparent if it can be replaced with its value without
changing the behavior of a program." (d.h. keine Seiteneffekte)
def square(i: Int) = i * i
// Bad! Referenziert auch etwas anderes als Parameter
def now() = System.currentTimeMillis()
Keine Reassignment von Variablen
Keine Setter, keine Mutations
Keine Exceptions
Keine Konsolenein-/ausgabe
Keine Netzwerkkommunikation
...
Exceptions als Beispiel
def f(): Int = {
val y = g()
try {
y + y
} catch {
case e: Exception => 1337
}
}
def f(): Int = {
try {
g() + g()
} catch {
case e: Exception => 1337
}
}
def g(): Int = { throw new Exception("No!"); }
Checked vs. unchecked exceptions (etwas) irrelevant
Compiler versichert, dass Seiteneffekte behandelt werden
Besser: Sie treten gar nicht erst auf
Viel zu triviales Beispiel, ich weiß!
Exceptions als Beispiel (2)
class Cache
(cacheLoader: CacheLoader) {
def get(key: Key): Value = {
// ...
}
}
trait CacheLoader {
def load(key: Key): Value
// throws Exception!
}
class Cache
(cacheLoader: CacheLoader) {
def preload() {
// ...
}
def get(key: Key): Value = {
// ...
}
}
Refactoring mit oder ohne Seiteneffekte?
Exceptions bei preload: Falsch/teilw. initialisierter Cache, fehlendes
Exception-Handling*, etc.?
Exceptions bei get: Mühsam
* z.B: Applikation startet nicht mehr, weil unbenützter Cache Key nicht geladen werden kann, andere Initialisierungen
werden nicht eingeleitet, etc. - Autor von Cache hat keinen Einfluss darauf!
Call Stack Abhängigkeit
Es treten immer dann Probleme auf,
wenn wir den Call Stack irgendwie ändern.
Multi-threaded Umgebungen
Web Services
RMI oder sonstiges Remoting
generell jegl. Netzwerkkommunikation
...
"Do or do not. There is no scala.util.Try[A]." - Yoda
scala.util.Try[A]
import scala.util.{Try, Success, Failure}
def divide: Try[Int] = for {
dividend <- Try(Console.readLine("Dividend:n").toInt)
divisor <- Try(Console.readLine("Divisor:n").toInt)
} yield (dividend / divisor)
val result: Try[Int] = /* .. */
val resultInt = if (result.isSuccess) { result.get } else { 0 }
val resultInt = result.getOrElse(0)
val resultInt: Try[Int] = result.recover {
case e: Exception => 0
}
Nicht einfach nur ein Wrapper um Exceptions
Macht aus einer partiellen Funktion eine totale (d.h. gut!)
"... the meaning of RT expressions does not depend on context and may be reasoned about locally, while the
meaning of non-RT expressions is context dependent and requires more global reasoning." (FPiS)
Typsystem = 
light-weight proof-system
I was eventually persuaded of the need to design programming notations so as to
1. maximise the number of errors which cannot be made,
2. or if made, can be reliably detected at compile time.
- Tony Hoare (ACM Turing Award Lecture)
Typsystem kann viel mehr beweisen als "x has method f"
Compile time = Build time, d.h. Unit Tests sind gut, aber nicht gut
genug
Transaktionen - CMT/Annotations/XML
@Transactional
public void updateSomething() { /* ... */ }
@Transactional
public List<SomeType> querySomething() { /* ... */ }
Offene Transaktion? @Transactional/Interceptor vergessen?
Richtige Transaktion? Implizite Beziehung zwischen Transaktion und
EntityManager
Kopplung mit Transaktionsattributen (Propagation, Isolation, etc.), retry?
Transaktionen - 
BMT/Templates
public void updateSomething(EntityManagerFactory emf) {
transactionTemplate.execute(emf,

new TransactionCallbackWithoutResult() {
protected void doInTransaction(EntityManager em) {
updateOperation1(em);
updateOperation2(em);
}
}
);
}
Wiederverwendung in anderer Transaktion?
Offene/richtige Transaktion? (teilweise ..)
EntityManager (o.ä.) müssen durchgereicht werden
Alternative: Transaktions Monad
Monad = Abstraktion zur sequentiellen Ausführung von einzelnen
Schritten in einem bestimmten Kontext (informell)
trait Tx[+A] { 

// Um die Transaktion "auszuführen" und den Inhalt zu bekommen
def run(em: EntityManager): A
}
object Tx {
// DSL für Transaktionen (konvertiert Funktion in Tx[A])
def transactional[A](body: EntityManager => A) = new Tx[A] {
override def run(em: EntityManager) = body(em)
}
}
Das ist noch kein Monad, nicht einmal informell.
Ich habe unser Reiseziel nur vorweggenommen.
Tx[A]-"Hello World"
def findAllProductIds() /* : Tx[List[Int]] */ = transactional {
em /* : EntityManager */ => // ...
}
def findEmployee(employeeId: String) = transactional {
em => /* ... */
}
So einfach wie mit Annotations - type inference :)
Erstmal vollkommen ohne Abhängigkeiten/Seiteneffekte*
Unmöglich falschen EntityManager zu verwenden**
* Für Skeptiker unter uns: Wie etwas sinnvolles programmieren ohne Seiteneffekte?
** Tony Hoar wäre stolz auf uns!
Tx<A> mit Java 8
Tx<A> = Keine Proxies, keine Konfiguration, keine sonstige "Magie",
eigentlich nur ein TransactionCallback<T> mit Lambdas?
@FunctionalInterface
public interface Tx<T> {
public T run(EntityManager em);
// Nicht wirklich hilfreich, Return Type sparen wir uns nicht
public static <T> Tx<T> transactional(
Function<EntityManager, T> body) {
return body::apply;
}
}
public Tx<List<Integer>> findProductIds(String searchCrit) {
return transactional(em -> /* ... */);
}
public Tx<Employee> findEmployee(String employeeId) {
return em -> /* ... */;
}
Tx[A] Ausführung
def run[A](emf: EntityManagerFactory, tx: Tx[A]): A = {
val em = emf.createEntityManager
try {
em.getTransaction.begin()
val result = tx.run(em)
em.getTransaction.commit()
result
} finally {
// Rollback (eventuell) und clean-up
}
}
Fazit bisher: Lediglich BMTs aufgesplittet?
Interpretieren des Monads verursacht Seiteneffekte
Andere Form der Abstrahierung - eine Beschreibung, viele
Möglichkeiten diese zu interpretieren
Compiler kennt jetzt
Transaktionsklammern
// Oh! Wir haben @Transactional vergessen, sozusagen
def findEmployeeName(employeeId: String) =
findEmployee(employeeId).name // Wäre zwar nicht so schlimm ..
// ERROR:
// value name is not a member of ...Tx[...Employee]
// Wir sind aber auch vergesslich!
def findManagerForEmployee(employeeId: String) =
// Lazy loading kann hier aber schon zu Problemen führen!
findEmployee(employeeId).department.manager
// findEmployee(employeeId).run(em).department.manager
// ERROR:
// value department is not a member of ...Tx[...Employee]
Transaktionskontext benötigt, um Inhalt zu bearbeiten
(bzw. eine EntityManager Referenz ..)
Tx[A] Verarbeitung 
(noch) unschön
def findManagerForEmployee(employeeId: String) = transactional {
em => findEmployee(employeeId).run(em).department.manager
}
public Tx<Employee> findManagerForEmployee(String employeeId) {
return em ->
findEmployee(employeeId).run(em).getDepartment().getManager();
}
Lesbarkeit eher suboptimal, etwas umständlich
EntityManager sollten wir nicht durchreichen müssen
Gesucht: Funktion mit Signatur Tx[A] => (A => B) => Tx[B]
Higher-order function: map
trait Tx[+A] { self => // Statt "Tx.this"
// ..
def map[B](f: A => B) = transactional {
em => f(self.body(em))
}
}
public interface Tx<T> {
// ..
default public <U> Tx<U> map(Function<T, U> f) {
return em -> f.apply(run(em));
}
}
Nicht nur bei Collections nützlich
Strukturerhaltende Verarbeitung von Elementen
Verwendung von map() (1)
def findManagerForEmployee(empId: String)/*: Tx[Employee] */ =
findEmployee(empId) map {
employee => employee.department.manager
}
public Tx<Employee> findManagerForEmployee(String empId) {
return findEmployee(empId).map(
employee -> employee.getDepartment().getManager());
}
Keine EntityManager, kein @Transactional - alles wird sozusagen
inferred
Erstmals wirklicher Unterschied zu der BMT Lösung
Verwendung von map() (2)
public Tx<Employee> findManagerForEmployee(String empId) {
return findEmployee(empId)
.map(Employee::getDepartment)
.map(Department::getManager);
}
def findDepartmentForEmployee(empId: String) = for {
employee <- findEmployee(empId)
} yield employee.department.manager
For comprehensions lediglich syntactic sugar
Verwendung von map (2)
def findProductIds(searchCrit: String) = transactional {
em => /* ... */
}
def findProductNames(productIds: List[Int]) = transactional {
em => /* ... */
}
def findProductNamesFor(searchCrit: String) = for {
productIds <- findProductIds(searchCriteria)
} yield findProductNames(productIds)
// od. einfacher:
def findProductNamesFor(searchCrit: String) =
findProductIds(searchCrit) map findProductNames
Kompiliert zwar, liefert aber Tx[Tx[List[String]
Wir suchen: T[x] => (A => Tx[B]) => Tx[B]
Higher-order function: flatMap
trait Tx[+A] { self =>
// ..
def flatMap[B](f: A => Tx[B]) = transactional {
em => f(self.run(em)).run(em)
}
}
public interface Tx<T> {
// ..
default public <U> Tx<U> flatMap(Function<T, Tx<U>> f) {
return em -> f.apply(run(em)).run(em);
}
}
Ergebnis von Tx[A] als Eingabe für Tx[B]
Tx[B] ersetzt sozusagen Tx[A], führt es aber auch aus
Garantiert die selbe Transaktion
Garantiert der selbe EntityManager
Vergleich mit Java 7
public Tx<List<String>>> findProductNamesFor(final String crit) {
return new Tx<List<String>>>() {
public List<String> run(EntityManager em) {
List<Integer> productIds =
findProductIds(crit).run(em); // self.run(em)
return findProductNames(productIds).run(em); // f(..).run(em)
}
};
}
Schwer zu lesen, aber nach wie vor nachvollziehbar(?)
Vergleich mit BMT: Suboperationen für sich verwendbar
Verwendung von flatMap
def findProductNamesFor(searchCrit: String) =
findProductIds(searchCrit) flatMap {
productIds => findProductNames(productIds)
}
def findProductNamesFor(searchCrit: String) =
findProductIds(searchCrit) flatMap findProductNames
def findProductNamesFor(searchCrit: String) = for {
productIds <- findProductIds(searchCrit)
productNames <- findProductNames(productIds)
} yield productNames
Zwei Datenbankqueries in der selben Transaktion
Wiederum weder EntityManager noch @Transactional (o.ä.) - Kontext
(hier Tx) wird wiederverwendet
Monad Definition für Scalaz
implicit val txMonad = new scalaz.Monad[Tx] {
def point[A](a: => A): Tx[A] = transactional {
// EntityManger wird nicht benötigt, kann ignoriert werden.
_ => a
}
def bind[A, B](fa: Tx[A])(f: A => Tx[B]): Tx[B] =
fa flatMap f
}
Essenz: Wir hatten die Implementierung praktisch schon
Gibt Zugriff zu nützlichen Monadic Functions
Monad Laws beweisen sparen wir uns momentan ..
Monadic Functions (1)
// Um die Signatur zu verdeutlichen:
def sequenceM[M[_] : Monad, T](ms: List[M[T]]): M[List[T]] =
ms.sequence
def zipM[M[_] : Monad, T, U](mt: M[T], mu: M[U]): M[(T, U)] = for {
t <- mt
u <- mu
} yield (t, u)
// In Bezug auf Transaktionen bedeutet das:
def joinTransactions[T](transactions: List[Tx[T]]): Tx[List[T]] =
sequenceM(transactions)
def joinTransactions[T, U](txT: Tx[T], txU: Tx[U]): Tx[(T, U)] =
zipM(txT, txU)
Kontext gibt dabei die jeweilige Semantik an
Monadic Functions (2)
def replicateM[M[_] : Monad, A](n: Int, ma: M[A]): M[List[A]] =
sequenceM(List.fill(n)(ma))
Referential transparency ist ja wieder gegeben
Unterschiedliche Datenbanken
trait Tx[+A, Tag] {
def run: A
def map[B](A => B): Tx[B, Tag] = // ...
def flatMap[B, ArgModule >: Module]
(f: A => Tx[B, ArgModule]): Tx[B, Module] = // ...
}
object Tx {
def transactional[A, Tag](body: EntityManager => A) = // ...
}
sealed trait Unit1
sealed trait Unit2
def txUnit1[A](body: EntityManager => A) =
transactional[A, Unit1](body)
def txUnit2[A](body: EntityManager => A) =
transactional[A, Unit2](body)
Tx bezieht sich nun auf bestimmte Datenbank
Compiler kennt nun auch
Datenbankgrenzen (1)
def findEmployee(employeeId: String) = txUnit1 { /* .. */ }
def saveEmployee(employee: Employee) = txUnit2 { /* .. */ }
// Wir wollen die zwei Operationen kombinieren
def findAndSaveEmployee(employeeId: String) =
findEmployee(employeeId) flatMap saveEmployee
// Error:(..) type mismatch;
// found : ..Tx[Unit,..Unit2]
// required: ..Tx[?],..Unit1]
// findEmployee(employeeId) flatMap saveEmployee
// ^
Modellierung ist einem selbst überlassen - z.B. common Tx für alle?
Compiler kennt nun auch
Datenbankgrenzen (2)
// Müssen wir über verschachtelte Transaktionen machen
def findAndSaveEmployee(employeeId: String)
/*: Tx[Tx[Unit, Unit2], Unit1] */ =
findEmployee(employeeId) map saveEmployee
Publikumsfrage: Funktioniert das?
@PersistenceContext(unitName = "db1Unit") private EntityManager em1;
@PersistenceContext(unitName = "db2Unit") private EntityManager em2;
@Transactional("unit1Manager")
@Transactional("unit2Manager")
// Employee redundant in zwei Datenbanken speichern
public void saveEmployee(String employeeName) {
em1.persist(new Employee(employeeName));
em2.persist(new Employee(employeeName));
}
def saveEmployee(employeeName: String) = (
txUnit1 { em1 => em1.persist(new Employee(employeeName)) },
txUnit2 { em2 => em2.persist(new Employee(employeeName)) }
)
Wie sicher ist man bei der Antwort dieser Frage?
Demo
Fazit
Ziel einer jeden API: LEGO(tm) Baukasten
Seiteneffekte kann man sehr gut einschränken
Deferred Execution / Interpretation, wenn man Seiteneffekte benötigt
Der (Scala!) Compiler ist dein Freund!
Q&A
Buchempfehlungen
Buchempfehlungen
Danke
Bernhard Huemer | IRIAN
bernhard.huemer@irian.at
@bhuemer

Weitere ähnliche Inhalte

Was ist angesagt?

Devs@Home - Einführung in Go
Devs@Home - Einführung in GoDevs@Home - Einführung in Go
Devs@Home - Einführung in Go
Frank Müller
 
BIT I WiSe 2014 | Basisinformationstechnologie I - 08: Programmiersprachen I
BIT I WiSe 2014 | Basisinformationstechnologie I - 08: Programmiersprachen IBIT I WiSe 2014 | Basisinformationstechnologie I - 08: Programmiersprachen I
BIT I WiSe 2014 | Basisinformationstechnologie I - 08: Programmiersprachen I
Institute for Digital Humanities, University of Cologne
 
Ctypes
CtypesCtypes
Einführung in die funktionale Programmierung
Einführung in die funktionale ProgrammierungEinführung in die funktionale Programmierung
Einführung in die funktionale Programmierung
Digicomp Academy AG
 
Dev Day 2019: Mirko Zeibig – "Hallo " <> "Elixir"
Dev Day 2019: Mirko Zeibig – "Hallo " <> "Elixir"Dev Day 2019: Mirko Zeibig – "Hallo " <> "Elixir"
Dev Day 2019: Mirko Zeibig – "Hallo " <> "Elixir"
DevDay Dresden
 
Metaprogrammierung mit Ruby
Metaprogrammierung mit RubyMetaprogrammierung mit Ruby
Metaprogrammierung mit RubyDario Rexin
 
An Introduction to Ruby
An Introduction to RubyAn Introduction to Ruby
An Introduction to Ruby
Jonathan Weiss
 
Skalierbare Anwendungen mit Google Go
Skalierbare Anwendungen mit Google GoSkalierbare Anwendungen mit Google Go
Skalierbare Anwendungen mit Google Go
Frank Müller
 
SEROM 2018 - 11/14/17/20 - C++ gestern heute und morgen
SEROM 2018 - 11/14/17/20 - C++ gestern heute und morgenSEROM 2018 - 11/14/17/20 - C++ gestern heute und morgen
SEROM 2018 - 11/14/17/20 - C++ gestern heute und morgen
Dr. Herwig Henseler
 
Parallele Softwareentwicklung mit .NET 4.0
Parallele Softwareentwicklung mit .NET 4.0Parallele Softwareentwicklung mit .NET 4.0
Parallele Softwareentwicklung mit .NET 4.0
Qiong Wu
 
Ein Gopher im Netz
Ein Gopher im NetzEin Gopher im Netz
Ein Gopher im Netz
Frank Müller
 
Slides
SlidesSlides
Java Script Ist Anders
Java Script Ist AndersJava Script Ist Anders
Java Script Ist Andersjlink
 
Warum ECMAScript 6 die Welt ein Stückchen besser macht
Warum ECMAScript 6 die Welt ein Stückchen besser machtWarum ECMAScript 6 die Welt ein Stückchen besser macht
Warum ECMAScript 6 die Welt ein Stückchen besser macht
Sebastian Springer
 
MapStruct - der neue Stern am Bean-Mapping Himmel?!
MapStruct - der neue Stern am Bean-Mapping Himmel?!MapStruct - der neue Stern am Bean-Mapping Himmel?!
MapStruct - der neue Stern am Bean-Mapping Himmel?!
inovex GmbH
 
Übungsaufgaben
ÜbungsaufgabenÜbungsaufgaben
Übungsaufgabenmaikinger
 
Vorlesung 7: Laufzeit, Maps
Vorlesung 7: Laufzeit, MapsVorlesung 7: Laufzeit, Maps
Vorlesung 7: Laufzeit, MapsMichael Zilske
 

Was ist angesagt? (19)

Devs@Home - Einführung in Go
Devs@Home - Einführung in GoDevs@Home - Einführung in Go
Devs@Home - Einführung in Go
 
BIT I WiSe 2014 | Basisinformationstechnologie I - 08: Programmiersprachen I
BIT I WiSe 2014 | Basisinformationstechnologie I - 08: Programmiersprachen IBIT I WiSe 2014 | Basisinformationstechnologie I - 08: Programmiersprachen I
BIT I WiSe 2014 | Basisinformationstechnologie I - 08: Programmiersprachen I
 
Ctypes
CtypesCtypes
Ctypes
 
Einführung in die funktionale Programmierung
Einführung in die funktionale ProgrammierungEinführung in die funktionale Programmierung
Einführung in die funktionale Programmierung
 
Php Schulung
Php SchulungPhp Schulung
Php Schulung
 
Dev Day 2019: Mirko Zeibig – "Hallo " <> "Elixir"
Dev Day 2019: Mirko Zeibig – "Hallo " <> "Elixir"Dev Day 2019: Mirko Zeibig – "Hallo " <> "Elixir"
Dev Day 2019: Mirko Zeibig – "Hallo " <> "Elixir"
 
Metaprogrammierung mit Ruby
Metaprogrammierung mit RubyMetaprogrammierung mit Ruby
Metaprogrammierung mit Ruby
 
An Introduction to Ruby
An Introduction to RubyAn Introduction to Ruby
An Introduction to Ruby
 
Skalierbare Anwendungen mit Google Go
Skalierbare Anwendungen mit Google GoSkalierbare Anwendungen mit Google Go
Skalierbare Anwendungen mit Google Go
 
SEROM 2018 - 11/14/17/20 - C++ gestern heute und morgen
SEROM 2018 - 11/14/17/20 - C++ gestern heute und morgenSEROM 2018 - 11/14/17/20 - C++ gestern heute und morgen
SEROM 2018 - 11/14/17/20 - C++ gestern heute und morgen
 
Parallele Softwareentwicklung mit .NET 4.0
Parallele Softwareentwicklung mit .NET 4.0Parallele Softwareentwicklung mit .NET 4.0
Parallele Softwareentwicklung mit .NET 4.0
 
Ein Gopher im Netz
Ein Gopher im NetzEin Gopher im Netz
Ein Gopher im Netz
 
Tutorium 4
Tutorium 4Tutorium 4
Tutorium 4
 
Slides
SlidesSlides
Slides
 
Java Script Ist Anders
Java Script Ist AndersJava Script Ist Anders
Java Script Ist Anders
 
Warum ECMAScript 6 die Welt ein Stückchen besser macht
Warum ECMAScript 6 die Welt ein Stückchen besser machtWarum ECMAScript 6 die Welt ein Stückchen besser macht
Warum ECMAScript 6 die Welt ein Stückchen besser macht
 
MapStruct - der neue Stern am Bean-Mapping Himmel?!
MapStruct - der neue Stern am Bean-Mapping Himmel?!MapStruct - der neue Stern am Bean-Mapping Himmel?!
MapStruct - der neue Stern am Bean-Mapping Himmel?!
 
Übungsaufgaben
ÜbungsaufgabenÜbungsaufgaben
Übungsaufgaben
 
Vorlesung 7: Laufzeit, Maps
Vorlesung 7: Laufzeit, MapsVorlesung 7: Laufzeit, Maps
Vorlesung 7: Laufzeit, Maps
 

Ähnlich wie Scalaz introduction for Java programmers

Go - Googles Sprache für skalierbare Systeme
Go - Googles Sprache für skalierbare SystemeGo - Googles Sprache für skalierbare Systeme
Go - Googles Sprache für skalierbare Systeme
Frank Müller
 
Java Streams und Lambdas
Java Streams und LambdasJava Streams und Lambdas
Java Streams und Lambdas
Nane Kratzke
 
.NET Summit 2016 München: EcmaScript 2015+ with TypeScript
.NET Summit 2016 München: EcmaScript 2015+ with TypeScript.NET Summit 2016 München: EcmaScript 2015+ with TypeScript
.NET Summit 2016 München: EcmaScript 2015+ with TypeScript
Manfred Steyer
 
Java und Go im Vergleich
Java und Go im VergleichJava und Go im Vergleich
Java und Go im Vergleich
QAware GmbH
 
WiSe 2014 | Softwaretechnologie I _ Funktionen, Zeiger, Strukturen, Bibliotheken
WiSe 2014 | Softwaretechnologie I _ Funktionen, Zeiger, Strukturen, BibliothekenWiSe 2014 | Softwaretechnologie I _ Funktionen, Zeiger, Strukturen, Bibliotheken
WiSe 2014 | Softwaretechnologie I _ Funktionen, Zeiger, Strukturen, Bibliotheken
Institute for Digital Humanities, University of Cologne
 
Funktionale Reaktive Programmierung mit Sodium
Funktionale Reaktive Programmierung mit SodiumFunktionale Reaktive Programmierung mit Sodium
Funktionale Reaktive Programmierung mit Sodium
Torsten Fink
 
Scala und Lift
Scala und LiftScala und Lift
Scala und Lift
Felix Müller
 
nagiosplugin - eine Python-Biblioth­ek für Monitoring-Plug­ins
nagiosplugin - eine Python-Biblioth­ek für Monitoring-Plug­ins nagiosplugin - eine Python-Biblioth­ek für Monitoring-Plug­ins
nagiosplugin - eine Python-Biblioth­ek für Monitoring-Plug­ins
Christian Kauhaus
 
2006 - NRW Conf: Asynchronous asp.net
2006 - NRW Conf: Asynchronous asp.net2006 - NRW Conf: Asynchronous asp.net
2006 - NRW Conf: Asynchronous asp.net
Daniel Fisher
 
C++11 und c++14
C++11 und c++14C++11 und c++14
C++11 und c++14
Holger Jakobs
 
TypeScript
TypeScriptTypeScript
TypeScript
Jens Siebert
 
Apache CouchDB at PHPUG Karlsruhe, Germany (Jan 27th 2009)
Apache CouchDB at PHPUG Karlsruhe, Germany (Jan 27th 2009)Apache CouchDB at PHPUG Karlsruhe, Germany (Jan 27th 2009)
Apache CouchDB at PHPUG Karlsruhe, Germany (Jan 27th 2009)
Nils Adermann
 
jQuery & CouchDB - Die zukünftige Webentwicklung?
jQuery & CouchDB - Die zukünftige Webentwicklung?jQuery & CouchDB - Die zukünftige Webentwicklung?
jQuery & CouchDB - Die zukünftige Webentwicklung?
openForce Information Technology GesmbH
 
Leichtgewichtige Architekturen mit Spring, JPA, Maven und Groovy
Leichtgewichtige Architekturen mit Spring, JPA, Maven und GroovyLeichtgewichtige Architekturen mit Spring, JPA, Maven und Groovy
Leichtgewichtige Architekturen mit Spring, JPA, Maven und Groovy
Thorsten Kamann
 
Java Design Pattern
Java Design PatternJava Design Pattern
Java Design Pattern
manuelberger
 
Praesentation TYPO3Camp Berlin Speed mit Extbase
Praesentation TYPO3Camp Berlin Speed mit ExtbasePraesentation TYPO3Camp Berlin Speed mit Extbase
Praesentation TYPO3Camp Berlin Speed mit Extbase
Stefan Frömken
 
Differenzial Analyse in der Praxis (Florian Walther)
Differenzial Analyse in der Praxis (Florian Walther)Differenzial Analyse in der Praxis (Florian Walther)
Differenzial Analyse in der Praxis (Florian Walther)GEEKcon
 
Werkzeugkasten
WerkzeugkastenWerkzeugkasten
Werkzeugkasten
Hendrik Lösch
 

Ähnlich wie Scalaz introduction for Java programmers (20)

Go - Googles Sprache für skalierbare Systeme
Go - Googles Sprache für skalierbare SystemeGo - Googles Sprache für skalierbare Systeme
Go - Googles Sprache für skalierbare Systeme
 
Java Streams und Lambdas
Java Streams und LambdasJava Streams und Lambdas
Java Streams und Lambdas
 
.NET Summit 2016 München: EcmaScript 2015+ with TypeScript
.NET Summit 2016 München: EcmaScript 2015+ with TypeScript.NET Summit 2016 München: EcmaScript 2015+ with TypeScript
.NET Summit 2016 München: EcmaScript 2015+ with TypeScript
 
Java und Go im Vergleich
Java und Go im VergleichJava und Go im Vergleich
Java und Go im Vergleich
 
WiSe 2014 | Softwaretechnologie I _ Funktionen, Zeiger, Strukturen, Bibliotheken
WiSe 2014 | Softwaretechnologie I _ Funktionen, Zeiger, Strukturen, BibliothekenWiSe 2014 | Softwaretechnologie I _ Funktionen, Zeiger, Strukturen, Bibliotheken
WiSe 2014 | Softwaretechnologie I _ Funktionen, Zeiger, Strukturen, Bibliotheken
 
Funktionale Reaktive Programmierung mit Sodium
Funktionale Reaktive Programmierung mit SodiumFunktionale Reaktive Programmierung mit Sodium
Funktionale Reaktive Programmierung mit Sodium
 
Scala und Lift
Scala und LiftScala und Lift
Scala und Lift
 
nagiosplugin - eine Python-Biblioth­ek für Monitoring-Plug­ins
nagiosplugin - eine Python-Biblioth­ek für Monitoring-Plug­ins nagiosplugin - eine Python-Biblioth­ek für Monitoring-Plug­ins
nagiosplugin - eine Python-Biblioth­ek für Monitoring-Plug­ins
 
Web Entwicklung mit PHP - Teil 1
Web Entwicklung mit PHP - Teil 1Web Entwicklung mit PHP - Teil 1
Web Entwicklung mit PHP - Teil 1
 
2006 - NRW Conf: Asynchronous asp.net
2006 - NRW Conf: Asynchronous asp.net2006 - NRW Conf: Asynchronous asp.net
2006 - NRW Conf: Asynchronous asp.net
 
C++11 und c++14
C++11 und c++14C++11 und c++14
C++11 und c++14
 
TypeScript
TypeScriptTypeScript
TypeScript
 
Apache CouchDB at PHPUG Karlsruhe, Germany (Jan 27th 2009)
Apache CouchDB at PHPUG Karlsruhe, Germany (Jan 27th 2009)Apache CouchDB at PHPUG Karlsruhe, Germany (Jan 27th 2009)
Apache CouchDB at PHPUG Karlsruhe, Germany (Jan 27th 2009)
 
WiSe 2013 | Programmierpraktikum C++ - 01_Basics
WiSe 2013 | Programmierpraktikum C++ - 01_BasicsWiSe 2013 | Programmierpraktikum C++ - 01_Basics
WiSe 2013 | Programmierpraktikum C++ - 01_Basics
 
jQuery & CouchDB - Die zukünftige Webentwicklung?
jQuery & CouchDB - Die zukünftige Webentwicklung?jQuery & CouchDB - Die zukünftige Webentwicklung?
jQuery & CouchDB - Die zukünftige Webentwicklung?
 
Leichtgewichtige Architekturen mit Spring, JPA, Maven und Groovy
Leichtgewichtige Architekturen mit Spring, JPA, Maven und GroovyLeichtgewichtige Architekturen mit Spring, JPA, Maven und Groovy
Leichtgewichtige Architekturen mit Spring, JPA, Maven und Groovy
 
Java Design Pattern
Java Design PatternJava Design Pattern
Java Design Pattern
 
Praesentation TYPO3Camp Berlin Speed mit Extbase
Praesentation TYPO3Camp Berlin Speed mit ExtbasePraesentation TYPO3Camp Berlin Speed mit Extbase
Praesentation TYPO3Camp Berlin Speed mit Extbase
 
Differenzial Analyse in der Praxis (Florian Walther)
Differenzial Analyse in der Praxis (Florian Walther)Differenzial Analyse in der Praxis (Florian Walther)
Differenzial Analyse in der Praxis (Florian Walther)
 
Werkzeugkasten
WerkzeugkastenWerkzeugkasten
Werkzeugkasten
 

Scalaz introduction for Java programmers

  • 1. Scalaz Einführung für Java Programmierer Bernhard Huemer | IRIAN bernhard.huemer@irian.at @bhuemer
  • 2.
  • 3. Agenda Keine Einführung in Scala Keine Einführung in Scalaz ("Haskell für Scala") Motivation Transaktionen mit Monads Type classes bzw. Ad-hoc polymorphismus ev. Arbeiten mit der Datenbank ohne* Seiteneffekte ev. Dependency Injection Funktionale APIs gestalten * Fast ohne
  • 4. Referential transparency "An expression is said to be referentially transparent if it can be replaced with its value without changing the behavior of a program." (d.h. keine Seiteneffekte) def square(i: Int) = i * i // Bad! Referenziert auch etwas anderes als Parameter def now() = System.currentTimeMillis() Keine Reassignment von Variablen Keine Setter, keine Mutations Keine Exceptions Keine Konsolenein-/ausgabe Keine Netzwerkkommunikation ...
  • 5. Exceptions als Beispiel def f(): Int = { val y = g() try { y + y } catch { case e: Exception => 1337 } } def f(): Int = { try { g() + g() } catch { case e: Exception => 1337 } } def g(): Int = { throw new Exception("No!"); } Checked vs. unchecked exceptions (etwas) irrelevant Compiler versichert, dass Seiteneffekte behandelt werden Besser: Sie treten gar nicht erst auf Viel zu triviales Beispiel, ich weiß!
  • 6. Exceptions als Beispiel (2) class Cache (cacheLoader: CacheLoader) { def get(key: Key): Value = { // ... } } trait CacheLoader { def load(key: Key): Value // throws Exception! } class Cache (cacheLoader: CacheLoader) { def preload() { // ... } def get(key: Key): Value = { // ... } } Refactoring mit oder ohne Seiteneffekte? Exceptions bei preload: Falsch/teilw. initialisierter Cache, fehlendes Exception-Handling*, etc.? Exceptions bei get: Mühsam * z.B: Applikation startet nicht mehr, weil unbenützter Cache Key nicht geladen werden kann, andere Initialisierungen werden nicht eingeleitet, etc. - Autor von Cache hat keinen Einfluss darauf!
  • 7. Call Stack Abhängigkeit Es treten immer dann Probleme auf, wenn wir den Call Stack irgendwie ändern. Multi-threaded Umgebungen Web Services RMI oder sonstiges Remoting generell jegl. Netzwerkkommunikation ...
  • 8. "Do or do not. There is no scala.util.Try[A]." - Yoda
  • 9. scala.util.Try[A] import scala.util.{Try, Success, Failure} def divide: Try[Int] = for { dividend <- Try(Console.readLine("Dividend:n").toInt) divisor <- Try(Console.readLine("Divisor:n").toInt) } yield (dividend / divisor) val result: Try[Int] = /* .. */ val resultInt = if (result.isSuccess) { result.get } else { 0 } val resultInt = result.getOrElse(0) val resultInt: Try[Int] = result.recover { case e: Exception => 0 } Nicht einfach nur ein Wrapper um Exceptions Macht aus einer partiellen Funktion eine totale (d.h. gut!)
  • 10. "... the meaning of RT expressions does not depend on context and may be reasoned about locally, while the meaning of non-RT expressions is context dependent and requires more global reasoning." (FPiS)
  • 11. Typsystem =  light-weight proof-system I was eventually persuaded of the need to design programming notations so as to 1. maximise the number of errors which cannot be made, 2. or if made, can be reliably detected at compile time. - Tony Hoare (ACM Turing Award Lecture) Typsystem kann viel mehr beweisen als "x has method f" Compile time = Build time, d.h. Unit Tests sind gut, aber nicht gut genug
  • 12.
  • 13. Transaktionen - CMT/Annotations/XML @Transactional public void updateSomething() { /* ... */ } @Transactional public List<SomeType> querySomething() { /* ... */ } Offene Transaktion? @Transactional/Interceptor vergessen? Richtige Transaktion? Implizite Beziehung zwischen Transaktion und EntityManager Kopplung mit Transaktionsattributen (Propagation, Isolation, etc.), retry?
  • 14. Transaktionen -  BMT/Templates public void updateSomething(EntityManagerFactory emf) { transactionTemplate.execute(emf,
 new TransactionCallbackWithoutResult() { protected void doInTransaction(EntityManager em) { updateOperation1(em); updateOperation2(em); } } ); } Wiederverwendung in anderer Transaktion? Offene/richtige Transaktion? (teilweise ..) EntityManager (o.ä.) müssen durchgereicht werden
  • 15. Alternative: Transaktions Monad Monad = Abstraktion zur sequentiellen Ausführung von einzelnen Schritten in einem bestimmten Kontext (informell) trait Tx[+A] { 
 // Um die Transaktion "auszuführen" und den Inhalt zu bekommen def run(em: EntityManager): A } object Tx { // DSL für Transaktionen (konvertiert Funktion in Tx[A]) def transactional[A](body: EntityManager => A) = new Tx[A] { override def run(em: EntityManager) = body(em) } } Das ist noch kein Monad, nicht einmal informell. Ich habe unser Reiseziel nur vorweggenommen.
  • 16. Tx[A]-"Hello World" def findAllProductIds() /* : Tx[List[Int]] */ = transactional { em /* : EntityManager */ => // ... } def findEmployee(employeeId: String) = transactional { em => /* ... */ } So einfach wie mit Annotations - type inference :) Erstmal vollkommen ohne Abhängigkeiten/Seiteneffekte* Unmöglich falschen EntityManager zu verwenden** * Für Skeptiker unter uns: Wie etwas sinnvolles programmieren ohne Seiteneffekte? ** Tony Hoar wäre stolz auf uns!
  • 17. Tx<A> mit Java 8 Tx<A> = Keine Proxies, keine Konfiguration, keine sonstige "Magie", eigentlich nur ein TransactionCallback<T> mit Lambdas? @FunctionalInterface public interface Tx<T> { public T run(EntityManager em); // Nicht wirklich hilfreich, Return Type sparen wir uns nicht public static <T> Tx<T> transactional( Function<EntityManager, T> body) { return body::apply; } } public Tx<List<Integer>> findProductIds(String searchCrit) { return transactional(em -> /* ... */); } public Tx<Employee> findEmployee(String employeeId) { return em -> /* ... */; }
  • 18. Tx[A] Ausführung def run[A](emf: EntityManagerFactory, tx: Tx[A]): A = { val em = emf.createEntityManager try { em.getTransaction.begin() val result = tx.run(em) em.getTransaction.commit() result } finally { // Rollback (eventuell) und clean-up } } Fazit bisher: Lediglich BMTs aufgesplittet? Interpretieren des Monads verursacht Seiteneffekte Andere Form der Abstrahierung - eine Beschreibung, viele Möglichkeiten diese zu interpretieren
  • 19. Compiler kennt jetzt Transaktionsklammern // Oh! Wir haben @Transactional vergessen, sozusagen def findEmployeeName(employeeId: String) = findEmployee(employeeId).name // Wäre zwar nicht so schlimm .. // ERROR: // value name is not a member of ...Tx[...Employee] // Wir sind aber auch vergesslich! def findManagerForEmployee(employeeId: String) = // Lazy loading kann hier aber schon zu Problemen führen! findEmployee(employeeId).department.manager // findEmployee(employeeId).run(em).department.manager // ERROR: // value department is not a member of ...Tx[...Employee] Transaktionskontext benötigt, um Inhalt zu bearbeiten (bzw. eine EntityManager Referenz ..)
  • 20. Tx[A] Verarbeitung  (noch) unschön def findManagerForEmployee(employeeId: String) = transactional { em => findEmployee(employeeId).run(em).department.manager } public Tx<Employee> findManagerForEmployee(String employeeId) { return em -> findEmployee(employeeId).run(em).getDepartment().getManager(); } Lesbarkeit eher suboptimal, etwas umständlich EntityManager sollten wir nicht durchreichen müssen Gesucht: Funktion mit Signatur Tx[A] => (A => B) => Tx[B]
  • 21. Higher-order function: map trait Tx[+A] { self => // Statt "Tx.this" // .. def map[B](f: A => B) = transactional { em => f(self.body(em)) } } public interface Tx<T> { // .. default public <U> Tx<U> map(Function<T, U> f) { return em -> f.apply(run(em)); } } Nicht nur bei Collections nützlich Strukturerhaltende Verarbeitung von Elementen
  • 22. Verwendung von map() (1) def findManagerForEmployee(empId: String)/*: Tx[Employee] */ = findEmployee(empId) map { employee => employee.department.manager } public Tx<Employee> findManagerForEmployee(String empId) { return findEmployee(empId).map( employee -> employee.getDepartment().getManager()); } Keine EntityManager, kein @Transactional - alles wird sozusagen inferred Erstmals wirklicher Unterschied zu der BMT Lösung
  • 23. Verwendung von map() (2) public Tx<Employee> findManagerForEmployee(String empId) { return findEmployee(empId) .map(Employee::getDepartment) .map(Department::getManager); } def findDepartmentForEmployee(empId: String) = for { employee <- findEmployee(empId) } yield employee.department.manager For comprehensions lediglich syntactic sugar
  • 24. Verwendung von map (2) def findProductIds(searchCrit: String) = transactional { em => /* ... */ } def findProductNames(productIds: List[Int]) = transactional { em => /* ... */ } def findProductNamesFor(searchCrit: String) = for { productIds <- findProductIds(searchCriteria) } yield findProductNames(productIds) // od. einfacher: def findProductNamesFor(searchCrit: String) = findProductIds(searchCrit) map findProductNames Kompiliert zwar, liefert aber Tx[Tx[List[String] Wir suchen: T[x] => (A => Tx[B]) => Tx[B]
  • 25. Higher-order function: flatMap trait Tx[+A] { self => // .. def flatMap[B](f: A => Tx[B]) = transactional { em => f(self.run(em)).run(em) } } public interface Tx<T> { // .. default public <U> Tx<U> flatMap(Function<T, Tx<U>> f) { return em -> f.apply(run(em)).run(em); } } Ergebnis von Tx[A] als Eingabe für Tx[B] Tx[B] ersetzt sozusagen Tx[A], führt es aber auch aus Garantiert die selbe Transaktion Garantiert der selbe EntityManager
  • 26. Vergleich mit Java 7 public Tx<List<String>>> findProductNamesFor(final String crit) { return new Tx<List<String>>>() { public List<String> run(EntityManager em) { List<Integer> productIds = findProductIds(crit).run(em); // self.run(em) return findProductNames(productIds).run(em); // f(..).run(em) } }; } Schwer zu lesen, aber nach wie vor nachvollziehbar(?) Vergleich mit BMT: Suboperationen für sich verwendbar
  • 27. Verwendung von flatMap def findProductNamesFor(searchCrit: String) = findProductIds(searchCrit) flatMap { productIds => findProductNames(productIds) } def findProductNamesFor(searchCrit: String) = findProductIds(searchCrit) flatMap findProductNames def findProductNamesFor(searchCrit: String) = for { productIds <- findProductIds(searchCrit) productNames <- findProductNames(productIds) } yield productNames Zwei Datenbankqueries in der selben Transaktion Wiederum weder EntityManager noch @Transactional (o.ä.) - Kontext (hier Tx) wird wiederverwendet
  • 28. Monad Definition für Scalaz implicit val txMonad = new scalaz.Monad[Tx] { def point[A](a: => A): Tx[A] = transactional { // EntityManger wird nicht benötigt, kann ignoriert werden. _ => a } def bind[A, B](fa: Tx[A])(f: A => Tx[B]): Tx[B] = fa flatMap f } Essenz: Wir hatten die Implementierung praktisch schon Gibt Zugriff zu nützlichen Monadic Functions Monad Laws beweisen sparen wir uns momentan ..
  • 29. Monadic Functions (1) // Um die Signatur zu verdeutlichen: def sequenceM[M[_] : Monad, T](ms: List[M[T]]): M[List[T]] = ms.sequence def zipM[M[_] : Monad, T, U](mt: M[T], mu: M[U]): M[(T, U)] = for { t <- mt u <- mu } yield (t, u) // In Bezug auf Transaktionen bedeutet das: def joinTransactions[T](transactions: List[Tx[T]]): Tx[List[T]] = sequenceM(transactions) def joinTransactions[T, U](txT: Tx[T], txU: Tx[U]): Tx[(T, U)] = zipM(txT, txU) Kontext gibt dabei die jeweilige Semantik an
  • 30. Monadic Functions (2) def replicateM[M[_] : Monad, A](n: Int, ma: M[A]): M[List[A]] = sequenceM(List.fill(n)(ma)) Referential transparency ist ja wieder gegeben
  • 31. Unterschiedliche Datenbanken trait Tx[+A, Tag] { def run: A def map[B](A => B): Tx[B, Tag] = // ... def flatMap[B, ArgModule >: Module] (f: A => Tx[B, ArgModule]): Tx[B, Module] = // ... } object Tx { def transactional[A, Tag](body: EntityManager => A) = // ... } sealed trait Unit1 sealed trait Unit2 def txUnit1[A](body: EntityManager => A) = transactional[A, Unit1](body) def txUnit2[A](body: EntityManager => A) = transactional[A, Unit2](body) Tx bezieht sich nun auf bestimmte Datenbank
  • 32. Compiler kennt nun auch Datenbankgrenzen (1) def findEmployee(employeeId: String) = txUnit1 { /* .. */ } def saveEmployee(employee: Employee) = txUnit2 { /* .. */ } // Wir wollen die zwei Operationen kombinieren def findAndSaveEmployee(employeeId: String) = findEmployee(employeeId) flatMap saveEmployee // Error:(..) type mismatch; // found : ..Tx[Unit,..Unit2] // required: ..Tx[?],..Unit1] // findEmployee(employeeId) flatMap saveEmployee // ^ Modellierung ist einem selbst überlassen - z.B. common Tx für alle?
  • 33. Compiler kennt nun auch Datenbankgrenzen (2) // Müssen wir über verschachtelte Transaktionen machen def findAndSaveEmployee(employeeId: String) /*: Tx[Tx[Unit, Unit2], Unit1] */ = findEmployee(employeeId) map saveEmployee
  • 34. Publikumsfrage: Funktioniert das? @PersistenceContext(unitName = "db1Unit") private EntityManager em1; @PersistenceContext(unitName = "db2Unit") private EntityManager em2; @Transactional("unit1Manager") @Transactional("unit2Manager") // Employee redundant in zwei Datenbanken speichern public void saveEmployee(String employeeName) { em1.persist(new Employee(employeeName)); em2.persist(new Employee(employeeName)); } def saveEmployee(employeeName: String) = ( txUnit1 { em1 => em1.persist(new Employee(employeeName)) }, txUnit2 { em2 => em2.persist(new Employee(employeeName)) } ) Wie sicher ist man bei der Antwort dieser Frage?
  • 35. Demo
  • 36. Fazit Ziel einer jeden API: LEGO(tm) Baukasten Seiteneffekte kann man sehr gut einschränken Deferred Execution / Interpretation, wenn man Seiteneffekte benötigt Der (Scala!) Compiler ist dein Freund!
  • 37. Q&A
  • 40. Danke Bernhard Huemer | IRIAN bernhard.huemer@irian.at @bhuemer