====== Curs 12. Typeclasses ====== -- import Prelude hiding (show, Show) -- import qualified Prelude as Prelude {- Part 1: Algebraic/Abstract datatypes -} data Nat = Zero | Succ Nat -- deriving Show add :: Nat -> Nat -> Nat add Zero x = x add (Succ y) x = Succ (add y x) toInt :: Nat -> Int toInt Zero = 0 toInt (Succ x) = 1 + toInt x {- 3 + 1 3 + 2 + 1 -} data Expr = Atom Int | Mult Expr Expr | Plus Expr Expr type Age = Int data Student = Student Int (String, String) Int | Absolvent Int (String, String) eval :: Expr -> Int eval (Atom x) = x eval (Mult e1 e2) = (eval e1) * (eval e2) eval (Plus e1 e2) = (eval e1) + (eval e2) e1 = Mult (Atom 1) (Atom 2) varsta :: Student -> Int varsta (Student x _ _) = x varsta (Absolvent x _) = x {- tipuri de date polimorfice!! -} data List a = Void | Cons a (List a) {- In Haskell programam cu functii obisnuite in lumea valorilor, inclusiv constructori de date programam cu constructori de tip in lumea tipurilor, atunci construim tipuri de date noi, DAR NU NUMAI! -} {- Sa presupunem ca vrem sa afisam expresii -} showExpr :: Expr -> String showExpr (Atom x) = Prelude.show x showExpr (Plus e1 e2) = (showExpr e1) ++ " + " ++(showExpr e2) showExpr (Mult e1 e2) = (showExpr e1) ++ " * " ++(showExpr e2) showNat :: Nat -> String showNat = Prelude.show . toInt -- compunerea de functii {- Pt fiecare tip de date nou, trebuie sa TINEM MINTE numele fiecareia Am dori: UN SINGUR NUME de functie "show", cu mai multe implementari. Polimorfism ad-hoc (vs polimorfism parametric) Vom folosi o constructie dedicata in Haskell: -} {- class Show a where show :: a -> String -} {- Show reprezinta O FAMILIE (o multime, sau colectie) de tipuri, sunt acele tipuri care pot fi afisate. Tipurile a din clasa Show sunt acele tipuri care suporta si implementeaza metoda show :: a -> String Cum inrolam tipuri noi intr-o clasa: -} instance Show Nat where show = Prelude.show . toInt instance Show Expr where show (Atom x) = Prelude.show x show (Plus e1 e2) = (show e1) ++ " + " ++ (show e2) show (Mult e1 e2) = (show e1) ++ " * " ++ (show e2) instance Eq Nat where Zero == Zero = True (Succ x) == (Succ y) = x == y _ == _ = False -- daca "a" este un membru al clasei Eq -- atunci "List a" este un membru al clasei Eq instance (Eq a) => Eq (List a) where Void == Void = True (Cons x xs) == (Cons y ys) = x == y && xs == ys _ == _ = False {- Ce altceva putem inrola intr-un type-class? -} -- Exista deja in Haskell: -- data Maybe a = Nothing | Just a data Option a = None | Some a deriving Show -- o functie care se comporta exact ca map: applyL :: (a -> b) -> (List a) -> (List b) applyL f Void = Void applyL f (Cons x xs) = Cons (f x) (applyL f xs) applyO :: (a -> b) -> (Option a) -> (Option b) applyO f None = None applyO f (Some x) = Some (f x) {- ?1 - ar trebui sa fie o colectie cu elemente tip a sa un constructor de tip ce primeste tipul a ?2 - ar trebui sa fie ACEEASI COLECTIE, dar cu elemente de tip b -} -- t este un constructor de tip avand kind-ul * => * {- class Functor t where fmap :: (a -> b) -> t a -> t b -} class Apply t where apply :: (a -> b) -> t a -> t b instance Functor List where fmap f Void = Void fmap f (Cons x xs) = Cons (f x) (fmap f xs) instance Functor Option where fmap f None = None fmap f (Some x) = Some (f x) -- f :: (a -> b) -> a -> b f g y = g y {- Operatia infix fmap este <$>. Este foarte utila pentru a scrie cod eficient si compact, la fel cum este si $. -}