Βασικές έννοιες τύπων ΙΙ
Αναρωτηθείτε ποιος είναι ο τύπος της συνάρτησης (+) χωρίς ακόμα να χρησιμοποιήσετε την :type;
Θα μπορούσε να είναι;
(+) :: Number -> Number -> Number
Όχι, διότι κάθε τύπος αριθμού (π.χ. ακέραιος, πραγματικός) αντιστοιχεί σε διαφορετικό τύπο της Haskell.
Θα μπορούσε να είναι ορισμένη πολυμορφικά;
(+) :: a -> a -> a
Όχι, διότι τότε η συνάρτηση (+) θα εφαρμόζονταν σε οποιαδήποτε ορίσματα του ίδιου τύπου, π.χ. Bool ή Char που δεν θα ήταν σωστό.
Ο τύπος που επιστρέφει η Haskell είναι.
(+) :: (Num a) => a -> a -> a
όπου Num είναι ένα typeclass — μια ομαδοποίηση τύπων — που περιέχει όλους τους τύπους που μπορούν να θεωρηθούν ως αριθμοί. Το τμήμα (Num a) => τηε υπογραφής της συνάρτησης περιορίζει το a σε αριθμητικούς τύπους (σε στιγμιότυπα της Num).
Αριθμητικοί τύποι
Οι σημαντικότεροι αριθμητικοί τύποι (που είναι στιγμιότυπα της Num) είναι οι Int (έχει περιορισμούς εύρους τιμών), Integer (υποστηρίζει μεγάλους αριθμούς), Double.
Prelude> (-7) + 5.12
-1.88
Prelude> :t (-7)
(-7) :: (Num a) => a
Η τιμή (-7) δεν είναι ούτε Int, ούτε Integer, αλλά μια πολυμορφική τιμή που μπορεί να μετασχηματιστεί σε οποιαδήποτε αριθμητικό τύπο.
Prelude> :t 5.12
5.12 :: (Fractional t) => t
Η τιμή 5.12 επίσης δεν είναι Double (ή Float), αλλά μια πολυμορφική τιμή της κλάσης Fractional που είναι υποκλάση του τύπου Num.
Στην πράξη (-7) + 5.12 η Haskell με την επαγωγή τύπων (type inference) θα συμπεράνει ότι το (-7) είναι οποιουδήποτε τύπου Num, αλλά λαμβάνοντας υπόψη τους περιορισμούς του 5.12 θα συμπεράνει ότι ο τύπος του είναι ο προκαθορισμένος για τον τύπο Fractional (υποκλάση του Num), δηλαδή ο Double (υποκλάση του Fractional). Για να γίνει η πράξη και το (-7) θα γίνει Double (υποκλάση του Num).
x = 2
Prelude> :load monitor_type1.hs
:type x
x :: Integer
x = 2
y = x + 3
Prelude> :load monitor_type2.hs
:type x
x :: Integer
:type y
y :: Integer
x = 2
y = x + 3.1
Prelude> :load monitor_type3.hs
:type x
x :: Double
:type y
y :: Double
Μονομορφικά προβλήματα
Ο τύπος του () είναι.
Prelude> :type (/)
(/) :: Fractional a => a -> a -> a
Οι πολυμορφικές τιμές 4 και 3 θα λάβουν τύπο Double λόγω του /
Prelude> 4 / 3
1.3333333333333333
Ωστόσο αυτό δεν μπορεί να γίνει στο ακόλουθο (δηλαδή η διαίρεση της ακέραιας τιμής 4 με το μήκος της λίστας [1,2,3]).
Prelude> length [1,2,3]
3
Prelude> 4 / length [1,2,3]
<interactive>:1:0:
No instance for (Fractional Int)
arising from a use of `/' at <interactive>:1:0-17
Possible fix: add an instance declaration for (Fractional Int)
In the expression: 4 / length [1, 2, 3]
In the definition of `it': it = 4 / length [1, 2, 3]
λόγω του ότι ο τύπος του length είναι ο ακόλουθος που επιστρέφει τιμή τύπου Int (μη πολυμορφικός τύπος που δεν είναι στιγμιότυπο του Fractional).
:type length
length :: Foldable t => t a -> Int
Το πρόβλημα λύνεται ως εξής.
Prelude> 4 / fromIntegral (length [1,2,3])
1.3333333333333333
χρησιμοποιώντας τη συνάρτηση fromIntegral που έχει τύπο που λαμβάνει μια τιμή Integral (π.χ. Int, Integer) και επιστρέφει μια πολυμορφική τιμή.
fromIntegral :: (Integral a, Num b) => a -> b
Κλάσεις πέρα από αριθμούς
Το παράδειγμα του Eq
H Haskell διαθέτει και άλλα typeclasses πέρα από τα typeclasses των αριθμών. Για παράδειγμα στην υπογραφή του (==) εμφανίζεται το typeclass Eq που αφορά τιμές που μπορούν να συγκριθούν για ισότητα.
(==) :: (Eq a) => a -> a -> Bool
Το παράδειγμα του Foldable
Επίσης, η συνάρτηση length δέχεται ως όρισμα μια τιμή του typeclass Foldable που είναι μια γενίκευση δομών ομαδοποίησης τιμών (υποκλάση της Foldable είναι και η λίστα [a]).