Haskell
H Haskell είναι μια αμιγώς συναρτησιακή γλώσσα προγραμματισμού (pure functional programming language) οκνηρής αποτίμησης (lazy evaluation). Η Haskell διαθέτει ισχυρό στατικό σύστημα τύπων (strong static type system) που αντιλαμβάνεται τους τύπους που απαιτούνται για τις συναρτήσεις και τις παραμέτρους τους.
Τα παραδείγματα κώδικα που ακολουθούν είναι από το "7 Languages in 7 Weeks, by Bruce A. Tate - A Pragmatic Guide to Learning Programming Languages" και από άλλες πηγές.
Εγκατάσταση της Haskell
H Haskell (η Glasgow Haskell) μπορεί να εγκατασταθεί χρησιμοποιώντας το GHCup, ακολουθώντας τις οδηγίες από το https://www.haskell.org/downloads/.
Εναλλακτικά, για την εγκατάσταση της Haskell μπορεί να χρησιμοποιηθεί το εργαλείο stack, ακολουθώντας τις οδηγίες από το https://docs.haskellstack.org/en/stable/.
Ο διερμηνευτής της Haskell
Ενεργοποίηση και τερματισμός του διερμηνευτή ghci της Haskell.
$ ghci
GHCi, version 9.4.8: https://www.haskell.org/ghc/ :? for help
ghci> "Hello World"
"Hello World"
ghci> :quit
Leaving GHCi.
$
$ stack ghci
tests> initial-build-steps (lib)
Configuring GHCi with the following packages: tests.
GHCi, version 9.2.8: https://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /Users/chgogos/.cache/stack/ghci-script/2a3bbd58/ghci-script
ghci> 2 ^100
1267650600228229401496703205376
ghci> :q
Leaving GHCi.
$
Εκφράσεις και βασικοί τύποι
Αριθμοί
ghci> 1 + 2
3
ghci> (+) 1 2
3
ghci> 1 + 2 * 3
7
ghci> 1 + 2 * 3.0
7.0
ghci> 2 ^ 10
1024
ghci> div 10 3
3
ghci> 10 `div` 3 -- div ως infix τελεστής με backticks `
3
ghci> mod 10 3
1
ghci> 10 `mod` 3 -- mod ως infix τελεστής με backticks `
1
ghci> gcd 24 56
8
Λεκτικά
ghci> "hi"
"hi"
ghci> "hello " ++ "world"
"hello world"
-- δεικτοδότηση λεκτικού με το !!
ghci> "hello" !! 1
'e'
Χαρακτήρες
Ένα λεκτικό είναι μια λίστα χαρακτήρων.
Λογικές τιμές
Συναρτήσεις
Οι συναρτήσεις στην Haskell αποτελούνται από δύο τμήματα. Ένα προαιρετικό τμήμα δήλωσης του τύπου της συνάρτησης. Ένα τμήμα με την υλοποίηση της συνάρτησης. Η Haskell έχει ένα ισχυρό σύστημα συμπερασμού για τον εντοπισμό των τύπων που χρησιμοποιούνται στις συναρτήσεις.
Πρόσδεση (binding) της μεταβλητής x
σε τοπική εμβέλεια.
Πρόσδεση της συνάρτησης double
σε τοπική εμβέλεια.
Αποθήκευση συνάρτησης σε αρχείο.
Αρχείο day1.hs με ορισμούς των συναρτήσεων: double, fact, factpm, factg, fibpm, fibt, fibc, size, prod, allEven
Φόρτωση κώδικα, κλήση συνάρτησης.
$ ghci
GHCi, version 9.4.8: https://www.haskell.org/ghc/ :? for help
ghci> :load day1.hs
[1 of 2] Compiling Main ( day1.hs, interpreted )
Ok, one module loaded.
ghci> double 10
20
ghci> :quit
Leaving GHCi
ή
$ ghci day1.hs
GHCi, version 9.4.8: https://www.haskell.org/ghc/ :? for help
[1 of 2] Compiling Main ( day1.hs, interpreted )
Ok, one module loaded.
ghci> double 10
20
ghci> :quit
Leaving GHCi
Αναδρομή (recursion)
Ορισμός της συνάρτησης fact απευθείας στο ghci με τη χρήση της if-then-else.
Ταίριασμα προτύπων (pattern matching)
Ορισμός της multiline συνάρτησης factpm
απευθείας στο ghci, προσοχή στη χρήση των συμβόλων :{
και :}
.
ghci> :{
ghci| factpm :: Integer -> Integer
ghci| factpm 0 = 1
ghci| factpm x = x * factpm (x - 1)
ghci| :}
ghci> factpm 10
3628800
Φρουροί (guards)
Οι φρουροί είναι συνθήκες που περιορίζουν τις τιμές των ορισμάτων όπως στη συνέχεια. Όταν η συνθήκη ενός φρουρού ικανοποιείται η Haskell καλεί την κατάλληλη συνάρτηση.
-- ο κώδικας βρίσκεται στο αρχείο day1.hs
factg :: Integer -> Integer
factg x
| x > 1 = x * factg (x-1)
| otherwise 1
Πλειάδες και λίστες
Στις πλειάδες τα στοιχεία μπορούν να είναι διαφορετικού τύπου ενώ στις λίστες όλα τα στοιχεία είναι του ίδιου τύπου.
Μια λίστα και μια πλειάδα. Με το :type
μπορούμε να δούμε τον τύπο μιας έκφρασης.
ghci>:type [1,2,3]
[1,2,3] :: Num a => [a]
ghci> :type ('1', "2", 3, 4)
('1', "2", 3, 4) :: (Num c, Num d) => (Char, String, c, d)
Πρώτο (fst) και δεύτερο (snd) στοιχείο σε μια πλειάδα
Συνάρτηση εύρεσης όρων της ακολουθίας fibonacci.
-- ο κώδικας βρίσκεται στο αρχείο day1.hs
fib :: Integer -> Integer
fib 0 = 1
fib 1 = 1
fib x = fib (x - 1) + fib (x - 2)
Υλοποίηση ταχύτερης έκδοσης της συνάρτησης fib
(με χρήση πλειάδων).
-- ο κώδικας βρίσκεται στο αρχείο day1.hs
fibTuple :: (Integer, Integer, Integer) -> (Integer, Integer, Integer)
fibTuple (x, y, 0) = (x, y, 0)
fibTuple (x, y, index) = fibTuple (y, x + y, index - 1)
fibResult :: (Integer, Integer, Integer) -> Integer
fibResult (x, y, z) = x
fib :: Integer -> Integer
fib x = fibResult (fibTuple (0, 1, x))
head, tail, init, last σε λίστες
ghci> head [1,2,3,4]
1
ghci> tail [1,2,3,4]
[2,3,4]
ghci> let (h:t) = [1,2,3,4]
ghci> h
1
ghci> t
[2,3,4]
ghci> init [1,2,3,4]
[1,2,3]
ghci> last [1,2,3,4]
4
Σύνθεση (composition)
Διάσχιση λιστών
Μέγεθος λίστας
Γινόμενο τιμών λίστας
Συνδυασμός λιστών με το zip
Συνδυασμός λιστών με συνάρτηση με το zipWith
Δημιουργία λιστών
Το σύμβολο :
λέγεται cons και διαχωρίζει την κεφαλή από την ουρά της λίστας.
Συνάρτηση που επιστρέφει μια λίστα με τους άρτιους αριθμούς μιας λίστας
-- ο κώδικας βρίσκεται στο αρχείο day1.hs
allEven :: [Integer] -> [Integer]
allEven [] = []
allEven (h:t) = if even h then h:allEven t else allEven t
Περιοχές τιμών (ranges)
Μη φραγμένες περιοχές τιμών
Περιφραστικές λίστες (list comprehensions)
ghci> [x * 2 | x <- [1, 2, 3]]
[2,4,6]
ghci> [ (y, x) | (x, y) <- [(1, 2), (2, 3), (3, 1)]]
[(2,1),(3,2),(1,3)]
ghci> [ (y, x) | (x, y) <- [(1, 2), (2, 3), (3, 1)]]
[(2,1),(3,2),(1,3)]
ghci> let a = [1..10]
ghci> let b = [0,5,20]
ghci> [(x,y) | x <- a, y<-b]
[(1,0),(1,5),(1,20),(2,0),(2,5),(2,20),(3,0),(3,5),(3,20),(4,0),(4,5),(4,20),(5,0),(5,5),(5,20),(6,0),(6,5),(6,20),(7,0),(7,5),(7,20),(8,0),(8,5),(8,20),(9,0),(9,5),(9,20),(10,0),(10,5),(10,20)]
ghci> [(x,y) | x <- a, y<-b, x < y]
[(1,5),(1,20),(2,5),(2,20),(3,5),(3,20),(4,5),(4,20),(5,20),(6,20),(7,20),(8,20),(9,20),(10,20)]
Συναρτήσεις υψηλότερης τάξης
Αρχείο day2.hs με ορισμούς των συναρτήσεων: squareAll, myRange
Ανώνυμες συναρτήσεις
Η σύνταξη των ανώνυμων συναρτήσεων (λάμδα συναρτήσεων) στη Haskell είναι η ακόλουθη:
Μια ανώνυμη συνάρτηση επιστροφής του τετραγώνου της παραμέτρου της και η κλήση της με παράμετρο την τιμή 5.
Μια ανώνυμη συνάρτηση με δύο παραμέτρους:
map
-- ο κώδικας βρίσκεται στο αρχείο day2.hs
squareAll :: Num b => [b] -> [b]
squareAll list = map square list
where square x = x * x
H (+1)
είναι μια μερικά εφαρμοσμένη συνάρτηση. Πρόκειται για τη συνάρτηση +
στην οποία έχει προσδεθεί η μια από τις δύο παραμέτρους με τιμή 1. Η δεύτερη παράμετρος σε κάθε κλήση της συνάρτησης θα είναι μια τιμή της λίστας.
filter
foldl
Μερικά εφαρμοσμένες συναρτήσεις και currying
Στη Haskell κάθε συνάρτηση ουσιαστικά έχει μια μόνο παράμετρο.
ghci> let prod x y = x * y
ghci> prod 3 4
12
ghci> (prod 3) 4
12
ghci> tripple = prod 3
ghci> tripple 4
12
Οκνηρή αποτίμηση
H Haskell επιτρέπει άπειρες λίστες να επιστρέφονται από συναρτήσεις.
Ορισμός μιας άπειρης λίστας.
-- ο κώδικας βρίσκεται στο αρχείο day2.hs
myRange :: Num t => t -> t -> [t]
myRange start step = start:(myRange (start + step) step)
Σύνταξη της Haskell για μη φραγμένες περιοχές τιμών.
Τύποι
Πρωτογενείς τύποι
Αρχείο day3.hs με ορισμούς τύπων δεδομένων: Suit, Rank, Card, Hand, Tree
Τύποι ορισμένοι από τον χρήστη
-- ο κώδικας βρίσκεται στο αρχείο day3.hs
data Suit = Spades | Hearts deriving (Show)
data Rank = Ten | Jack | Queen | King | Ace deriving (Show)
type Card = (Rank, Suit)
type Hand = [Card]
Αναδρομικοί τύποι
ghci> let tree = Children[Leaf 1, Children [Leaf 2, Leaf 3]]
ghci> tree
Children[Leaf 1, Children [Leaf 2, Leaf 3]]
Μεταγλώττιση και εκτέλεση κώδικα
example1.hs | |
---|---|
Εκτέλεση από τον διερμηνευτή ghci
$ ghci example1.hs
GHCi, version 9.4.8: https://www.haskell.org/ghc/ :? for help
[1 of 2] Compiling Main ( example1.hs, interpreted )
Ok, one module loaded.
ghci> main
1405006117752879898543142606244511569936384000000000
Μεταγλώττιση από το ghc, εκτέλεση
$ ghc example1.hs
[1 of 2] Compiling Main ( example1.hs, example1.o )
[2 of 2] Linking example1
$ ./example1
1405006117752879898543142606244511569936384000000000
Μεταγλώττιση και εκτέλεση με το runghc
Ανάγνωση και εγγραφή σε αρχεία
Ανάγνωση από το αρχείο κειμένου input_for_haskell.txt
fileReadFrom.hs | |
---|---|
Μεταγγλώττιση και εκτέλεση:
$ ghc fileReadFrom.hs
[1 of 2] Compiling Main ( fileReadFrom.hs, fileReadFrom.o )
[2 of 2] Linking fileReadFrom
$ ./fileReadFrom
[["maria","8"],["petros","9"],["kostas","5"]]
Εγγραφή περιεχομένων λίστας στο αρχείο κειμένου "output_from_haskell.txt"
fileWriteTo.hs | |
---|---|
Μεταγγλώττιση και εκτέλεση:
$ ghc fileWriteTo.hs
[1 of 2] Compiling Main ( fileWriteTo.hs, fileWriteTo.o )
[2 of 2] Linking fileWriteTo
$ ./fileWriteTo
"Write successed!"
Το αρχείο output_from_haskell.txt αποθηκεύεται στον τρέχοντα φάκελο.
Βιβλία - σημειώσεις
- Σταματόπουλος, Π. (2015). Λογικός και συναρτησιακός προγραμματισμός [Προπτυχιακό εγχειρίδιο]. Κάλλιπος, Ανοικτές Ακαδημαϊκές Εκδόσεις. https://hdl.handle.net/11419/3587
- Learn You a Haskell for Great Good
Videos
- Graham Hutton - Functional Programming
- Philipp Hagenlocher - Haskell for Imperative Programmers
- Curtis D'Alves - Learning Haskell
- Haskell Course by IOG Academy
Μαθήματα
- Haskell MOOC Part 1 & Part 2 - by Joel Kaasinen (Nitor) and John Lång (University of Helsinki)
- Beginner crash course - by Type Classes
- Haskell and Functional Programming course for complete beginners - by Dmitrii Kovanikov
- Functional Programming Course - by Tony Morris & Mark Hibberd
- Haskell via Sokoban by Joachim Breitner