To jest stara wersja strony!
Monady i Typy
Wprowadzenie
W Haskellu mamy do czynienia z typowaniem statycznym. Oznacza to, ze już podczas kompilacji typ każdego z wyrażeń jest znany.
W odróżnieniu jednka od standardowych języków programowania (np. Java), Haskell posiada mechanizm inferencji typów, co pozwala kompilatorowi wywnioskować typy wyrażeń, np:
ghci> :t 'a'
'a' :: Char
ghci> :t True
True :: Bool
ghci> :t "HELLO!"
"HELLO!" :: [Char]
ghci> :t (True, 'a')
(True, 'a') :: (Bool, Char)
ghci> :t 4 == 5
4 == 5 :: Bool
Podobnie w przypadku funkcji, chociaż tutaj zaleca sie stosowanie deklaracji explicite, chyba że tworzymy bardzo proste funkcjie, np:
mycomp a b = a < b
ghci> :t mycomp
ghci> Ord a => a -> a -> Bool
Widzimy tutaj ciekaą rzecz. Sygnatura funkcji została poprawnie rozwinięta, ale Nie mamy tutaj żadnego konkretnego typu, znanego z innych językóœ progrmowania (Int, Float, Double). Zamiast tego mamy `a → a → Bool`. Jedynie typ wynikowy wygląda znajomo.
Po lewej stronie tego wyrażenia, tuż przed `=⇒` pojawia się tzw. Ograniczenie klasy `Ord a` wymuszające, że typ wejścia dla tej funkcji musi implementować interfejs `Ord`. Do poczytanie o ograniczeniach było na Lab2 (zobacz: ).
Pozwala to na pewnego rodzaju metaprogramowanie na poziomie typów.
Definiowanie własnych type-classes
Możemy oczywiście definiować własne interfejsty dla typów, czyli type-classy i dziedziczyć istniejące.
Co więcej, mozna je aplikować do istniejących typów.
class Listable a where
toList :: a -> [Int]
instance Listable Int where
toList x = [x]
instance Listable Bool where
toList True = [1]
toList False = [0]
Definiowanie własnych typów
W haskelu robimy to za pomoca słowa kluczowego data.
Na przykład, aby zdefiniować typ opisujący punkt w przesrtzeniu 2D:
data NamedPoint = NamedPoint
{ pointName :: String
, pointX :: Int
, pointY :: Int
}
Z takim type niewiele da sie zrobić. Nie da sie go nawet wyświetlić, ponieważ aby być wyświetlanym typ musi implementować interfejs Show, czyli dziedziczyć go.
Na szczęscie możliwe jest dziedziczenie typów.
data NamedPoint = NamedPoint
{ pointName :: String
, pointX :: Int
, pointY :: Int
} deriving (Show)
Monady
Monady w Haskellu to konstruktory polimorficznych typów.
Typy takie mogą przechowywaćć wartości wielu różnych typów.
Najczęsciej używana monadą jest Maybe, definiowana jako
data Maybe a = Nothing | Just a
Pozwala tworzyć pewnego rodzaju wrapper wokół typu, któ©y w zależnośći od tego jak pójdą obliczenia, wyrzuci ten typ, albo Nothing, ale nadal interpretowany jako Maybe.
maybe_sqrt :: Float -> Maybe Float
maybe_sqrt x
| x >= 0 = Just (sqrt x)
| otherwise = Nothing
Zadania
Wykorzystując wiedzę o tym jak tworzyć typeclassy, stwórz jedną o nazwie Intable, która pozwoli na konwersję [Char] to Int poprzez funkcję toInt:
mySuperAdd (Intable a) ==> a->a->Int
mySuperAdd x y = toInt x + toInt y
mySuperAdd "123" + "12"
mySuperAdd "123" + (12::Int)
Zbuduj nowy typ Osoba, zawierający imię, nazwisko, pesel. Napisz typeclase umożliwiającą porównywanie osób (po peselu) i sortowanie po nazwisku. Czy da się wyświetlić osobę? Co gdyby dziedziczyć po Eq i Ord? Jak zachowywałoby się porównywanie `==,⇐` ?Poniższe powinny działać poprawnie:
let szymon = Osoba "Szymon" "Bobek" "12345678901"
let bobek = Osoba "S" "Bobek" "12345678901"
let zenon = Osoba Zenon Adamczyk "111111111"
ghci> szymon == bobek
True
ghci> szymon > zenon
True
Zaimplementuj drzewo binarne umożliwiające przechowywanie dowolnych typów, tak aby dało sie stworzyć je w następujący sposób:
myTree :: Tree Int
myTree = Node 1 (Node 2 Empty (Node 3 Empty Empty)) (Node 4 Empty Empty)
insert (wstawienie elementu)
empty (sprawdzanie czy drzewo jest puste)
isBinary (sprawdzenie czy drzewo jest drzewem binarnym)
search (sprawdzanie czy element jest w drzewie)
toString (wypisującą drzewo w postaci „a(b(d,e),c(,f(g,)))” )
leaves ( zwracającą listę liści )
nnodes (podającą ilość węzłów)
nsum (zliczającą sumę wartości w węzłach)
remove (usuwanie elementu)*