Vortragsfolien ()

Werbung
Monaden in anderen
Programmiersprachen
Themen Informatik-Seminar SS 2013:
Programmiersprachen und Sprachsysteme
Bei: Prof. Dr. Schmidt, FH Wedel
inf9500 Sebastian Philipp
Überblick
● Motivation
● Monaden in Haskell
● Monaden in C#
● Monaden in Ruby
● Monaden in Javascript
Motivation
Wieso dieses Seminarthema?
Asynchrone Algorithmen
●
●
●
●
Concurrency ohne Threads
Funktionen rufen Callbacks auf
Beispiel Node.js
Callbacks sollen
○ Nie warten
○ Nie blockieren
○ Schnell zurückkehren
● Problem: verschachtelte Callback-Handler
Asynchrone NetzwerkRequests mit Qt
● Qt ist ein Framework für C++
● Netzwerk API ist asynchron
○ Callback wird aufgerufen, sobald Request beendet
wurde.
○ Danach neuen Request starten
Problem
Algorithmus
○
○
○
○
○
Sehr viele unterschiedliche Requests
Rekursion
HTTP-Redirekts
Sessions
Fehlerbehandlung
Traditionelle Implementierung scheidet aus.
Lösung: Monaden?
● Asynchron:
○ Ein Thread
○ Progress, Abort
● Synchron
○ Lokale Variablen
○ Kontrollstrukturen (Funktionen, Rekursionen)
Motivation: MaybeFunktionen
Gegeben:
Library-Funktionen, die Error-Codes oder Bool als returnValue liefern
if (!funcA(..))
return false;
if (!funcB(...))
return false;
Trennen von Inhalt und Fehlerbehandlung?
Monaden in Haskell
Monaden sind parametrisierbare Typen
Monaden implementieren dieses Interface:
unit :: a -> m a
bind :: m a -> (a -> m b) -> m b
Monadengesetze
(unit x) `bind` f
== f x
x
`bind` unit == x
(x `bind` f) `bind` g ==
x `bind` (\ y -> f y `bind` g)
● Neutralität von unit
● Assoziativität von bind
● Infixnotation in anderen Sprachen?
Maybe-Monade
Die einfachste Monade
data Maybe a = Nothing | Just a
unit :: a -> Maybe a
unit x = Just x
bind :: Maybe a -> (a -> Maybe b) -> Maybe b
bind Nothing f = Nothing
bind (Just x) f = f x
Do-Notation
● func1 und func2 liefern beide
möglicherweise ein Ergebnis
f :: a -> Maybe b
f x = do
y <- func1 x
z <- func2 y
unit z
Do-Notation
● func1 und func2 liefern beide
möglicherweise ein Ergebnis
f :: a -> Maybe b
f x =
func1 x `bind` \ y ->
func2 y `bind` \ z ->
unit z
Monaden in Haskell
● Do-Notation: Einfache Transformation
● In Haskell sind Lambda-Ausdrücke extrem
einfach.
● Monaden in Sprachen ohne Lambda?
Monaden in C#
LINQ
LINQ
LINQ ist eine Syntax in Anlehnung an SQL
Ziel: Zugriff auf Daten
int[] func(int[] numbers) {
return
from
n in numbers
where n > 5
select n + 1;
}
LINQ
Bindings für Datenbanken, Container, XML und
Arrays
XElement books = XElement.Parse(
@"<books>
<book>
<title>Pro LINQ: Language Integr. Query in C# 2010</title>
<author>Joe Rattz</author>
</book>
<book>
<title>Pro .NET 4.0 Parallel Programming in C#</title>
<author>Adam Freeman</author>
</book>
</books>");
LINQ
var titles =
from
book in books.Elements("book")
where (string)book.Element("author")
== "Joe Rattz"
select book.Element("title");
● Schlüsselwörter: from, where, select,
group, orderby, ...
● Jede LINQ-Abfrage endet mit select. Ist
das ein Problem?
LINQ
int[] linq(int[] numbers) {
return
from
n in numbers
where n > 5
select n + 1;
}
wird transformiert in
int[] linq(int[] numbers) {
return numbers
.Where(n => n > 5)
.Select(n => n + 1)
}
LINQ und Monaden
Mehrere "from .. in" - Anweisungen
werden mit SelectMany zusammengebunden.
public static M<B>
SelectMany<A, B>(
this M<A> source,
Func<A, M<B>> selector
)
Das ist die bind Funktion aus Haskell.
LINQ und Monaden
public Maybe<int> DoSomeDivision(int denominator)
{
return from a in 12.Div(denominator)
from b in a.Div(2)
select b;
}
● Wie können wir die Maybe-Monade mit
SelectMany implementieren?
Maybe Monade in C#
Unser Datentyp:
public interface Maybe<T> { }
public class Nothing<T> : Maybe<T> { }
public class Just<T>
: Maybe<T> {
public T Value;
public Just(T value) {
Value = value;
}
}
Maybe-Monade in C#
Unsere unit Funktion:
public static Maybe<T> ToMaybe<T>(this T value)
{
return new Just<T>(value);
}
Nicht unbedingt nötig in C#, verbessert aber die
Lesbarkeit.
Maybe-Monade in C#
Unsere bind-Funktion:
public static Maybe<B>
SelectMany<A, B>(
this Maybe<A> source,
Func<A, Maybe<B>> selector
)
{
var justA = source as Just<A>;
return justA == null ?
new Nothing<B>() :
selector(justA.Value);
}
Async in C# und F#
● Async ist in C# keine Monade
○ Nicht mithilfe der Continuation Monade
implementiert
○ Ähnlich wie eine Koroutine
○ await ⇔ yield
● F# : Async Workflows
○ Ist eine Monade.
F# Async-Workflow
let downloadAndExtractLinks url =
async {
let webClient = new System.Net.WebClient()
let html = webClient
.DownloadString(url : string)
let! links = extractLinksAsync html
return url, links.Count
}
Computational Expressions sind Monaden.
F# Async
Die Do-Notation in F#.
Construct
De-sugared Form
let pat = expr in cexpr
let pat = expr in cexpr
let! pat = expr in cexpr b.Bind(expr, (fun pat ->
cexpr))
Monaden in Ruby
Ruby unterstützt mehrere
Programmierparadigmen:
● Objektorientierung
○ "Infix" bind
● Funktionale Programmierung
○ Bekannte Funktionen: map, concat (flatten)
Monadengesetze in Ruby
m.class.unit[x].bind[f] == f[x]
x.bind[m.class.unit]
== x
x.bind[f].bind[g]
==
x.bind[lambda { |y| f[y].bind[y] }]
● Gibt es Unterschiede zu den anderen
Sprachen?
● Wieso eckige Klammern für die
Funktionsaufrufe?
Lazy Identitätsmonade
class Id
def initialize(lam)
@v = lam
end
def force # :: Id a -> a
@v[]
end
def self.unit # a -> Id a
lambda { |x| Id.new(lambda { x }) }
end
def bind # :: Id a -> (a -> Id b) -> Id b
x = self
lambda { |f| f[x.force] }
end
end
Ruby Funktionsaufruf
Was
Funktion
Lambda
func
Aufruf ohne
Parameter
Funktionsobjekt
func(x)
x als Parameter
Syntaxfehler
func (x)
x als Parameter
Syntaxfehler
func[x]
Syntaxfehler*
x als Parameter
func [x]
[x] als Parameter
x als Parameter
* hier wird func ohne Parameter ausgeführt, dann das
Ergebnis als Lambda interpretiert.
Lazy Identitätsmonade
Anwendung der Identitätsmonade:
x = Id.unit[20]
.bind[lambda { |x| puts x; Id.unit[x * 2] }
x.force
Die 20 wird nicht ausgegeben, bis wir x.force
aufrufen.
Set-Monade
require "set"
class Set
def self.unit # :: a -> Set a
lambda { |x|
Set.new [x]
}
end
def bind # :: Set a -> ( a -> Set b ) -> Set b
s = self
lambda { |f|
Set.new(self.map(&f)
.map{ |x| x.map{|y|y} }
.flatten(1))
}
end
Set-Monade
● Wieso gibt es keine Set-Monade in Haskell?
● Wofür kann sie verwendet werden?
● Was ist das Ergebnis?
s = Set.new [1,2,3]
s.bind[lambda { |x| Set.new [x,x+1,x+10] }]
Set Monade: Mixin
module Monad
def map
lambda { |f|
bind[lambda { |x| self.class.unit[f[x]] }]
}
end
end
● Mit diesem Mixin können wir map für alle möglichen
Typen verwenden.
● Andere Funktionen sinnvoll?
class Set
include Monad
end
Monaden in Javascript
Keine Monade in Javascript:
● JQuery
○ JQuery ist Method-Chainig
Monaden in Javascript
Monaden sind Js-Objekte mit den Funktionen
function unit(value)
function bind(monad, function(value));
Monadengesetze in Js
unit(value).bind(f)
=== f(value)
monad.bind(unit)
=== monad
monad.bind(f).bind(g) ===
monad.bind(function (value) {
return f(value).bind(g)
})
Gibt es Unterschiede zu Ruby?
Maybe-Monade in Js
var Maybe = function(value) {
this.value = value;
};
Maybe.unit = function(value) {
return new Maybe(value)
};
Maybe-Monade in Js
Vererbung mit Prototypen
Maybe.prototype.bind = function(fn) {
if (this.value != null)
return fn(this.value);
return this;
};
Maybe-Monade in Js
Anwendung der Maybe-Monade in Js
m1 = new Maybe(1);
var maybe11 = m1.bind(function(v) {
return new Maybe(v + 10)
})
Maybe-Monade in Js
Wir können normale Funktionen in monadische
Funktionen heben:
Maybe.lift = function(fn) {
return function(m) {
return m.bind(function(x) {
return Maybe.unit(fn(x));
})
};
};
Maybe-Monade in Js
Anwendung von maybeAddOne
var addOne = function(val) {
return val + 1;
};
var maybeAddOne = Maybe.lift(addOne);
maybeAddOne(m1).ret() // == 2
maybeAddOne(new Maybe(undefined)) //undefined
LiftM2 in Js
Maybe.liftM2 = function(fn) {
return function(M1, M2) {
return M1.bind(function(val1) {
return M2.bind(function(val2) {
return Maybe.unit(fn(val1, val2));
});
});
};
};
Zusammenfassung
● Haskell
○ Keine Set-Monade?
○ Schöne Do-Notation
● C#
○ Kann Monaden
● Ruby
○ Lambdas sind nicht First-Class-Objects
○ Keine Do-Notations
Lösung: Callbacks
bool putFile(File f) { network.put(f); }
bool onRequestFinished() {
if (network.hasError()) ...
swich (currentState) ...
}
Eigenschaften
○
○
○
○
○
Eine onRequestFinished() Methode
Eine C++-Klasse
Globale States
Implizite Statemachine
Unwartbar
Lösung: Synchron
bool putFile(File f) {
network.put(f);
network.wait();
return network.hasError();
}
Probleme:
○
○
○
○
○
Extra Netzwerk-Thread
Ein Request pro Thread
Caching Probleme
Progress
Request abbrechen?
Herunterladen