Haskell/Typy danych
Typy danych w Haskellu
[edytuj]Typ jest zbiorem pewnych wartości. Na przykład typ Bool składa się z wartości True i False. Każda wartość (jak również funkcja) w Haskellu ma ściśle określony typ. Jawne definiowanie typów nie jest konieczne, ponieważ Haskell sam rozpoznaje typ wartości. Warto jednak jawnie określać typ wyrażenia, ponieważ bardzo ułatwia to analizę i dokumentację kodu oraz usuwanie błędów.
Najprostszym sposobem na poznanie typów jest użycie polecenia interpretera, dzięki któremu możemy sprawdzić typ dowolnego wyrażenia. Poleceniem tym jest :type, lub krócej :t.
Prelude> :t "Witaj inny swiecie!" "Witaj inny swiecie!" :: [Char]
Symbol :: może być odczytywany jako "jest typu". "Witaj inny swiecie!" jest więc listą znaków.
Stosując symbol :: możemy zapisać:
1 :: Integer 1.5 :: Double True :: Bool 'a' :: Char "Witaj" :: String "Witaj" :: [Char]
Dwie ostatnie linie przedstawiają ten sam łańcuch znaków. Na pierwszy rzut oka może się wydawać, że zostały zdefiniowane jako zmienne innych typów. Jest to jeden z przykładów wykorzystania mechanizmu synonimów typów dostępnego w Haskellu. String jest synonimem [Char] i oznacza tutaj dokładnie to samo co [Char]. Mechanizm synonimów typów zostanie omówiony w dalszej części rozdziału.
Rodzaje typów
- liczbowe ( class Num )[1]
- podstawowe ( ang. standard or primitive numeric types )
- Int[2]
- Integer
- Float
- Double
- pochodne ( ang. Constructed types)
- complex
- ratio
- podstawowe ( ang. standard or primitive numeric types )
- algebraiczne (ang. Algebraic data type = ADT )[3]
Podział wg implementacji:
- abstrakcyjne [4]
- polimorficzne
- monomorficzne : Integer, Float
- konkretne ( ang. Concrete data type )
Typy podstawowe
[edytuj]Typy całkowite
[edytuj]W Haskellu dostępne są dwa typy całkowite:
- Int (fixed-precision) - liczby całkowite z zakresu [-2^29 .. 2^29-1],
- Integer (arbitrary-precision) - wartością Integer może być dowolna liczba całkowita (zarówno ujemna jak i dodatnia).
Typy rzeczywiste
[edytuj]Dostępne są dwa typy liczb rzeczywistych:
- Float - liczba zmiennoprzecinkowa pojedynczej precyzji,
- Double - liczba zmiennoprzecinkowa podwójnej precyzji.
Typ znakowy
[edytuj]Typ pojedynczego znaku to char. Jest to typ wyliczeniowy, którego wartości reprezentują znaki Unicode.
Podobnie jak w wielu innych językach (np. C++), również w Haskellu pojedyncze znaki (Char) są w apostrofach, natomiast łańcuchy znaków (String) w cudzysłowach. |
Bool - zmienne logiczne
[edytuj]Podobnie jak w innych językach, do reprezentowania zmiennych logicznych służy typ Bool. Jest to typ wyliczeniowy zawierający dwie wartości False (fałsz - 0) i True (prawda - 1).
Typ relacji między elementami
[edytuj]Ordering, typ relacji, w większości języków programowania nie jest obecny. Jest to typ wyliczeniowy posiadający trzy wartości:
- LT (less than - mniejszy niż)
- EQ (equal - równy)
- GT (greater than - większy niż)
Wartości tego typu są zwracane między innymi przez funkcję compare porównującą dwa elementy.
Prelude> compare 1 2 LT
Typy strukturalne
[edytuj]Typy strukturalne to listy i krotki. Listy to struktury homogeniczne, natomiast krotki heterogeniczne. Rozmiar listy nie jest określony - można dołączać do niej kolejne elementy. Rozmiar krotki jest ściśle określony podczas jej tworzenia. Nie jest możliwe dołączanie elementów do istniejącej krotki.
Przykład listy:
Prelude> :t ['a','b','c'] ['a','b','c'] :: [Char]
i krotki:
Prelude> :t (True,"Haskell") (True,"Haskell") :: (Bool,[Char])
Typy funkcji
[edytuj]Nie tylko zmienne, ale również funkcje mają w Haskellu swój typ. Na typ funkcji składają się typy przyjmowanych przez nią parametrów oraz typ wartości zwracanej przez funkcję. Typy te podajemy w następujący sposób:
nazwa_funkcji :: TypParam_1 -> TypParam_2 -> ... -> TypParam_n -> TypWartosciZwracanej
na przykład definicje funkcji inc zwiększającej wartość liczby Int o jeden, oraz funkcji add dodającej dwie liczby Double wyglądają następująco:
inc :: Int -> Int add :: Double -> Double -> Double
Synonimy typów
[edytuj]Synonimy typów umożliwiają nadanie własnej nazwy dla dowolnego typu. Wykorzystanie synonimów typów jest możliwe tylko w zewnętrznym pliku.
Poniżej utworzony został typ przechowujący dwie współrzędne punktu (x,y) oraz funkcja obliczająca odległość między tymi punktami.
type Punkt = (Double, Double) odleglosc :: Punkt -> Punkt -> Double odleglosc (x1,y1) (x2,y2) = sqrt ( (x1-x2)^2 + (y1-y2)^2 )
Przykładem zastosowania synonimów typów jest String, czyli synonim tablicy znaków [Char].
Typy polimorficzne
[edytuj]Typ polimorficzny oznacza rodzinę typów. Na przykład, [a] jest rodziną typów zawierającą listy dowolnych typów. Jeśli utworzymy listę zawierającą wartości dowolnego typu, lista ta będzie należała do rodziny [a]. Lista wartości Int (np. [1,2,3]), lista znaków (np. ['a','b','c']), jak również lista list (np. [[1,1],[1,2]]) czy lista krotek (np. [(1,'a'),(2,'b')]) należą do rodziny [a].
Identyfikatory takie jak zastosowana powyżej litera a są nazywane zmiennymi wartości i w przeciwieństwie do nazw typów, są pisane z małej litery.
Listy, bardzo często używane w programowaniu funkcyjnym, są doskonałym przykładem do zademonstrowania polimorfizmu. Aby wyznaczyć liczbę elementów w liście możemy zastosować funkcję length, na przykład:
Prelude> length [1,2,3] 3
Sprawdźmy teraz jaki jest typ tej funkcji:
Prelude> :t length length :: [a] -> Int
Jak widać funkcja ta przyjmuje listę dowolnego typu i zwraca liczbę całkowitą. Dzięki polimorfizmowi funkcja length może zostać zastosowana dla dowolnej tablicy ([Int], [Char], [ [Int] ] itp). Nie potrzeba definiować osobnych funkcji dla tablic różnych typów.
Inną funkcją polimorficzną jest funkcja head zwracająca pierwszy element listy.
Prelude> head [1,2,3] 1 Prelude> :t head head :: [a] -> a
Jak widać funkcja head przyjmuje listę elementów dowolnego typu i zwraca element tego samego typu.
Zobacz również
[edytuj]- Typy definiowane przez użytkownika
- klasy typów