Haskell
Eine funktionale Programmiersprache
filter :: (a -> Bool) -> [a] -> [a]
filter _ [] = []
filter pred (x:xs)
| pred x = x : filter pred xs
| otherwise = filter pred xs
Quick-Check
Paradigmen
- funktional
- keine Nebenwirkungen, keine Invarianten
- z.B. Scala, SQL, JavaScript, (C#), XSLT, uvm.
- nicht-strikt: lazy, Auswertung nach Bedarf
- deklarativ: Beschreibung des Was, nicht des Wie
Typ-System
- statisch
- stark
- inferiert
Weitere Infos
- Compiler: Glaskow Haskell Compiler (GHC) u.a.
- entwickelt Ende der 80er, u.a. von Simon Peyton Jones
- Funktionen höherer Ordnung (Closures, Lambda-Kalkül)
- algebraische Datentypen
- Currying
- Pattern matching
Die Syntax
Case-Sensitive
- beginnend mit Großbuchstaben
- Typen, Konstruktoren, Typ-Klassen
String, Int, LT, Num
- beginnend mit Kleinbuchstaben
- Bezeichner, Typ-Variablen, Funktionen, Parameter
fun x = x * x
sort :: Ord a => [a] -> [a]
Struktur per Einrückung
- Indentation-based, vgl. Python u. CoffeeScript
- vorzugsweise (mindestens 2) Leerzeichen pro Ebene
Kommentare
- Einzeilige Kommentare: "
--
" - Mehrzeilige Kommentare: "
{- -}
" - auch geschachtelt möglich
- "
{- Kommentar-Block {- innerer Block -} wieder äußerer Block -}
"
- "
Typ-System
- Compiler ist strikter als bekannte andere
- zwingt Programmierer zum genauen Nachdenken
- wenns compiliert, läufts (meist) auch fehlerfrei
Basis-Datentypen
let c = 'a' -- Typ Char
let s = "abc" -- Typ String, bzw. [Char]
let i = 0 -- Typ Int
let i = 0 :: Integer -- Typ Integer
let d = 0.0 -- Typ Double
let d = 0 :: Double -- Typ Double
let b = True -- Typ Bool, Gegenteil: False
let l = [1,2,3,4] -- Typ [Int]
let l = [1..10] -- Kurzform einer Liste
let infinite = [1..] -- unendliche Liste (lazy)
let t = (1,"abc") -- Typ (Int, String)
Listen
- Listen sind keine Arrays!
- Element an den Anfang:
- Der Listen-Konstruktor
let newList = newElement : list
- Element an das Ende:
- Die append-Funktion
let new List = list ++ [newElement]
- Prepending ist besser als Appending
Listen
- Gestalt von Listen ähnelt der von LinkedLists
- List-Comprehensions:
- let xs = [x*x | x <- [1..10]]
- Pattern-Matching mit Listen:
-
filter :: (a -> Bool) -> [a] -> [a] filter _ [] = [] filter pred (x:xs) | pred x = x : filter pred xs | otherwise = filter pred xs
-
Eigene Datentypen
data Day =
Monday
| Tuesdays
| Wednesdays
| Thursday
| Friday
| Saturday
| Sunday
data Person =
Person String Int
Typ-Synonyme
type Name = String
type Age = Int
data Person = Person Name Age
let person = Person "SDK" 30
Wie kommen wir an die Eigenschaften heran?
Accessoren
getName (Person n _) = n
getAge (Person _ a) = a
Geht das noch besser/einfacher?
Records
data Person = Person
{ name :: String
, age :: Int
}
let p = Person "SDK" 30
let p = Person
{ name = "SDK"
, age = 30
}
Accessoren:
name :: Person -> Name
age :: Person -> Age
Records
Vorteile:
- automatische Accessoren
- extending: (ähnlich jQuery)
let newPerson =
p { name = "Flo" }
Nachteil:
- nicht-intialisierte Felder
- das geht nicht:
data Person =
Person
{ name :: Name
, age :: Age
}
data Dog =
Dog
{ name :: Name
, age :: Age
}
Lösung:
"OverloadedRecordFields" (Language-Extension)
Wrapper-Typen
-- wird vom Compiler in Int umgewandelt
newtype Natural = MkNatural Int
Datentypen sind lazy per default, können aber mit dem strictness-flag ("!
") als non-lazy bzw. strict deklariert werden. Beispiel:
data RealFloat a => Complex a = !a :+ !a
Strictness in Funktionen mit "seq
" oder "!$
".
Funktionen
- Typ-Signatur:
- polymorph, generisch
- nimmt Ausdruck, gibt Ausdruck zurück
- einschränkend:
- Typ "
a
" muss eine Instanz der Typ-Klasse "Ord
" sein (muss also sortierbar sein)
- Typ "
fun :: a -> b
compare :: Ord a => a -> a -> Ordering
Funktionen
Klassische Funktionen:
mult (a,b) = a*b
Haskell-Funktionen:
mult a b = a*b
multBy2 = mult 2
-- oder:
multBy2 = (*2)
Currying:
Lambda-Ausdrücke:
mult = \a b -> a*b
map2 = map (\a -> a * 2)
-- kürzer:
map2 = map (*2)
Operatoren
List-Concatanation:
infixr 5 ++
(++) :: [a] -> [a] -> [a]
(++) [] ys = ys
(++) (x:xs) ys = x : xs ++ ys
Function Composition:
infixr 9 .
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)
Operatoren
- infixe Funktionen
- präfixe Anwendung:
- normale Funktion infix:
- Rang und Assoziativität:
- je höher n, desto eher wird der Operator ausgeführt
- infixr: Rechts-Assoziativität
- infixl: Links-Assoziativität
let a = (+) 2 3
let a = 2 `mult` 3
infix[l/r] n operator
Beispiel:
infixl 7 *
infixl 6 +, -
infixr 0 $
Pattern Matching
contrived :: ([a], Char, (Int, Float), String, Bool) -> Bool
contrived ([], 'b', (1, 2.0), "hi", True) = False
As-Pattern:
f (x:xs) = x:x:xs
-- geht kürzer
f s@(x:xs) = x:s -- s == x:xs
fib@(1:tfib) = 1 : 1 : [ a+b | (a,b) <- zip fib tfib ]
Wild-Cards:
head (x:_) = x
tail (_:xs) = xs
Guards:
Case Expressions:
sign x | x > 0 = 1
| x == 0 = 0
| x < 0 = -1
let s = "1"
let default n = 0
let n = case reads s of
[(n,_)] -> n
_ -> 0
Let-Ausdrücke
checkPrefix s con action next =
let (pre, post) = T.splitAt (T.length s) con
in if pre == s then action post else next
Where-Ausdrücke
f x y | y > z = ...
| y == z = ...
| y < z = ...
where z = x*x
Typ-Klassen
- entsprechen in etwa den Interfaces im OOP oder virtual Functions in C++
- Typen werden als Instanzen einer Typ-Klasse definiert
- Intanziieren funktioniert per manuellem Überladen
- Bestimmte Basis-Typ-Klassen können automatisch "derived" werden
derive (Eq, Read, Show)
Input/Output (IO)
- IO ist monadisch
- IO ist typsicher
getChar :: IO Char
readFile :: FilePath -> IO Char
-- type FilePath = String
putChar :: Char -> IO () -- () ~= void
putStrLn :: String -> IO ()
writeFile :: FilePath -> String -> IO ()
Von IO:
Nach IO:
Die main-Funktion
main :: IO ()
main = do
c <- getChar
if c == 'y'
then return ()
else main
- ist der Einstiegspunkt jedes Haskell-Programms
public static void main( String[] args )
Vgl:
Actions
- Funktionen mit
IO
im Rückgabewert sindIO
-Actions -
do
-Notation kann angewandt werden - "syntactiv sugar" für die Arbeit mit Monaden
- reine Funktionen können keine
IO
-Actions enthalten - Ausnahme: unsafe Implementierungen wie
Debug.Trace
Do-Notation
do { a <- f ; m } ≡ f >>= \a -> do { m }
do { f ; m } ≡ f >> do { m }
do { m } ≡ m
do {
a <- f ;
b <- g ;
c <- h ;
return (a, b, c)
}
f >>= \a ->
g >>= \b ->
h >>= \c ->
return (a, b, c)
Do-Notation
(>>=) :: Monad m => m a -> (a -> m b) -> m b
(>>) :: Monad m => m a -> m b -> m b
Sequencing
putStr :: String -> IO ()
putStr = mapM_ putChar
putStr' = foldr ((>>) . putChar) (return ())
putStrLn' s = putStr s >> putChar '\n'
Zeit für ein wenig Real World Code!
Quo Vadis?
Haskell (PLUSPOL interactive)
By Stefan Von Der Krone
Haskell (PLUSPOL interactive)
- 459