Podpůrné nástroje (HLint, Haddock), funktory IB016 Seminář z funkcionálního programování Vladimír Štill, Martin Ukrop Fakulta informatiky, Masarykova univerzita Jaro 2018 IB016: Cvičení 02 Jaro 2018 1 / 17 GHC warnings GHC při kompilaci může vypisovat kromě chyb i varování varování se zapíná argumentem při volaní GHC, resp. :set v GHCi -W zapíná větší sadu varování -Wall zapíná většinu varování -w vypíná všechna varování -Werror vyhodnocuje varování jako chyby úplný přehled varování v sekci dokumentace 4.8 Warnings and sanity checking IB016: Cvičení 02 Jaro 2018 2 / 17 GHC warnings GHC při kompilaci může vypisovat kromě chyb i varování varování se zapíná argumentem při volaní GHC, resp. :set v GHCi -W zapíná větší sadu varování -Wall zapíná většinu varování -w vypíná všechna varování -Werror vyhodnocuje varování jako chyby úplný přehled varování v sekci dokumentace 4.8 Warnings and sanity checking Standard pro domácí úkoly IB016 je čistý překlad s -Wall! možno přidat :set -Wall do ~/.ghc/ghci.conf IB016: Cvičení 02 Jaro 2018 2 / 17 Hlint Nástroj hlásící návrhy na zlepšení kódu („linter“) samostatný balík z Hackage, nutno doinstalovat často dostupný přímo v repozitářích distribuce FI PC: nainstalovaný z repozitářů Ubuntu více podrobností v samostatném návodu v ISu závisí na generátoru parsrů Happy cabal install happy hlint hlint [--hint ] možno integrovat přímo do GHCi, viz návod v ISu soubor s extra definicemi v ISu IB016: Cvičení 02 Jaro 2018 3 / 17 Dokumentace v Haskellu: Haddock -- | The 'square' function squares an integer. square :: Int -> Int square x = x * x data T a b = C1 a b -- ^ info about constructor 'C1' | C2 a b -- ^ info about constructor 'C2' syntax komentářů pro automatické zpracování generování HTML dokumentace programem haddock mkdir -p doc && haddock file.hs --html -o doc více info viz oficiální dokumentace IB016: Cvičení 02 Jaro 2018 4 / 17 Testování pomocí balíku HUnit Základní v Haskellu obecně: mnoho příspupů i balíků dnes HUnit, ideově vycházející z JUnit v průběhu semestru ještě QuickCheck IB016: Cvičení 02 Jaro 2018 5 / 17 Testování pomocí balíku HUnit Základní v Haskellu obecně: mnoho příspupů i balíků dnes HUnit, ideově vycházející z JUnit v průběhu semestru ještě QuickCheck Balík HUnit ve zkratce: jednoduché unit testování (hierarchie testů) v balíku HUnit instalace: cabal update && cabal install hunit kompletní dokumentace na Hackage IB016: Cvičení 02 Jaro 2018 5 / 17 HUnit: základní syntax Operátory pro konstrukci testů: (~?) :: (...) => t -> String -> Test (~=?) :: (...) => a -> a -> Test (~?=) :: (...) => a -> a -> Test IB016: Cvičení 02 Jaro 2018 6 / 17 HUnit: základní syntax Operátory pro konstrukci testů: (~?) :: (...) => t -> String -> Test (~=?) :: (...) => a -> a -> Test (~?=) :: (...) => a -> a -> Test Hierarchie a pojmenovávání testů: data Test = TestCase Assertion | TestList [Test] | TestLabel String Test IB016: Cvičení 02 Jaro 2018 6 / 17 HUnit: základní syntax Operátory pro konstrukci testů: (~?) :: (...) => t -> String -> Test (~=?) :: (...) => a -> a -> Test (~?=) :: (...) => a -> a -> Test Hierarchie a pojmenovávání testů: data Test = TestCase Assertion | TestList [Test] | TestLabel String Test Spuštění testů: runTestTT :: Test -> IO Counts IB016: Cvičení 02 Jaro 2018 6 / 17 HUnit: ukázka module HUnitExample (fact, runTests) where import Test.HUnit fact :: Integer -> Integer fact n = product [1..n] runTests :: IO Counts runTests = runTestTT $ TestList [testSet1, testSet2] testSet1 :: Test testSet1 = TestLabel "Factorials" $ TestList [ fact 4 ~?= 25, fact 0 ~?= 1 ] testSet2 :: Test testSet2 = TestLabel "Numerical functions" $ TestList [ even 4 ~? "4 even?", odd 4 ~? "4 odd?" ] IB016: Cvičení 02 Jaro 2018 7 / 17 Typový konstruktor Either data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show) používá se, když může mít výpočet dva typy výsledků často se používá jako rozšíření Maybe Left a označuje chybný výpočet, hodnota specifikuje chybu Right b označuje korektní výpočet, hodnota je výsledkem IB016: Cvičení 02 Jaro 2018 8 / 17 Typový konstruktor Either data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show) používá se, když může mít výpočet dva typy výsledků často se používá jako rozšíření Maybe Left a označuje chybný výpočet, hodnota specifikuje chybu Right b označuje korektní výpočet, hodnota je výsledkem Jaké případy pro použití Either vám napadají? IB016: Cvičení 02 Jaro 2018 8 / 17 Druhy aneb typování typů všechny konkrétní typy jsou druhu * IB016: Cvičení 02 Jaro 2018 9 / 17 Druhy aneb typování typů všechny konkrétní typy jsou druhu * Integer :: * Maybe Int :: * Either String Int :: * BinTree (Int, [Int]) :: * typové konstruktory vyšší arity jsou vlastně „typové funkce“ IB016: Cvičení 02 Jaro 2018 9 / 17 Druhy aneb typování typů všechny konkrétní typy jsou druhu * Integer :: * Maybe Int :: * Either String Int :: * BinTree (Int, [Int]) :: * typové konstruktory vyšší arity jsou vlastně „typové funkce“ Maybe :: * -> * Either :: * -> * -> * [] :: * -> * (,) :: * -> * -> * opět platí princip částečné aplikace IB016: Cvičení 02 Jaro 2018 9 / 17 Druhy aneb typování typů všechny konkrétní typy jsou druhu * Integer :: * Maybe Int :: * Either String Int :: * BinTree (Int, [Int]) :: * typové konstruktory vyšší arity jsou vlastně „typové funkce“ Maybe :: * -> * Either :: * -> * -> * [] :: * -> * (,) :: * -> * -> * opět platí princip částečné aplikace Either String :: * -> * GHCi definuje povel :k na určení druhu IB016: Cvičení 02 Jaro 2018 9 / 17 Motivace: map Funkce map na seznamech: data [a] = [] | a : [a] map :: (a -> b) -> [a] -> [b] map _ [] = [] map f (x:xs) = fx : map f xs IB016: Cvičení 02 Jaro 2018 10 / 17 Motivace: map Funkce map na seznamech: data [a] = [] | a : [a] map :: (a -> b) -> [a] -> [b] map _ [] = [] map f (x:xs) = fx : map f xs Funkce map na binárních stromech: data BinTree a = BNode a (BinTree a) (BinTree a) | BEmpty treeMap :: (a -> b) -> BinTree a -> BinTree b treeMap _ BEmpty = BEmpty treeMap f (BNode v l r) = BNode (f v) (treeMap f l) (treeMap f r) IB016: Cvičení 02 Jaro 2018 10 / 17 Motivace: map Funkce map na seznamech: data [a] = [] | a : [a] map :: (a -> b) -> [a] -> [b] map _ [] = [] map f (x:xs) = fx : map f xs Funkce map na binárních stromech: data BinTree a = BNode a (BinTree a) (BinTree a) | BEmpty treeMap :: (a -> b) -> BinTree a -> BinTree b treeMap _ BEmpty = BEmpty treeMap f (BNode v l r) = BNode (f v) (treeMap f l) (treeMap f r) Nedalo by se to zobecnit? Jaký je obecný typ funkce map? IB016: Cvičení 02 Jaro 2018 10 / 17 Typová třída Functor class Functor f where fmap :: (a -> b) -> f a -> f b definováno v modulu Data.Functor pro typy, přes které se dá „mapovat“ třída pro „unární typové funkce“, tedy věci druhu * -> * instance pro [], BinTree, Maybe ne pro konkrétní typy ([String], BinTree a, Maybe Int) IB016: Cvičení 02 Jaro 2018 11 / 17 Typová třída Functor class Functor f where fmap :: (a -> b) -> f a -> f b definováno v modulu Data.Functor pro typy, přes které se dá „mapovat“ třída pro „unární typové funkce“, tedy věci druhu * -> * instance pro [], BinTree, Maybe ne pro konkrétní typy ([String], BinTree a, Maybe Int) jiný pohled: funktory tvoří kontext/kontejner pro typy (obalují je další strukturou) IB016: Cvičení 02 Jaro 2018 11 / 17 Instance třídy Functor I. instance Functor [] where IB016: Cvičení 02 Jaro 2018 12 / 17 Instance třídy Functor I. instance Functor [] where fmap :: (a -> b) -> [a] -> [b] fmap = map instance Functor BinTree where IB016: Cvičení 02 Jaro 2018 12 / 17 Instance třídy Functor I. instance Functor [] where fmap :: (a -> b) -> [a] -> [b] fmap = map instance Functor BinTree where fmap :: (a -> b) -> BinTree a -> BinTree b fmap = treeMap instance Functor Maybe where IB016: Cvičení 02 Jaro 2018 12 / 17 Instance třídy Functor I. instance Functor [] where fmap :: (a -> b) -> [a] -> [b] fmap = map instance Functor BinTree where fmap :: (a -> b) -> BinTree a -> BinTree b fmap = treeMap instance Functor Maybe where fmap :: (a -> b) -> Maybe a -> Maybe b fmap f (Just x) = Just (f x) fmap f Nothing = Nothing IB016: Cvičení 02 Jaro 2018 12 / 17 Instance třídy Functor II. instance Functor IO where IB016: Cvičení 02 Jaro 2018 13 / 17 Instance třídy Functor II. instance Functor IO where fmap :: (a -> b) -> IO a -> IO b fmap f action = do result <- action return (f result) IB016: Cvičení 02 Jaro 2018 13 / 17 Instance třídy Functor II. instance Functor IO where fmap :: (a -> b) -> IO a -> IO b fmap f action = do result <- action return (f result) Either je binární typový konstruktor, musíme tedy udělat instanci pro jeho částečnou aplikaci na jeden argument. Either e :: * -> * instance Functor (Either e) where IB016: Cvičení 02 Jaro 2018 13 / 17 Instance třídy Functor II. instance Functor IO where fmap :: (a -> b) -> IO a -> IO b fmap f action = do result <- action return (f result) Either je binární typový konstruktor, musíme tedy udělat instanci pro jeho částečnou aplikaci na jeden argument. Either e :: * -> * instance Functor (Either e) where fmap :: (a -> b) -> Either e a -> Either e b fmap f (Right x) = Right (f x) fmap f (Left x) = Left x IB016: Cvičení 02 Jaro 2018 13 / 17 Instance třídy Functor III. Funkce je vlastně použití binárního typového konstruktoru (->) (->) :: * -> * -> * IB016: Cvičení 02 Jaro 2018 14 / 17 Instance třídy Functor III. Funkce je vlastně použití binárního typového konstruktoru (->) (->) :: * -> * -> * Můžeme tedy zadefinovat instanci pro její částečnou aplikaci na jeden argument. (->) r :: * -> * (tedy „funkce z r“) IB016: Cvičení 02 Jaro 2018 14 / 17 Instance třídy Functor III. Funkce je vlastně použití binárního typového konstruktoru (->) (->) :: * -> * -> * Můžeme tedy zadefinovat instanci pro její částečnou aplikaci na jeden argument. (->) r :: * -> * (tedy „funkce z r“) instance Functor ((->) r) where IB016: Cvičení 02 Jaro 2018 14 / 17 Instance třídy Functor III. Funkce je vlastně použití binárního typového konstruktoru (->) (->) :: * -> * -> * Můžeme tedy zadefinovat instanci pro její částečnou aplikaci na jeden argument. (->) r :: * -> * (tedy „funkce z r“) instance Functor ((->) r) where fmap :: (a -> b) -> (r -> a) -> (r -> b) fmap f g = (\x -> f (g x)) IB016: Cvičení 02 Jaro 2018 14 / 17 Pravidla pro třídu Functor Pro instance třídy Functor by mělo platit: fmap id ≡ id fmap (f . g) ≡ fmap f . fmap g IB016: Cvičení 02 Jaro 2018 15 / 17 Pravidla pro třídu Functor Pro instance třídy Functor by mělo platit: fmap id ≡ id fmap (f . g) ≡ fmap f . fmap g Pravidla musí platit! kompilátor se spoléhá na výše uvedená pravidla jejich platnost musí ověřit programátor (!) pro všechny knihovní instance platí IB016: Cvičení 02 Jaro 2018 15 / 17 Úkol: instance třídy Functor Uvažme následující „nové“ datové typy pro mapy a dvojice: newtype MyMap k v = Mp { unMp :: Map k v } newtype Pair a b = Pr { unPr :: (a, b) } 1 Napište instanci pro třídu Functor pro tyto struktury. (Jak bude fungovat funkce map?) 2 Zamyslete se nad platností pravidel (!) třídy Functor. IB016: Cvičení 02 Jaro 2018 16 / 17 Úkol: Hlint, Hunit, Haddock 1 Nainstalujte si doplňková pravidla pro HLint a zkontrolujte nimi své řešení z minulé hodiny. 2 Do svého řešení z minulé hodiny doplňte pár testů využívajících knihovnu HUnit. 3 Do svého řešení z minulé hodiny doplňte správně formátované komentáře a vygenerujte k němu dokumentaci pomocí systému Haddock. IB016: Cvičení 02 Jaro 2018 17 / 17