Ein Blick über den Tellerrand Funktionale Ideen in der praktischen Programmierung Jasper van de Ven ([email protected]) Praktische Informatik 3 Universität Bremen Wintersemester 09/10 Motivation You can never understand one language until you understand at least two. -Roland Searle Motivation funktionale Programmierung in der praktischen Anwendung einordnen Programmiersprachen, die funktionale Konzepte unterstützen Beispiele wo funktionale Programme mit großem Erfolg eingesetzt werden Ablauf Kurze Historie der funktionalen Programmierung Vorstellung der betrachteten Sprachen Vorstellung der verwendeten Konzepte Geschichtsstunde... The future, according to some scientists, will be exactly like the past, only far more expensive. -John Sladek Geschichtsstunde... entwickelt aus der akademischen Forschung Lambda-Klakül Ende 50iger Jahre → LISP 1987 → Haskell Betrachtete Sprachen Haskell Erlang Ruby Scala Haskell main = putStrLn "Hello, World!" vorgestellt 1987 Zusammenführung der Forschung Sprache zum Austausch —————————– fac 0 = 1 fac n = n * fac (n-1) Erlang -mod(hello). -export([start/0]). start() -> io:format("Hello, World!"). —————————– Ericsson Computer Science Lab 1987 Telekommunikation -module(test). -export([fac/1]). fac(0) -> 1; fac(N) -> N * fac(N-1). Ruby puts "Hello, world!" —————————– Yukihiro ’Matz’ Matsumoto 1995 (Java auch...) Rails 2005 def fac(n) result = 1 n.times do |i| result *= i end result end Scala 2003 (2001 Entwicklungsbegin) École Polytechnique Fédérale de Lausanne (EPFL) Anfang 2004 → JVM Mitte 2004 → .Net object HelloWorld extends Application { println("Hello, world!") } —————————– def fac(n: Int): Int = { if(n <= 1) 1 else n * fac(n - 1) } Scala def fac(x: Int): Int = x match { case 0 => 1 case n => fac(n - 1) * n } Betrachtete Eigenschaften Paradigmen Typsysteme Spezieller funktionale Techniken in Ruby Nebenläufigkeit in Erlang Scala - Ein Blick in die Zukunft Betrachtete Eigenschaften Paradigmen Typsysteme Spezieller funktionale Techniken in Ruby Nebenläufigkeit in Erlang Scala - Ein Blick in die Zukunft Paradigmen der Programmierung imperativ deklarativ prozedural logisch objektorientiert funktional multiparadigmatisch Programmiersprachen unterstützen einen Stil und beschränken nicht auf ihn Paradigmen der Programmierung imperativ deklarativ prozedural logisch objektorientiert funktional multiparadigmatisch Programmiersprachen unterstützen einen Stil und beschränken nicht auf ihn Paradigmen von Haskell rein funktional lazy evaluation modular Paradigmen von Erlang rein funktional modular hoch Verfügbar Parallelität Paradigmen von Erlang hoch Verfügbar Parallelität Was heist das jetzt genau? Ericcson: 99% Verfügbarkeit im Jahr A = 1+5. B = 4+6. C = A+B. Paradigmen von Erlang hoch Verfügbar Parallelität Was heist das jetzt genau? Ericcson: 99% Verfügbarkeit im Jahr A = 1+5. B = 4+6. C = A+B. Paradigmen von Erlang hoch Verfügbar Parallelität Was heist das jetzt genau? Ericcson: 99% Verfügbarkeit im Jahr A = 1+5. B = 4+6. C = A+B. Paradigmen von Ruby multiparadigmatisch objektorientiert (prozedural, funktional) modular Metaprogrammierung Paradigmen von Scala multiparadigmatisch objektorientiert, funktional, imperativ direkte Anbindung an Java Typsysteme [A type system is a] tractable syntactic method for proving the absence of certain program behaviors by classifying phrases according to the kinds of values they compute. -Benjamin C. Pierce Haskell und Erlang Haskell statisch stark Typinferenz Erlang dynamisch stark Haskell und Erlang Haskell statisch stark Typinferenz Erlang dynamisch stark Ruby Duck-Typing class Ente class Kuh def beschreibung def beschreibung "Eine graue Ente" "Eine dicke Kuh" end end def sprechen def sprechen "Quak!" "Muuuh!" end end end end def lass_sprechen tier puts tier.beschreibung+ " macht "+tier.sprechen end lass_sprechen Ente.new lass_sprechen Kuh.new Ruby Duck-Typing class Ente class Kuh def beschreibung def beschreibung "Eine graue Ente" "Eine dicke Kuh" end end def sprechen def sprechen "Quak!" "Muuuh!" end end end end def lass_sprechen tier puts tier.beschreibung+ " macht "+tier.sprechen end lass_sprechen Ente.new lass_sprechen Kuh.new Scala statisch stark Typinferenz object InferenceTest1 extends Application { val x = 1 + 2 * 3 val y = x.toString() def succ(x: Int) = x + 1 } Scala statisch stark Typinferenz object InferenceTest1 extends Application { val x = 1 + 2 * 3 val y = x.toString() def succ(x: Int) = x + 1 } Scala object InferenceTest2 { def fac(n: Int) = if (n == 0) 1 else n * fac(n - 1) } ———————– case class MyPair[A, B](x: A, y: B); object InferenceTest3 extends Application { def id[T](x: T) = x val p = new MyPair(1, "scala") val q = id(1) } val x: MyPair[Int, String] = new MyPair[Int, String](1, "scala") val y: Int = id[Int](1) Scala object InferenceTest2 { def fac(n: Int) = if (n == 0) 1 else n * fac(n - 1) } ———————– case class MyPair[A, B](x: A, y: B); object InferenceTest3 extends Application { def id[T](x: T) = x val p = new MyPair(1, "scala") val q = id(1) } val x: MyPair[Int, String] = new MyPair[Int, String](1, "scala") val y: Int = id[Int](1) Scala object InferenceTest4 { var obj = null obj = new Object() } Funktionale Konzepte in Ruby Funktionen höherer Ordnung Anonyme Funktionen Proc Objekte Code Blocks Funktionale Konzepte in Ruby Funktionen höherer Ordnung Anonyme Funktionen Proc Objekte Code Blocks Proc Objekte Proc objects are blocks of code that have been bound to a set of local variables. Once bound, the code may be called in different contexts and still access those variables. -Ruby Dokumentation Proc Objekte def gen_times(factor) return Proc.new {|n| n*factor } end times3 = gen_times(3) times5 = gen_times(5) times3.call(12) times5.call(5) times3.call(times5.call(4)) #=> 36 #=> 25 #=> 60 Proc Objekte def foo (a, b) a.call(b) end putser = Proc.new {|x| puts x} foo(putser, 34) ———————————— putser = lambda {|x| puts x} Proc Objekte def foo (a, b) a.call(b) end putser = Proc.new {|x| puts x} foo(putser, 34) ———————————— putser = lambda {|x| puts x} Code Blocks # a naked block can’t live in Ruby # this is a compilation error ! {puts "hello"} ———————– # now it’s alive, having been converted # to a Proc ! pr = lambda {puts "hello"} pr.call Code Blocks # a naked block can’t live in Ruby # this is a compilation error ! {puts "hello"} ———————– # now it’s alive, having been converted # to a Proc ! pr = lambda {puts "hello"} pr.call Code Blocks 10.times do |i| print "#{i} " end numbers = [1, 2, 5, 6, 9, 21] numbers.each do |x| puts "#{x} is " + (x >= 3 ? "many" : "few") end squares = numbers.map {|x| x * x} Code Blocks def do_twice(&block) yield yield end do_twice {puts "Hola"} Nebenläufigkeit in Erlang Prozessstruktur Message-passing Nebenläufigkeit in Erlang -module(ping_pong). -export([ping/0, pong/0]). ping() -> Pong = spawn(ping_pong, pong, []), Pong ! {self(), ping}, receive pong -> pong end. pong() -> receive {Ping, ping} -> Ping ! pong end. Scala - Funktionaler Zucker und Nebenläufigkeit Actor-Designpattern Funktionen höherer Ordnung Anonyme Funktionen Actor-Pattern import scala.actors.Actor import scala.actors.Actor._ val fussyActor = actor { loop { receive { case s: String => println("I got a String: " + s) case i: Int => println("I got an Int: " + i.toString) case _ => println("I have no idea what I just got.") } } } Actor-Patterm fussyActor ! "hi there" fussyActor ! 23 fussyActor ! 3.33 Funktionen höherer Ordnung def apply(f: Int => String, v: Int) = f(v) ———————– class Decorator(left: String, right: String) { def layout[A](x: A) = left + x.toString() + right } object FunTest extends Application { def apply(f: Int => String, v: Int) = f(v) val decorator = new Decorator("[", "]") println(apply(decorator.layout, 7)) } Funktionen höherer Ordnung def apply(f: Int => String, v: Int) = f(v) ———————– class Decorator(left: String, right: String) { def layout[A](x: A) = left + x.toString() + right } object FunTest extends Application { def apply(f: Int => String, v: Int) = f(v) val decorator = new Decorator("[", "]") println(apply(decorator.layout, 7)) } Anonyme Funktionen new Function1[Int, Int] { def apply(x: Int): Int = x + 1 } (x: Int, y: Int) => "(" + x + ", " + y + ")" () => { System.getProperty("user.dir") } Anonyme Funktionen new Function1[Int, Int] { def apply(x: Int): Int = x + 1 } (x: Int, y: Int) => "(" + x + ", " + y + ")" () => { System.getProperty("user.dir") } Closures object TargetTest1 extends Application { def whileLoop(cond: => Boolean) (body: => Unit): Unit = if (cond) { body whileLoop(cond)(body) } var i = 10 whileLoop (i > 0) { println(i) i -= 1 } } Einsatzgebiete und bekannte Projekte Haskell → akademische Welt Erlang → Telekommunikationssysteme (Ericsson) Ruby → Ruby on Rails (Web 2.0) Scala → Twitter (Web 2.0) aber auch z.B. durch französischem Energie Unternehmen Électricité de France (EDF) Fazit Use the right tools for the job... -an old saying Zum Abschluss noch zwei Kleinigkeiten... Fazit Use the right tools for the job... -an old saying Zum Abschluss noch zwei Kleinigkeiten... Remember... 1 1 http://xkcd.com/519/ In Gedenken an Klaus’ Katzen-Presentationen... 2 Ich hoffe, dass hab ich... 2 http://catsonthecounter.blogspot.com/2009/09/im-not-lazyim-sickyeahthat.html