1 Types and Typeclasses Syntax in Functions(Gaik Dawid, Marciniak Michał)
2 * Funkcja bez definicji typu * Funkcja z jawną definicją typu Typy i Klasy typów Typy w jęzku Haskell Rozpoznawanie typu Typy funkcji * Funkcja bez definicji typu * Funkcja z jawną definicją typu Typy całkowite i zmienne Klasy typów
3 Typy w języku haskell Wszystkie typy i wyrażenia znane są w języku Haskell już podczas kompilacji (mają zdefiniowany typ), co daje nam bardzo dużą zaletę - zwiększa bezpieczeństwo kodu, zabezpieczając nas przed ewentualnymi problemami w działaniu aplikacji. Nazwa wszystkich typów w języku Haskell zaczynają się od dużej litery np. Char, Bool. Aby sprawdzić dowolny typ wyrażenia, można użyć polecenia ,,:t " :
4 Rozpoznawanie typów Język Haskell pozwala na samoczynne wywnioskowanie typów zmiennych i argumentów funkcji, dlatego ich jawne podawanie jest zbędne. Jest to jedna z cech Haskella, ktora wyróżnia go na tle innych języków, pozwalając na większą swobodę w programowaniu.
5 Funkcja bez definicji typuWbudowana rozpoznawalność typów przez Haskella pozwala nam większą swobodę podczas programowania i nie wymaga od nas definiowania jawnego typu plików przy tworzeniu prostych funkcji. Funkcje bez definicji typu dobrze widać na przykładzie prostej funkcji, writeit x=x++"!" która ma za zadanie dopisanie znaku "!" do podanego wyrazu. Funkcja będzie posiadała typ [Char]->[Char] co oznacza mapowanie typu znakowego na typ znakowy. Użycie "[]" świadczy o tym, że mamy do czynienia z listą znaków - w tym wypadku listą znaków typu Char. Wynik naszej funkcji po sprawdzeniu typu Wynik funkcji Sprawdzenie typu
6 Funkcja Z JAWNĄ DEFINICJĄ TYPURozważmy teraz funkcje, której zadanie będzie takie samo jak w Funkcji bez definicji typu, czyli dopisanie znako "!" na koniec podanego wyrazu. Typ [Char] jes równoważny ze String`iem, który jest po prostu pojedyńczą listą znaków typu Char. writeit :: String -> String writeit x=x++ "!„ Definiując typ dla parametrów funkcji, używamy symbol "->". Typ zwracanej wartości znajduje się na końcu definiowanych typów. Wynik funkcji Sprawdzenie typu
7 Integer - typ całkowity (Nie posiada ograniczeń do 32 bitów), TYPY CAŁKOWITE Typy całkowite : Int - ograniczony typ całkowity (Liczba całkowita zaisywana na 32 bitach), Integer - typ całkowity (Nie posiada ograniczeń do 32 bitów), Float - typ z pojedyńczą precyzją Double - typ z podwójną precyzją, Inne typy: "Bool", "Char"
8 TYPY Zmienne W języku Haskell oprócz typów całkowitych, występują także typy zmienne, których konstrukcja nie narzuca z góry określonego typu dla parametru. Przykładem może być funkcja fst: która pobiera dwuelementową krotkę i zwraca element będący tym samym typem co pierwszy element tej krotki (Elementy a i b nie muszą być tego samego typu)
9 TYPY Zmienne Operatory ==,+,*,- i / oraz wiele innych to funkcje w Haskellu. Funkcja zawierająca jedynie znaki specjalne to tak zwana funkcja infiksowa. Aby sprawdzić na jakich typach pracuje, musimy przekazać ją do innej funkcji lub uczynić tę funkcję prefiksową (Nazwę funkcji zawrzeć w nawiasach). Część wyrażenia, występująca przed symbolem => nazywana jest klasą ograniczeń, Funkcje, które przyjmują typ zmienny nazywamy funkcjami polimorficznymi.
10 Klasy typów Klasy typów - Typeclasses to mechnizmy określające zachowanie poszczególnych typów związanych z daną klasą. Typ, będący Typeclass ma zaimplementowane pewne zachowania, które opisuje type class. Symbol => jest ograniczeniem klasy, nazywany jest class constraint Eq jeden z podstawowych typów klass w Haskellu. Używany w porównaniach
11 Klasy typów - eQ Typeclass Eq - udostępnia interfejs do testowania równości. Klasa dla typów używanych w porównaniach (==, /=). Standard języka Haskell wymaga aby wszystkie typy i funkcje (z wyjątkiem IO) należały do klasy Eq.
12 Klasy typów - ORD Typeclass Ord - dla typów używanych w porządkowaniu wartości (<, >, <=, >=). Funkcje porównujące pobierają dwa argumenty tego samego typu, które należą do klasy Ord i zwracają obiekt klasy Ordering tzn - EQ, GT lub LT (Greater than, Lesser than, Equal) Aby typ należał do klasy Ord musi należeć do klasy Eq
13 Klasy typów – SHOW i ReadTypeclass Show - dla typów, które mogą być prezentowane jako String. Najcześciej w klasie typu Show wykorzystywana jest funkcja show, która pobiera wartość i wyświetla ją jako tekst. Typeclass Read - funkcja odwrotna do Typeclass show. Pobiera tekst i zwraca typ należący do Read.
14 Klasy typów - ENUM Typeclass Enum - klasa obiektód do którego należą typy, które mogą być wliczalne. W tym typie mamy dostęp do funkcji pred (poprzednik) i succ (następnik). Do tej klasy należą typy ; (), Bool, Char, Ordering, Int, Integer, Float, Double
15 Klasy typów - BOUNDED Typeclass Bounded - typy mające wartości graniczne należą do tej klasy. Funkcja minBound zwraca minimalną wartość danego typu, a maxBound maksymalną wartość.
16 Pozostałe typy klas Typeclass Num - dla wszystkich typów numerycznych, Typeclass Integral - dla typów liczb całkowitych, Typeclass Floating - dla typów liczb zmiennoprzecinkowych,
17 Porównywanie wzorca (Pattern matching) Składanie w funkcji Porównywanie wzorca (Pattern matching) Strażnicy (instrukcje warunkowe) Instrukcja Where Instrukcja Let Wyrażenie Case
18 Porównywanie wzorca (Pattern matching)Haskel pozwala nam używać w składni funkcji, konkretnego wzorca, który jest porównywany z parametrami wejściowymi fukcji (matching) następnie na podstawie tego wzorca wykonywane są operacje. Składnia takiej funkcji pozwala na implementację różnych zachowań funkcji w zależności od wykorzystywanego wzorca.
19 Porównywanie wzorca (Pattern matching)Oto przykład użycia wzorca: sayMe :: (Integral a) => a -> String sayMe 1 = "One!" sayMe 2 = "Two!" sayMe 3 = "Three!" sayMe 4 = "Four!" sayMe 5 = "Five!" sayMe x = "Not between 1 and 5"
20 Porównywanie wzorca (Pattern matching)sayMe :: (Integral a) => a -> String * Na początku deklarujemy nazwę, * Następnie typ który funkcja ma przyjąć (Integral – typ liczb całkowitych) Na koniec zwracany typ, w tym przypadku będzie to String sayMe 1 = "One!" sayMe 2 = "Two!" Powyższe wzorce oznaczają, że dla konkretnego parametru wejściowego funkcja ma zwrócić przypisany parametr wyjściowy.
21 Porównywanie wzorca (Pattern matching)sayMe x = "Not between 1 and 5" Powyższe wyrażenie oznacza: dla dowolnego parametru wypisz "Not between 1 and 5„ a więc dla 1,2,3,4,5 również! Dla tego wzorzec ten został zaimplementowany dopiero na końcu składni, ma służyć jako zabezpieczenie na wypadek pozostałych parametrów nie uwzględnionych we wcześniejszych wzorcach (takie else ze znanych nam instrukcji warunkowych)
22 Porównywanie wzorca (Pattern matching)Należy również wspomnieć, iż weryfikacja wzorca następuje od góry do dołu a więc gdybyśmy poprzednią funkcję napisali w ten sposób: sayMe :: (Integral a) => a -> String sayMe x = "Not between 1 and 5" sayMe 1 = "One!" sayMe 2 = "Two!" …. Funkcja dla każdego parametru zwracałaby "Not between 1 and 5"
23 Porównywanie wzorca (Pattern matching)Rekurencja przy użyciu wzorca: silnia:: (Integral a) => a -> a silnia 0 = 1 silnia n = n * silnia(n - 1)
24 Porównywanie wzorca (Pattern matching)Przykład błędu gdy nie zakończymy składnii, wzorcem dla wszelkich pozostałych parametrów: charName :: Char -> String charName ’a’ = "Albert" charName ’b’ = "Broseph" ghci > charName ’h’ "*** Exception: tut.hs:(53,0)-(55,21): Non-exhaustive patterns in function charName Exception widoczny przy wywołaniu funkcji dla nie zabezpieczonego parametru ‚h’
25 Porównywanie wzorca (Pattern matching)Za pomocą wzorca napiszemy funkcję pobierające kolejne elementy: first :: (a, b, c) -> a first (x, _, _) = x second :: (a, b, c) -> b second (_, y, _) = y third :: (a, b, c) -> c third (_, _, z) = z
26 Porównywanie wzorca (Pattern matching)Implementacja znanej już nam funkcji head, która wypisuje pierwszy element listy: head ’ :: [a] -> a head ’ [] = error „podałeś pustą listę” head ’ (x:_) = x
27 Porównywanie wzorca (Pattern matching)tell :: (Show a) => [a] -> String tell [] = "The list is empty" tell (x:[]) = "The list has one element: " ++ show x tell (x:y:[]) = "The list has two elements: " ++ show x ++ " and " ++ show y tell (x:y:_) = "This list is long. The first two elements are: " ++ show x ++ " and " ++ show y
28 Porównywanie wzorca (Pattern matching)Suma elementów listy: capital :: String -> String capital "" = "Empty string , whoops!" capital = "The first letter of " ++ all ++ " is " ++ [x] ghci > capital "Dracula" "The first letter of Dracula is D"
29 Strażnicy (Guards) Strażnik jest w Haskell mechanizmem w sowim działaniu bardzo zbliżonym do warunku „if”. Wynikiem działania strażników jest wartość logiczna „True” lub „False”. Dodatkowo strażnicy bardzo dobrze współpracują z wcześniej opisanym mechanizmem porównywania wzorców.
30 Strażnicy (Guards) bmiTell :: (RealFloat a) => a -> String bmiTell bmi | bmi <= 18.5 = „jesteś zbyt chudy” | bmi <= 25.0 = „wszystko w normie" | bmi <= 30.0 = „pomyśl nad dietą" | otherwise = „powodzi się…” Deklarujemy wzorzec. Następnie deklarujemy instrukcje warunkową przy pomocy „|” oraz warunek a po znaku „=” deklarujemy parametr który ma zostać zwrócony. W celu zabezpieczenia funkcji dla pozostałych przypadków używamy „otherwise”
31 Strażnicy (Guards) bmiTell :: (RealFloat a) => a -> a -> String bmiTell weight height | weight / height ^ 2 <= 18.5 = „jesteś zbyt chudy” | weight / height ^ 2 <= 25.0 = „wszystko w normie" | weight / height ^ 2 <= 30.0 = „pomyśl nad dietą" | otherwise = „powodzi się…” Możemy tego również używać dla wielu parametrów wykonując operacje w deklaracji strażnika.
32 Strażnicy (Guards) max’ :: (Ord a) => a -> a -> a max’ a b | a > b = a | otherwise = b
33 Where Z poprzedniego przykładu można zauważyć, że cały czas powtarzany jest wzór na BMI a dokładniej„ weight / height ^ 2 „ W celu uniknięcia tego i używania raz zdefiniowanej wartości w całej składnii funkcji użyjemy where nazywane w także wiązaniem. Where na końcu funkcji łączy wyrażenie ze zmiennymi.(widoczne jest w całej funkcji).
34 Where bmiTell :: (RealFloat a) => a -> a -> String bmiTell weight height | bmi <= skinny = „jesteś zbyt chudy” | bmi <= normal = „wszystko w normie" | bmi <= fat = „pomyśl nad dietą" | otherwise = „powodzi się…” where bmi = weight / height ^ 2 skinny = 18.5 normal = 25.0 fat = 30.0 Warunek where umieszczamy zaraz po naszych strażnikach ( na końcu funkcji)
35 Let Wyrażenie Let jest w swoim działaniu bardzo podobne do wyrażenia Where i jest również nazywane wiązaniem. Z tą różnicą, że Let przypisuje wartość do podanych zmiennych lokalnie tzn. zmienna nie będzie widoczna w całej funkcji. Zasada działania Let < łączenia> in
36 LET ghci > [let square x = x * x in (square 5, square 3, square 2)] [(25,9,4)] ghci > (let a = 100; b = 200; c = 300 in a*b*c, let foo="Hey "; bar = "there!" in foo ++ bar) ( ,"Hey there!") Przy deklaracji wielu zmiennych oddzielamy je średnikiem. Instrukcji let możemy używać zamiast if: ghci > 4 * ( if 10 > 5 then 10 else 0) + 2 ghci > 4 * ( let a = 9 in a + 1) + 2
37 LET calcBmis :: (RealFloat a) => [(a, a)] -> [a] calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2] ghci > let zoot x y z = x * y + z ghci > zoot ghci > let boot x y z = x * y + z in boot ghci > boot
38 Wyrażenie Case Jak w każdym języku programowania w tym również dostajemy wyrażenie case Zasada działania case expression of pattern -> result pattern -> result ... Wyrażenie jest porównywane ze wzorem, a gdy pasuje podawany jest wynik. W przypadku gdy żaden wzór nie pasuje, dostaniemy exception
39 Wyrażenie Case describeList :: [a] -> String describeList xs = "The list is " ++ case xs of [] -> "empty." [x] -> "a singleton list." xs -> "a longer list.„ describeList xs = "The list is " ++ what xs where what [] = "empty." what [x] = "a singleton list." what xs = "a longer list."