Programmieren in Haskell Stefan Janssen Programmieren in Haskell Haskell Syntax Stefan Janssen Universität Bielefeld AG Praktische Informatik October 12, 2014 Haskell Syntax Scopes Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Wir beginnen mit einem Überblick über die Syntax von Haskell Namen versus Schlüsselwörter Programmieren in Haskell Stefan Janssen Namen bezeichnen Werte (aller Art) und sind frei wählbar Schlüsselwörter strukturieren das Programm; sie dürfen nicht als Namen verwendet werden Namen und Schlüsselwörter dürfen nicht durch Leerzeichen oder Zeilenende unterbrochen werden Nicht zu verwechseln: Schlüsselwörter und vordefinierte Namen Haskell Syntax Scopes Schlüsselwörter 1 2 3 case class data default deriving do else if import in infix infixl infixr instance let module newtype of then type where _ Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Schlüsselwörter 1 2 3 case class data default deriving do else if import in infix infixl infixr instance let module newtype of then type where _ Programmieren in Haskell Stefan Janssen Haskell Syntax Vergleich Scopes Sprache #Keywords Haskell Scheme C (C89) Java Ada C++ 21 23 32 50 72 74 Groß- und Kleinschreibung in Namen Programmieren in Haskell Modulname erster Buchstabe großgeschrieben Modulname == Dateiname Stefan Janssen Haskell Syntax Scopes Datentypnamen: Großgeschrieben Datentyp-Konstrukturen: Großgeschrieben (z.B. Datentyp Bool → Konstruktoren: True und False) Variablen, Funktionsnamen: Kleingeschrieben Operatorzeichen → Infix-Schreibweise Sonstige Funktionsnamen: Präfix-Schreibweise Prioritäten und Assoziation sind definierbar Keine (!) Anweisungen Programmieren in Haskell Für Kenner von Java, C, Pascal, ...: keine Statements in Haskell keine Wertzuweisung, kein FOR, kein WHILE ... Stefan Janssen Haskell Syntax Scopes Keine (!) Anweisungen Programmieren in Haskell Für Kenner von Java, C, Pascal, ...: keine Statements in Haskell keine Wertzuweisung, kein FOR, kein WHILE ... → da funktionale Programmiersprache Es wird beschrieben, WAS berechnet werden soll, aber nicht WIE. So wie in (1 + f (2)) ∗ 32 ∗ (4 + g(5)) Hier ist nicht festgelegt, ob zuerst f (2), 32 , oder g(5) berechnet wird Stefan Janssen Haskell Syntax Scopes Funktionsdefinitionen 1 2 3 4 Einfache Funktionsgleichungen: > p x = 5* x ^2 -7* x +9 > aba x y = x ++ y ++ x > f n = if n ==0 then 1 else n * f (n -1) > g x y z = f (x * p y ) - z Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes 5 6 7 8 9 10 > dnacomp b = if b == ’A ’ then ’T ’ else > if b == ’C ’ then ’G ’ else > if b == ’G ’ then ’C ’ else > if b == ’T ’ then ’A ’ else > error " illegal symbol in DNA " Allgemein: f p1 ... pk = Ausdruck, der p1 bis pk enthält Mustergleichungen (pattern matching) Programmieren in Haskell 1 2 Mustergleichungen treffen Fallunterscheidungen auf der linken Seite (und erlauben mehr als eine Gleichung) > ff 0 = 1 > ff n = n * f (n -1) -- ueberlappende Muster Stefan Janssen Haskell Syntax Scopes 3 4 5 6 7 8 > > > > > dnaComp dnaComp dnaComp dnaComp dnaComp ’A ’ ’C ’ ’G ’ ’T ’ _ = = = = = ’T ’ ’G ’ ’C ’ ’A ’ error " illegal symbol in DNA " _ bezeichnet ein Argument, das nicht gebraucht wird Fehlerbehandlung Es gibt mehrere Möglichkeiten, Fehler zu behandeln in Haskell Programmen (mindestens 8) einfachster Mechanismus ist die Funktion error Programmieren in Haskell Stefan Janssen error msg darf überall in einem Ausdruck stehen Haskell Syntax msg ist ein String Scopes error msg führt zum Programmabbruch mit Ausgabe von msg Die Funktion hat einen ganz besonderen Typ: error :: String -> a Fehlerbehandlung Es gibt mehrere Möglichkeiten, Fehler zu behandeln in Haskell Programmen (mindestens 8) einfachster Mechanismus ist die Funktion error Haskell Syntax msg ist ein String Scopes Die Funktion hat einen ganz besonderen Typ: error :: String -> a Example 2 3 Stefan Janssen error msg darf überall in einem Ausdruck stehen error msg führt zum Programmabbruch mit Ausgabe von msg 1 Programmieren in Haskell > divide a b = > if b == 0 then error " divide by zero " > else div a b Präfix, Infix, Postfix-Notation Die Mathematik hat viele Schreibweisen für Ausdrücke erfunden, um sie gut lesbar zu machen Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Notation Präfix Infix Postfix Grafix mathematisch −1, f (x , y , z) x < y , n2 , 2x + 5 0 n!, q f √ 3 1− y Rb in Haskell -1, f x y z x<y, nˆ2, 2*x+5 --root 3 (1 - (root 2 y)) integral a b f a f (x ) dx Präfix-Notation ohne Klammern ist der Regelfall in Haskell. Was ist der Unterschied zwischen f a g b und f a (g b) ? Operatoren Zweistellige Funktionen mit Infix-Schreibweise nennt man Operatoren. Boolsche Operatoren sind vordefiniert: || oder && und ‘not‘ Vergleichsoperatoren: < <= == /= . . . Arithmetik: + - * / ‘div‘ ‘mod‘ Zeichenreichen: : ++ Operatoren sind reguläre Funktionen in Haskell; nur die Infix-Notation und die gegebenen Prioritäten sind das Besondere. Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Operatoren Zweistellige Funktionen mit Infix-Schreibweise nennt man Operatoren. Boolsche Operatoren sind vordefiniert: || oder && und ‘not‘ Vergleichsoperatoren: < <= == /= . . . Arithmetik: + - * / ‘div‘ ‘mod‘ Zeichenreichen: : ++ Operatoren sind reguläre Funktionen in Haskell; nur die Infix-Notation und die gegebenen Prioritäten sind das Besondere. Example Main> let (+) x y = x*y in 2+3 6 Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Prefix/Infix-Konversion Funktionen als Operatoren Operatoren als Funktionen Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Prefix/Infix-Konversion Funktionen als Operatoren Operatoren als Funktionen ‘fn‘ konvertiert nach Infix (op) konvertiert nach Prefix Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Prefix/Infix-Konversion Funktionen als Operatoren Operatoren als Funktionen ‘fn‘ konvertiert nach Infix (op) konvertiert nach Prefix Example Main> 23 Main> 23 Test> 23 Test> 23 21 + 2 (+) 21 2 div 69 3 69 ‘div‘ 3 Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Prefix/Infix: Prioritäten Programmieren in Haskell Stefan Janssen Flexible Anwendung von Funktionen Haskell Syntax Deklaration von Prioritäten möglich meist geeignet vordefiniert – siehe Haskell report Scopes Funktionsanwendung in Präfix hat höchste Priorität 1 2 3 f x + g y * 2 ist ( f x ) + (( g y ) *2) fuer einstelliges f und g Achtung bei Definitionen : 4 5 f ( x : xs ) ys = x : f xs ys braucht Klammern !! Bedingter Ausdruck 1 if expr1 then expr2 else expr3 Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Bedingter Ausdruck 1 if expr1 then expr2 else expr3 Programmieren in Haskell Stefan Janssen Example Haskell Syntax 1 f a b = if a <= b then a +1 else b -2 Scopes Bedingter Ausdruck 1 if expr1 then expr2 else expr3 Programmieren in Haskell Stefan Janssen Example Haskell Syntax 1 f a b = if a <= b then a +1 else b -2 Kein If-Statement! if expr1 then expr2 ? Scopes Bedingter Ausdruck 1 if expr1 then expr2 else expr3 Programmieren in Haskell Stefan Janssen Example Haskell Syntax 1 f a b = if a <= b then a +1 else b -2 Kein If-Statement! if expr1 then expr2 ? Fehler! → hier wäre der Wert des Ausdrucks im else-Fall nicht definiert Scopes Bedingter Ausdruck 1 if expr1 then expr2 else expr3 Programmieren in Haskell Stefan Janssen Example Haskell Syntax 1 f a b = if a <= b then a +1 else b -2 Kein If-Statement! if expr1 then expr2 ? Fehler! → hier wäre der Wert des Ausdrucks im else-Fall nicht definiert Vergleichbar mit dem Bedingungsoperator in C/Java: cond_expr ? expr2 : expr3 Scopes Sichtbarkeitsbereiche Programmieren in Haskell Alle (modernen) Programiersprachen haben Sichtbarkeitsbereiche (scopes). Jeder Name ist in dem Bereich sichtbar in dem er definiert wurde und in allen darin eingebetteteten Bereichen ... ... es sei denn, er wird verschattet Scopes in einem Programm sind also “verschachtelt”. Explizite Regeln gelten für Sichtbarkeit über Modulgrenzen hinweg. Stefan Janssen Haskell Syntax Scopes Layout und Abseitsregel “Whitespace” sind Zeichen ohne Bedeutung: Leerzeichen, NeueZeile, Kommentar Haskell-Programme sind whitespace-sensitive es gilt die Offside-Rule (Abseitsregel) Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes → die Einrückung ist entscheidend Layout und Abseitsregel “Whitespace” sind Zeichen ohne Bedeutung: Leerzeichen, NeueZeile, Kommentar Haskell-Programme sind whitespace-sensitive es gilt die Offside-Rule (Abseitsregel) Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes → die Einrückung ist entscheidend in der Regel Verzicht auf Trennzeichen wie ; {}, aber explizite Verwendung von ; {} ist möglich Layout und Abseitsregel “Whitespace” sind Zeichen ohne Bedeutung: Leerzeichen, NeueZeile, Kommentar Haskell-Programme sind whitespace-sensitive es gilt die Offside-Rule (Abseitsregel) Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes → die Einrückung ist entscheidend in der Regel Verzicht auf Trennzeichen wie ; {}, aber explizite Verwendung von ; {} ist möglich Example (richtig vs falsch) 1 2 3 4 > dreh (x , y ) = > schieb (x , y )= > heb (x , y ) = > ( -y , x ) ( x +1 , y ) (x , y +1) > dreh (x , y )= ( -y , x ) > schieb (x , y )=( x +1 , y ) > heb (x , y )= (x , > y +1) Layout definiert Sichtbarkeitsbereiche Programmieren in Haskell Neue Bereiche entstehen nach of, where, let: Haskell Syntax Example (richtig) 1 2 3 4 5 6 > > > > > > show xs = where show ’ show ’ show ’ Stefan Janssen Scopes " [ " ++ show ’ xs ++ " ] " Nil = "" ( Cons x Nil ) = show x ( Cons x xs ) = show x ++ " ," ++ show ’ xs Layout Programmieren in Haskell Definitionen gleicher Stufe müssen in der gleichen Einrücktiefe stehen Example (falsch) 1 2 3 4 5 6 > > > > > > show xs = " [ " ++ show ’ xs ++ " ] " where show ’ Nil = "" show ’ ( Cons x Nil ) = show x show ’ ( Cons x xs ) = show x ++ " ," ++ show ’ xs Stefan Janssen Haskell Syntax Scopes Explizite Scopes Programmieren in Haskell Stefan Janssen Gleichwertig sind Haskell Syntax Example 1 2 3 4 5 > f x = g x + > where { g > f x = g x + > where g x h x Scopes h x h = = x = x * x ; h x = x + x } x x * x x + x Allgemeine Abseitsregel Scopes sind Sichtbarkeitsbereiche von (Gruppen von) Definitionen alle Definitionen eines Scope stehen in der gleichen Einrücktiefe tieferes Einrücken bedeutet Fortsetzung der aktuellen Definitionszeile nach let, where, of, do bestimmt der nächste Buchstabe die neue Einrücktiefe Ausrücken nach links beendet den aktuellen Scope Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Allgemeine Abseitsregel Scopes sind Sichtbarkeitsbereiche von (Gruppen von) Definitionen alle Definitionen eines Scope stehen in der gleichen Einrücktiefe tieferes Einrücken bedeutet Fortsetzung der aktuellen Definitionszeile nach let, where, of, do bestimmt der nächste Buchstabe die neue Einrücktiefe Ausrücken nach links beendet den aktuellen Scope Vorteile ?? Nachteile ?? Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Noch mehr Syntax Programmieren in Haskell Stefan Janssen Haskell Syntax Die folgenden Syntax-Elemente werden wir erst später kennelernen, wenn die entsprechenden Sprachkonstrukte behandelt werden. Sie sind hier nur der Vollständigkeit halber aufgeführt. Scopes Case-Ausdruck (pp.) vielfache Fallunterscheidung statt verschachtelte If-Expressions Programmieren in Haskell Stefan Janssen überall wo ein Ausdruck stehen kann Haskell Syntax Pattern matching Scopes Case-Ausdruck (pp.) vielfache Fallunterscheidung statt verschachtelte If-Expressions 1 2 3 4 5 Programmieren in Haskell Stefan Janssen überall wo ein Ausdruck stehen kann Haskell Syntax Pattern matching Scopes case expr of pat1 | guard1 -> expr1 pat2 | guard2 -> expr2 pat3 | guard3 -> expr3 ... Case-Ausdruck (pp.) vielfache Fallunterscheidung statt verschachtelte If-Expressions 1 2 3 4 5 Programmieren in Haskell Stefan Janssen überall wo ein Ausdruck stehen kann Haskell Syntax Pattern matching Scopes case expr of pat1 | guard1 -> expr1 pat2 | guard2 -> expr2 pat3 | guard3 -> expr3 ... Pattern kann leer sein statt pat | True -> gilt auch die Konvention pat -> otherwise als default Case-Ausdruck Programmieren in Haskell Stefan Janssen Example Haskell Syntax Scopes 1 2 3 4 5 > laststr xs = case reverse xs of > [] -> " leer " > ( l : ls ) | l == 1 -> " eins " > ( l : ls ) | l == 2 -> " zwei " > otherwise -> " ganz viele " Let Expression 1 2 3 4 let decl1 decl2 ... in expr Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Let Expression 1 2 3 4 let decl1 decl2 ... in expr Kann überall dort stehen wo ein Ausdruck stehen kann lokaler Scope (Sichtbarkeitsbereich) Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Let Expression let decl1 decl2 ... in expr 1 2 3 4 Kann überall dort stehen wo ein Ausdruck stehen kann lokaler Scope (Sichtbarkeitsbereich) Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Example 1 2 3 > func a b > > = let square x = x * x plus a b = a + b in plus ( square a ) ( square b ) Let Expression let decl1 decl2 ... in expr 1 2 3 4 Kann überall dort stehen wo ein Ausdruck stehen kann lokaler Scope (Sichtbarkeitsbereich) Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Example 1 2 3 1 2 3 > func a b > > = let square x = x * x plus a b = a + b in plus ( square a ) ( square b ) Versus: > square x = x * x > plus a b = a + b > func a b = plus ( square a ) ( square b ) Hier sind plus und square auch außerhalb von func bekannt. Where-Notation lokale Definitionen Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Where-Notation lokale Definitionen bei Funktionsdefinitionen case-Ausdrücken Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Where-Notation lokale Definitionen bei Funktionsdefinitionen case-Ausdrücken Module-Definition . . . Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Where-Notation lokale Definitionen bei Funktionsdefinitionen case-Ausdrücken Module-Definition . . . Example 1 2 3 > f x = square x + square x > where > square x = x * x Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Where-Notation lokale Definitionen bei Funktionsdefinitionen case-Ausdrücken Module-Definition . . . Example 1 2 3 > f x = square x + square x > where > square x = x * x vs. let Ausdruck notationelle Betonung syntaktischer Zucker where kann nicht überall stehen Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Where-Notation Programmieren in Haskell Stefan Janssen 1 2 3 4 5 Schachtelungen auch möglich: > foo x = ... f ... > where > f x = ... g ... > where > g x = .... Haskell Syntax Scopes Modul-Aufbau Modulname Imports Definitionen Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Modul-Aufbau Programmieren in Haskell Modulname Imports Definitionen 1 2 > module Meinmodul > where Stefan Janssen Haskell Syntax Scopes 3 4 > import Data . List 5 6 > splits n = [ splitAt k [1.. n ] | k < -[1.. n -1]] Modul-Aufbau Programmieren in Haskell Modulname Imports Definitionen 1 2 Stefan Janssen Haskell Syntax > module Meinmodul > where Scopes 3 4 > import Data . List 5 6 > splits n = [ splitAt k [1.. n ] | k < -[1.. n -1]] Main> splits [ ( [1 ( [1,2 ( [1,2,3 ( [1,2,3,4 5 ],[ ],[ ],[ ],[ 2,3,4,5] 3,4,5] 4,5] 5] ) ) ) ) , , , ] Wächter (Guards) in Funktionsdefinitionen (pp.) 1 2 3 4 > fn pat1 pat2 ... > | guard1 = expr1 > | guard2 = expr2 > ... Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Wächter (Guards) in Funktionsdefinitionen (pp.) 1 2 3 4 > fn pat1 pat2 ... > | guard1 = expr1 > | guard2 = expr2 > ... syntaktische Zucker für Funktionsdefinitionen ersetzt geschachtelte if-Anweisungen guards (Wächter) sind Ausdrücke (vom Typ Bool) Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Wächter (Guards) in Funktionsdefinitionen (pp.) 1 2 3 4 > fn pat1 pat2 ... > | guard1 = expr1 > | guard2 = expr2 > ... syntaktische Zucker für Funktionsdefinitionen ersetzt geschachtelte if-Anweisungen guards (Wächter) sind Ausdrücke (vom Typ Bool) Example 1 2 3 4 howManyEqual n m p | ( n == m ) && ( m == p ) = 3 | ( n == m ) || ( m == p ) || ( n == p ) = 2 | otherwise = 0 Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Pattern matching Pattern Matching auf natürlichen Zahlen Werten Datentyp-Konstruktoren Joker _ auf der linken Seite von Funktionsdefinitionen in case-Ausdrücken Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Pattern matching Pattern Matching auf natürlichen Zahlen Werten Datentyp-Konstruktoren Joker _ auf der linken Seite von Funktionsdefinitionen in case-Ausdrücken Example 1 2 3 > take ’ :: Int -> [ a ] -> [ a ] > take ’ ( n +1) ( a : as ) = a : take ’ n as > take ’ _ _ = [] Programmieren in Haskell Stefan Janssen Haskell Syntax Scopes Pattern matching In der Anwendung wird die erste Gleichung benutzt, deren Muster auf die Argumente passen. Example 1 2 3 4 > > > > f f f f ( a : b : c : d : ds ) ( a : as ) ( a :[]) [a] 7 8 > g 0 > g 1 > g _ Stefan Janssen Haskell Syntax = = = = ... ... ... ... 5 6 Programmieren in Haskell = ... = ... = ... Wo es geht, wählt man die Muster einander ausschließend. Scopes