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
  • 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.

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
 
Klasy typów w Haskellu
  1. haskell.org tutoria : numbers
  2. stackoverflow question : haskell-int-and-integer
  3. haskell.org: Algebraic_data_type
  4. haskell.org Abstract_data_type