Haskell
Eine funktionale Programmiersprache
filter :: (a -> Bool) -> [a] -> [a]
filter _ [] = []
filter pred (x:xs)
| pred x = x : filter pred xs
| otherwise = filter pred xsQuick-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 * xsort :: 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 Charlet s = "abc" -- Typ String, bzw. [Char]let i = 0 -- Typ Intlet i = 0 :: Integer -- Typ Integerlet d = 0.0 -- Typ Doublelet d = 0 :: Double -- Typ Doublelet b = True -- Typ Bool, Gegenteil: Falselet l = [1,2,3,4] -- Typ [Int]let l = [1..10] -- Kurzform einer Listelet 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
| Sundaydata Person =
Person String IntTyp-Synonyme
type Name = String
type Age = Intdata Person = Person Name Agelet person = Person "SDK" 30Wie kommen wir an die Eigenschaften heran?
Accessoren
getName (Person n _) = n
getAge (Person _ a) = aGeht 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 -> AgeRecords
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 IntDatentypen 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 -> bcompare :: Ord a => a -> a -> OrderingFunktionen
Klassische Funktionen:
mult (a,b) = a*bHaskell-Funktionen:
mult a b = a*bmultBy2 = 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 ++ ysFunction 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 3let a = 2 `mult` 3infix[l/r] n operatorBeispiel:
infixl 7 *
infixl 6 +, -
infixr 0 $Pattern Matching
contrived :: ([a], Char, (Int, Float), String, Bool) -> Bool
contrived ([], 'b', (1, 2.0), "hi", True) = FalseAs-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) = xsGuards:
Case Expressions:
sign x | x > 0 = 1
| x == 0 = 0
| x < 0 = -1let s = "1"
let default n = 0
let n = case reads s of
[(n,_)] -> n
_ -> 0Let-Ausdrücke
checkPrefix s con action next =
let (pre, post) = T.splitAt (T.length s) con
in if pre == s then action post else nextWhere-Ausdrücke
f x y | y > z = ...
| y == z = ...
| y < z = ...
where z = x*xTyp-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 = StringputChar :: 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
IOim 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 } ≡ mdo {
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 bSequencing
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)
- 523