summaryrefslogtreecommitdiffhomepage
path: root/el-gr/haskell-gr.html.markdown
diff options
context:
space:
mode:
Diffstat (limited to 'el-gr/haskell-gr.html.markdown')
-rw-r--r--el-gr/haskell-gr.html.markdown476
1 files changed, 476 insertions, 0 deletions
diff --git a/el-gr/haskell-gr.html.markdown b/el-gr/haskell-gr.html.markdown
new file mode 100644
index 00000000..d2fed85a
--- /dev/null
+++ b/el-gr/haskell-gr.html.markdown
@@ -0,0 +1,476 @@
+---
+language: Haskell
+filename: haskell-gr.html.markdown
+contributors:
+ - ["Miltiadis Stouras", "https://github.com/mstou"]
+---
+
+Η Haskell σχεδιάστηκε για να είναι μια πρακτική, αγνή συναρτησιακή γλώσσα προγραμματισμού.
+Είναι διάσημη για τα monads και το σύστημα τύπων της, αλλά χρησιμοποιείται από πολλούς
+κυρίως για την κομψότητά της. Προσωπικά θεωρώ ότι είναι από τις πιο όμορφες, αν όχι
+η πιο όμορφη, γλώσσα προγραμματισμού.
+
+```haskell
+-- Τα σχόλια μιας γραμμής ξεκινούν με 2 παύλες.
+{- Ενώ τα σχόλια πολλών γραμμών βρίσκονται
+μέσα σε blocks σαν αυτό
+-}
+
+----------------------------------------------------
+-- 1. Πρωτόγονοι Τύποι Δεδομένων (Primitive datatype) και Τελεστές
+----------------------------------------------------
+
+-- Οι αριθμοί είναι ένα primitive datatype
+3 -- 3
+
+-- Και οι τελεστές κάνουν αυτό που θα περιμέναμε
+1 + 1 -- 2
+8 - 1 -- 7
+10 * 2 -- 20
+35 / 5 -- 7.0
+
+-- Η καθιερωμένη διαίρεση δεν είναι ακέραια
+35 / 4 -- 8.75
+
+-- Η ακέραια διαίρεση γίνεται με την συνάρτηση div
+35 `div` 4 -- 8
+
+-- Και οι boolean μεταβλητές ειναι primitives
+True
+False
+
+-- Πράξεις με booleans
+not True -- False
+not False -- True
+1 == 1 -- True
+1 /= 1 -- False
+1 < 10 -- True
+
+-- Στα παραπάνω παραδείγματα, το `not` είναι μια συνάρτηση που παίρνει ένα όρισμα
+-- Στην Haskell δεν χρειάζονται παρενθέσεις για τις κλήσεις συναρτήσεων, όλες οι παράμετροι
+-- γράφονται με κενά αμέσως μετά την συνάρτηση. Στην γενική περίπτωση,
+-- η κλήση συνάρτησης μοιάζει κάπως έτσι: func arg1 arg2 arg3...
+-- Για το πως να ορίσετε τις δικές σας συναρτήσεις διαβάστε το κεφάλαιο των συναρτήσεων παρακάτω
+
+-- Συμβολοσειρές και χαρακτήρες
+"This is a string." -- συμβολοσειρά
+'a' -- χαρακτήρας
+'You cant use single quotes for strings.' -- error!
+-- δεν μπορούμε να γράψουμε συμβολοσειρές ανάμεσα από ''
+
+-- Οι συμβολοσειρές μπορούν να συννενωθούν με την χρήση του τελεστή ++
+"Hello " ++ "world!" -- "Hello world!"
+
+-- Η συμβολοσειρά είναι ουσιαστικά μια λίστα χαρακτήρων
+['H', 'e', 'l', 'l', 'o'] -- "Hello"
+"This is a string" !! 0 -- 'T'
+
+
+----------------------------------------------------
+-- 2. Λίστες και διατεταγμένα σύνολα (tuples)
+----------------------------------------------------
+
+-- Όλα τα στοιχεία μιας λίστας πρέπει να είναι του ίδιου τύπου
+-- Οι δύο παρακάτω λίστες είναι οι ίδιες:
+[1, 2, 3, 4, 5]
+[1..5] -- διάστημα ή range
+
+-- Τα διαστήματα μπορούν να χρησιμοποιηθούν και για άλλους τύπους εκτός από αριθμούς
+['A'..'F'] -- "ABCDEF"
+
+-- Μπορούμε ακόμη να ορίσουμε και ένα βήμα
+[0,2..10] -- [0, 2, 4, 6, 8, 10]
+[5..1] -- [] (Το default βήμα της Haskell είναι το 1, επομένως η διπλανή λίστα είναι κενή)
+[5,4..1] -- [5, 4, 3, 2, 1]
+
+-- Προσπέλαση στοιχείου σε τυχαία θέση
+[1..10] !! 3 -- 4 (οι δείκτες των θέσεων ξεκινούν από το 0)
+
+-- Στην Haskell υπάρχουν και άπειρες λίστες!
+[1..] -- η λίστα των φυσικών αριθμών
+
+-- Οι άπειρες λίστες μπορούν να λειτουργούν επειδή η Haksell έχει "lazy evaluation".
+-- Αυτό σημαίνει ότι η Haskell κάνει υπολογισμούς μόνο όταν πραγματικά χρειάζεται!
+-- οπότε αν ζητήσουμε το 1000στό στοιχείο μιας άπειρης λίστας θα μας το δώσει,
+-- ξέρει ότι δεν χρειάζεται να υπολογίσει όλη την άπειρη λίστα πρώτα!
+
+[1..] !! 999 -- 1000
+
+-- Στο παραπάνω παράδειγμα η Haskell υπολόγισε τα στοιχεία 1 μέχρι 1000...τα υπόλοιπα
+-- στοιχεία της άπειρης λίστας δεν υπάρχουν ακόμα! Η Haskell θα τα υπολογίσει
+-- μόνο αν κάποια στιγμή τα χρειαστεί.
+
+-- συνένωση δύο λιστών με τον τελεστή ++ (σε γραμμικό χρόνο)
+[1..5] ++ [6..10]
+
+-- προσθήκη στοιχείου στην αρχή της λίστας (σε σταθερό χρόνο)
+0:[1..5] -- [0, 1, 2, 3, 4, 5]
+
+-- περισσότερες συναρτήσεις για τις λίστες
+head [1..5] -- 1
+tail [1..5] -- [2, 3, 4, 5]
+init [1..5] -- [1, 2, 3, 4]
+last [1..5] -- 5
+
+-- list comprehensions
+-- ένας άλλος τρόπος να ορίζουμε τις λίστες που θυμίζει πολύ τον ορισμό συνόλων στα μαθηματικά!
+[x*2 | x <- [1..5]] -- [2, 4, 6, 8, 10]
+
+-- list comprehension με συνθήκη
+[x*2 | x <- [1..5], x*2 > 4] -- [6, 8, 10]
+
+-- Κάθε στοιχείο ενός tuple μπορεί να έχει διαφορετικό τύπο, όμως το tuple έχει σταθερό μέγεθος.
+-- Ένα tuple:
+("haskell", 1)
+
+-- προσπέλαση στοιχείων ενός ζεύγους στοιχείων (δηλαδή ενός tuple μεγέθους 2)
+fst ("haskell", 1) -- "haskell"
+snd ("haskell", 1) -- 1
+
+-- οι παραπάνω συναρτήσεις δεν λειτουργούν σε tuples μεγαλύτερου μεγέθους
+snd ("snd", "can't touch this", "da na na na") -- error!
+
+----------------------------------------------------
+-- 3. Συναρτήσεις
+----------------------------------------------------
+-- Μια απλή συνάρτηση που παίρνει 2 μεταβλητές a, b και επιστρέφει το άθροισμά τους
+add a b = a + b
+
+-- Προσέξτε ότι αν χρησιμοποιείτε το διαδραστικό περιβάλλον της Haskell (ghci), δηλαδή
+-- τον interpreter, θα πρέπει να προσθέσετε ενα `let` πριν τον ορισμό της συνάρτησης:
+-- let add a b = a + b
+
+-- Κλήση της συνάρτησης
+add 1 2 -- 3
+
+-- Μπορούμε να καλέσουμε την συνάρτηση και σαν τελεστή ανάμεσα στα 2 ορίσματα
+-- γράφοντας το όνομα της συνάρτησης μέσα σε backticks:
+1 `add` 2 -- 3
+
+-- Μπορούμε να ορίσουμε και συναρτήσεις που δεν έχουν γράμματα στο όνομά τους!
+-- Αυτό μας επιτρέπει να ορίσουμε δικούς μας τελεστές, όπως για παράδειγμα την ακέραια διάιρεση:
+
+(//) a b = a `div` b
+35 // 4 -- 8
+
+-- Guards: ένας εύκολος τρόπος να υλοποιήσουμε διακλαδώσεις σε μια συνάρτηση
+fib x
+ | x < 2 = 1
+ | otherwise = fib (x - 1) + fib (x - 2)
+
+-- Το ταίριασμα προτύπων (Pattern matching) είναι παρόμοιο.
+-- Εδώ δίνουμε 3 διαφορετικούς ορισμούς για την συνάρτηση fib
+-- H Haskell θα χρησιμοποιήσει αυτόματα τον πρώτο ορισμό το οποίου οι παράμετροι
+-- ταιριάζουν με τις παραμέτρους της κλήσης
+
+fib 1 = 1
+fib 2 = 2
+fib x = fib (x - 1) + fib (x - 2)
+
+-- Pattern matching σε tuples
+sndOfTriple (_, y, _) = y
+-- η κάτω παύλα χρησιμοποιείται για να μην δίνουμε ονόματα
+-- σε μεταβλητές που δεν θα χρησιμοποιήσουμε και
+-- ταιριάζει με όλους τους τύπους
+
+-- Pattern matching σε λίστες.
+-- Στο παρακάτω παράδειγμα, το `x` είναι το πρώτο στοιχείο της λίστας
+-- και τo `xs` είναι η λίστα με τα υπόλοιπα στοιχεία
+
+myMap func [] = []
+myMap func (x:xs) = func x : (myMap func xs)
+
+-- Μπορούμε να ορίσουμε και ανώνυμες συναρτήσεις (lambdas) χρησιμοποιώντας το
+-- backslash (που μοιάζει με λ) ακολουθούμενο από τις παραμέτρους:
+myMap (\x -> x + 2) [1..5] -- [3, 4, 5, 6, 7]
+
+-- χρήση της συνάρτησης fold με μία ανώνυμη συνάρτηση
+-- Το foldl1 είναι σαν fold από αριστερά, αλλά χρησιμοποιεί σαν αρχική τιμή του
+-- accumulator το πρώτο στοιχείο της λίστας.
+foldl1 (\acc x -> acc + x) [1..5] -- 15
+
+----------------------------------------------------
+-- 4. Περισσότερες συναρτήσεις
+----------------------------------------------------
+
+-- Μερική κλήση: αν δεν περάσουμε όλες τις μεταβλητές σε μια συνάρτηση,
+-- τότε αυτή "καλείται μερικώς". Αυτό σημαίνει ότι μας επιστρέφει μια συνάρτηση
+-- η οποία παίρνει ως ορίσματα τις εναπομείνασες μεταβλητές
+
+add a b = a + b
+foo = add 10 -- η foo είναι μια συνάρτηση που περιμένει 1 αριθμό και του προσθέτει 10
+foo 5 -- 15
+
+-- Ένας άλλος τρόπος να γράψουμε το ίδιο πράγμα:
+foo = (10+)
+foo 5 -- 15
+
+-- Σύνθεση συναρτήσεων
+-- Ο τελεστής `.` χρησιμοποιείται για την σύνθεση ("αλυσίδωση") συναρτήσεων.
+-- Για παράδειγμα, η foo παρακάτω είναι μια συνάρτηση που παίρνει ως όρισμα 1 αριθμό.
+-- Πρώτα προσθέτει 10 στον αριθμό που δώσαμε και μετά πολλαπλασιάζει το αποτέλεσμα με 4
+foo = (4*) . (10+)
+
+-- 4*(10+5) = 60
+foo 5 -- 60
+
+-- διόρθωση προτεραιότητας
+-- Στην Haskell υπάρχει ο τελεστής `$`. Ο τελεστής αυτός εφαρμόζει μια συνάρτηση
+-- σε μία παράμετρο. Σε αντίθεση με την απλή εφαρμογή συνάρτησης, η οποία έχει
+-- την μεγαλύτερη πιθανή προτεραιότητα και είναι αριστερά προσεταιριστική,
+-- ο τελεστής `$` έχει την ελάχιστη προτεραιότητας και είναι δεξιά προσεταιριστικός.
+-- Λόγω της χαμηλής του προτεραιότητας, η έκφραση που βρίσκεται στα δεξιά του
+-- θα υπολογιστεί και θα περαστεί σαν παράμετρος στην συνάρτηση που βρίσκεται στα αριστερά του
+
+
+-- πριν
+even (fib 7) -- false
+
+-- ισοδύναμα
+even $ fib 7 -- false
+
+-- χρησιμοποιόντας σύνθεση συναρτήσεων
+even . fib $ 7 -- false
+
+
+----------------------------------------------------
+-- 5. Τύποι
+----------------------------------------------------
+
+-- Η Haskell έχει ένα πολύ ισχυρό σύστημα τύπων, στο οποίο κάθε έκφραση έχει έναν τύπο
+
+-- Κάποιο βασικοί τύποι:
+5 :: Integer
+"hello" :: String
+True :: Bool
+
+-- Και οι συναρτήσεις έχουν κάποιο τύπο
+-- Η συνάρτηση`not` παίρνει ένα boolean και επιστρέφει ένα boolean:
+-- not :: Bool -> Bool
+
+-- Παρακάτω βλέπετε μια συνάρτηση που παίρνει 2 ορίσματα:
+-- add :: Integer -> Integer -> Integer
+
+-- Όταν ορίζουμε μια συνάρτηση ή μεταβλητή, είναι καλή πρακτική να γράφουμε
+-- και τον τύπο της:
+double :: Integer -> Integer
+double x = x * 2
+
+----------------------------------------------------
+-- 6. Έλεγχος ροής και συνθήκες
+----------------------------------------------------
+
+-- if-expressions
+haskell = if 1 == 1 then "awesome" else "awful" -- haskell = "awesome"
+
+-- τα if-expressions μπορούν να πιάνουν και πολλές γραμμές
+-- αλλά η στοίχιση είναι σημαντική!
+haskell = if 1 == 1
+ then "awesome"
+ else "awful"
+
+-- case expressions: Με τον παρακάτω τρόπο θα μπορούσαμε να κάνουμε parse
+-- command line arguments
+case args of
+ "help" -> printHelp
+ "start" -> startProgram
+ _ -> putStrLn "bad args"
+
+-- Η Haskell δεν έχει βρόχους επανάληψης; αντιθέτως, χρησιμοποιούμε αναδρομή.
+-- Η συνάρτηση map εφαρμόζει μια συνάρτηση σε κάθε στοιχείο μιας λίστας
+
+map (*2) [1..5] -- [2, 4, 6, 8, 10]
+
+-- μπορούμε να κατασκευάσουμε τον βρόχο for χρησιμοποιώντας την map
+for array func = map func array
+
+-- και να τον χρησιμοποιήσουμε
+for [0..5] $ \i -> show i
+
+-- το παραπάνω θα μπορούσε να γραφτεί και έτσι:
+for [0..5] show
+
+-- Μπορούμε να χρησιμοποιήσουμε τις συναρτήσεις foldl και foldr
+-- για να υπολογίζουμε μια τιμή από μια λίστα (πχ άθροισμα ή γινόμενο)
+-- foldl <fn> <initial value> <list>
+foldl (\x y -> 2*x + y) 4 [1,2,3] -- 43
+
+-- Η παραπάνω κλήση είναι η ίδια με:
+(2 * (2 * (2 * 4 + 1) + 2) + 3)
+
+-- Η foldl γίνεται από τα αριστερά ενώ η foldr από τα δεξιά
+foldr (\x y -> 2*x + y) 4 [1,2,3] -- 16
+
+-- Η παραπάνω κλήση είναι τώρ:
+(2 * 1 + (2 * 2 + (2 * 3 + 4)))
+
+----------------------------------------------------
+-- 7. Τύποι δεδομένων
+----------------------------------------------------
+
+-- Με τον παρακάτω τρόπο μπορούμε να ορίζουμε δικούς μας τύπους
+-- δεδομένων στην Haskell
+
+data Color = Red | Blue | Green
+
+-- Τώρα μπορούμε να χρησιμοποιήσουμε τον τύπο μας και σε συναρτήσεις:
+
+say :: Color -> String
+say Red = "You are Red!"
+say Blue = "You are Blue!"
+say Green = "You are Green!"
+
+-- Οι τύποι δεδομένων μας μπορεί να είναι και παραμετρικοί, να δέχονται δηλαδή
+-- κάποιον τύπο ως παράμετρο
+
+data Maybe a = Nothing | Just a
+
+-- Όλες οι παρακάτω τιμές έχουν τύπο Maybe
+Just "hello" -- of type `Maybe String`
+Just 1 -- of type `Maybe Int`
+Nothing -- of type `Maybe a` for any `a`
+
+----------------------------------------------------
+-- 8. Haskell IO
+----------------------------------------------------
+
+-- Αν και το IO δεν μπορεί να εξηγηθεί σε βάθος χωρίς να εξηγήσουμε
+-- πρώτα τα monads, δεν είναι δύσκολο να το εξηγήσουμε αρκετά ώστε να μπορεί
+-- κάποιος να το χρησιμοποιήσει
+
+-- Όταν ένα πρόγραμμα Haskell εκτελείται, καλείται η συνάρτηση `main`
+-- Η συνάρτηση αυτή πρέπει να επιστρέφει τύπο `IO a` για κάποιο τύπο `a`.
+-- Για παράδειγμα:
+
+main :: IO ()
+main = putStrLn $ "Hello, sky! " ++ (say Blue)
+-- η συνάρτηση putStrLn έχει τύπο: String -> IO ()
+
+-- Είναι πιο εύκολο να χρησιμοποιήσουμε IO αν μπορούμε να γράψουμε το πρόγραμμά μας
+-- ως μια συνάρτηση από String σε String. Η συνάρτηση
+-- interact :: (String -> String) -> IO ()
+-- παίρνει ως είσοδο ένα string, τρέχει μια συνάρτηση πάνω στην είσοδο
+-- και τυπώνει την έξοδο
+
+countLines :: String -> String
+countLines = show . length . lines
+
+main' = interact countLines
+
+-- Μπορείτε να σκεφτείτε μια συνάρτηση που επιστρέφει τιμή με τύπο `IO ()`
+-- ως μια ακολουθία πράξεων, περίπου όπως και σε μια imperative γλώσσα
+-- Μπορούμε να χρησιμοποιήσουμε το `do` και να ενώσουμε αυτές τις κλήσεις
+-- Για παράδειγμα:
+
+sayHello :: IO ()
+sayHello = do
+ putStrLn "What is your name?"
+ name <- getLine -- η συνάρτηση αυτή διαβάζει μια γραμμή και την αναθέτει στην μετβαλήτη name
+ putStrLn $ "Hello, " ++ name
+
+-- Δοκιμάστε να γράψετε την συνάρτηση `interact` που θα διαβάζει μια γραμμή
+
+-- Ωστόσο ο κώδικας της συνάρτησης `sayHello` δεν θα εκτελεστεί ποτέ. Η μόνη συνάρτηση
+-- που εκτελείται όταν κάνουμε compile ένα αρχείο haskell είναι η `main`.
+-- Αν θέλετε να τρέξετε την sayHello (εκτός από το να φορτώσετε τον κώδικα στο
+-- ghci) μπορείτε να βάλετε σε σχόλια τον προηγούμενο ορισμό της main
+-- και να την ορίσετε ως:
+-- main = sayHello
+
+-- Ας προσπαθήσουμε να καταλάβουμε πως λειτουργεί η συνάρτηση `getLine`
+-- Ο τύπος της είναι:
+-- getLine :: IO String
+-- Μπορείτε να φανταστείτε ότι μια τιμή με τύπο `IO a` θα παραχθεί
+-- από ένα πρόγραμμα που παράγει μια τιμή με τύπο `a` (ενώ παράλληλα κάνει και κάτι άλλο)
+-- Μπορούμε να πάρουμε και να επαναχρησιμοποιήσουμε αυτήν την τιμή χρησιμοποιώντας
+-- το `<-`. Μπορούμε ακόμα και να φτιάξουμε την δική μας συνάρτηση με τύπο
+-- `IO String`:
+
+action :: IO String
+action = do
+ putStrLn "This is a line. Duh"
+ input1 <- getLine
+ input2 <- getLine
+ -- Ο τύπος του `do` μπλοκ είναι εκείνος της τελευταίας γραμμής.
+ -- Το `return` δεν είναι κάποια ειδική λέξη, αλλά απλώς μια συνάρτηση
+ return (input1 ++ "\n" ++ input2) -- return :: String -> IO String
+
+-- Μπορούμε να χρησιμοποιήσουμε την παραπάνω συνάρτηση ακριβώς όπως την `getLine`:
+
+main'' = do
+ putStrLn "I will echo two lines!"
+ result <- action
+ putStrLn result
+ putStrLn "This was all, folks!"
+
+-- Ο τύπος `IO` είναι παράδειγμα ενός "monad". Χρησιμοποιώντας τα monads για το
+-- ΙΟ, η Haskell καταφέρνει να είναι αγνή συναρτησιακή γλώσσα. Κάθε συνάρτηση που
+-- αλληλεπιδρά με τον έξω κόσμο (δηλαδή κάνει IO), έχει το IO (ή κάποιο άλλο monad)
+-- στον τύπο της. Αυτό μας διευκολύνει να γνωρίζουμε ποιές συναρτήσεις είναι αγνές
+-- (μαθηματικές -- δεν αλληλεπιδρούν με τον έξω κόσμο ούτε αλλάζουν κάποιο state)
+-- και ποιες δεν είναι.
+
+-- Αυτό είναι ένα πολύ ισχυρό χαρακτηριστικό γιατί είναι πολύ εύκολο να
+-- εκτελούμε παράλληλα αγνές συναρτήσεις! Οπότε η παραλληλοποίηση στην Haskell
+-- είναι αρκετά πιο εύκολη
+
+----------------------------------------------------
+-- 9. Haskell REPL
+----------------------------------------------------
+
+-- Μπορείτε να ξεκινήσετε το διαδραστικό περιβάλλον της Haskell με την εντολή `ghci`.
+-- Εδώ μπορείτε να γράψετε και να εκτελέσετε κώδικα haskell.
+-- Κάθε νέα τιμή πρέπει να ορίζεται με το `let`
+
+let foo = 5
+
+-- Μπορείτε να βρείτε τον τύπο μιας συνάρτησης με το `:t`:
+
+> :t foo
+foo :: Integer
+
+-- Οι τελεστές, όπως οι `+`, `:` και `$`, είναι επίσης συναρτήσεις.
+-- Μπορούμε να δούμε τον τύπο τους βάζοντας τους μέσα σε παρενθέσεις:
+
+> :t (:)
+(:) :: a -> [a] -> [a]
+
+-- Για περισσότερες πληροφορίες για οποιαδήποτε συνάρτηση ή τύπο,
+-- μπορείτε να χρησιμοποιήσετε το `:i`:
+
+> :i (+)
+class Num a where
+ (+) :: a -> a -> a
+ ...
+ -- Defined in ‘GHC.Num’
+infixl 6 +
+
+-- Μπορείτε επίσης να τρέξετε κάθε συνάρτηση με τύπο `IO ()`
+
+> sayHello
+What is your name?
+Friend!
+Hello, Friend!
+
+```
+
+Υπάρχουν πολλά ακόμα πράγματα να εξερευνήσετε στην Haskell, όπως τα typeclasses
+και διάφορα monads! Αυτές οι μαθηματικά ορισμένες έννοιες είναι που κάνουν την
+Haskell αυστηρή, αγνή και κομψή! Θα τελειώσουμε αυτήν την σύντομη περιήγηση με
+ένα τελευταίο παράδειγμα, η υλοποίηση της QuickSort σε Haskell:
+
+```haskell
+qsort [] = []
+qsort (p:xs) = qsort lesser ++ [p] ++ qsort greater
+ where lesser = filter (< p) xs
+ greater = filter (>= p) xs
+```
+
+Υπάρχουν 2 παραδοσιακοί τρόποι να εγκαταστήσετε την Haskell:
+- [Cabal-based installation](http://www.haskell.org/platform/),
+- [Stack-based process](https://www.stackage.org/install).
+
+Στις παρακάτω πηγές μπορείτε να βρείτε αρκετά κομψές εισαγωγές στην Haskell
+- [Learn you a Haskell](http://learnyouahaskell.com/),
+- [Happy Learn Haskell Tutorial](http://www.happylearnhaskelltutorial.com/),
+- [Real World Haskell](http://book.realworldhaskell.org/)