Spróbuj trochę przeczytać o typie konstruktora rodzaj.
Jeśli masz typ polimorficzny w zależności od niektórych zmiennych typu, to twój konstruktor typu musi mieć rodzaj, który to odzwierciedla.
Na przykład, konstruktor typu MyBool
określone w:
data MyBool = False | True
jest rodzaju *
. Oznacza to, że mój konstruktor typu MyBool
nie przyjmuje parametrów definiujących typ. Jeśli piszę coś takiego:
data MyMaybe a = Just a | Nothing
następnie konstruktor typu MyMaybe
mieć miłą *->*
, to znaczy, że potrzebuje „typ argumentu”, aby określić typ.
Można porównać sposób działania typów konstruktorów z typem konstruktora danych.
Konstruktor danych True
może być wartością danych typu MyBool
na własną rękę, bez żadnego parametru. Ale konstruktor dane Just
jest wartością typu a -> MyMaybe a
będzie działać na wartości typu A, aby utworzyć kolejną wartość typu MyMaybe a
- jak na przykład w tym ghci sesji:
> let x = Just 5
> :t x
Maybe Int
> let y = Just
> :t y
a -> Maybe a
Jest to bardziej lub mniej porównywalne z różnicą między konstruktorami typu MyMaybe
i MyBool
. Biorąc pod uwagę, że MyBool
ma rodzaj *
, możesz mieć wartości o typie MyBool
, bez żadnego dodatkowego parametru typu. Ale MyMaybe
nie jest typem sam w sobie - jest konstruktorem typu, który "działa" na typ, aby utworzyć inny typ, czyli jego rodzaj to * -> *
. I tak, że nie można mieć rzeczy typu MyMaybe
, ale rzeczy typu MyMaybe Int
, MyMaybe Bool
, MyMaybe [Int]
, itp ...
Jeżeli typ jest polimorficzny, musi wynosić co najmniej od rodzaju * -> *
, ale może być *->*->*
, jak w:
data MyPair a b = Pair a b
MyPair
potrzebuje dwa parametry typu, aby określić typ, podobnie jak w MyPair Int Bool
, MyPair Int Int
, itp ...
Komunikat take home jest podobny do: konstruktory wartości mają sygnatury typów, konstruktory typów mają własne sygnatury i należy to wziąć pod uwagę przy planowaniu nowego typu danych.
http://en.wikipedia.org/wiki/Kind_%28type_theory%29