To pytanie nie jest subiektywne. Bardzo konkretny czasownik jest używany w książce, do której się odwołuje, i chciałbym zrozumieć, jakie są implikacje tego sformułowania, ponieważ obawiam się, że coś źle zrozumiałem.
W Learn You a Haskell poniższy akapit jest trzecim i ostatnim, który zawiera „zakładamy *
”.
data Barry t k p = Barry { yabba :: p, dabba :: t k }
A teraz chcemy zrobić z tego przykład
Functor
.Functor
chce rodzajów,* -> *
aleBarry
nie wygląda na to, że ma tego rodzaju. Jakiego rodzajuBarry
? Widzimy, że wymaga trzech parametrów typu, więc tak będziesomething -> something -> something -> *
. Można śmiało powiedzieć, żep
jest to konkretny typ, a zatem ma swego rodzaju*
. Dlak
zakładamy,*
a więc co za tym idzie,t
ma niby* -> *
. Teraz po prostu zamieńmy te rodzaje na tesomething
, których używaliśmy jako symbole zastępcze i widzimy, że ma coś w rodzaju(* -> *) -> * -> * -> *
.
Dlaczego w ogóle coś zakładamy? Po przeczytaniu „zakładamy, że X (tzn. Zakładamy, że X jest prawdą)” jest dla mnie naturalne, że powinniśmy również rozważyć przypadek, w którym X jest fałszywy. Czy w konkretnym przypadku tego przykładu nie może t
być miły (* -> *) -> *
i k
miły (* -> *)
? Jeśli tak było, cokolwiek t
i k
faktycznie tak było,t k
nadal będzie to rodzaj betonu, nie?
Widzę, że cała linia rozumowania jest następnie porównywana z kompilatorem, ale nie sądzę, że kompilator zakłada . Jeśli tak, chciałbym wiedzieć, a jeśli nie, to obawiam się, że nie rozumiem znaczenia akapitu.
k :: L
na każdy rodzajL
, o ilet :: L -> *
. Kompilator tutaj musi jednak wybrać jakiś konkretnyL
lub uciekać się do polikinda. Polykind byłby najbardziej ogólną opcją, ale tutaj GHC wybieraL = *
(podstawowy Haskell nie ma polikindów, należy je włączyć jako rozszerzenie). Ponieważ wybiera coś, co jest raczej arbitralne, LYAH używa słowa „zakładać” (AFAICT).Odpowiedzi:
W rzeczywistości kompilator zakłada! Ale możesz tego poprosić o rozszerzenie PolyKinds. Możesz przeczytać o tym bardziej szczegółowo tutaj . Po włączeniu tego rozszerzenia taki
Barry
będzieforall k. (k -> *) -> k -> * -> *
.źródło
Słuszna uwaga. Autor dokonuje niepotrzebnego założenia. Być może po to, aby ułatwić zrozumienie w jego rozdziale Type Foo, ale ludzie tacy jak ty mogą słusznie to kwestionować.
Obaj
t
,k
ip
są zmiennymi typu. Jak widzimyyabba :: p
, może żyć sam, więc jest jak stała funkcja, tak jakby to była wartość zamiast typu, jego podpis typu powiedziałbyInt
lubChar
, cokolwiek ... jak to nazwiesz. Ale ponieważ jest to typ, jest to miły podpis*
.Jednak
t
tutaj typ pobiera zmienną typu,k
aby skonstruować typ (dabba :: t k
),więc jesteśmy pewni, że (tutaj nie ma tutaj żadnego przypuszczenia).t
ma taką podobną sygnaturę jak* -> *
ik
ma*
Kiedy się o tym dowiemy ...
Barry t k p
miły podpis(* -> *) -> * -> * -> *
tego typu oznacza, że trzeba go odt
czasuk
do czasup
podać i podaćBarry
.Edytuj Pamiętaj, aby przeczytać komentarz @ luqui poniżej.
źródło
k
nie jest*
tak, jak się twierdzi, podczas dedukcjit
. Moglibyśmyk :: * -> *
it :: (* -> *) -> *
na przykład. Dodaj poledoo :: k Int
do rekordu, a przejdzie ono bez problemu.