Jestem nowy w teorii języków programowania. Oglądałem kilka wykładów online, w których instruktor twierdził, że funkcją typu polimorficznego forall t: Type, t->t
jest tożsamość, ale nie wyjaśniłem dlaczego. Czy ktoś może mi wyjaśnić, dlaczego? Może dowód roszczenia z pierwszych zasad.
programming-languages
type-theory
abhishek
źródło
źródło
Odpowiedzi:
Pierwszą rzeczą do odnotowania jest to, że niekoniecznie jest to prawda. Na przykład, w zależności od języka, funkcja tego typu, oprócz tego, że jest funkcją tożsamości, może: 1) zapętlać na zawsze, 2) mutować jakiś stan, 3) zwracać
null
, 4) zgłaszać wyjątek, 5) wykonywać pewne operacje we / wy, 6) rozwiąż wątek, aby zrobić coś innego, 7) zróbcall/cc
shenanigany, 8) użyj czegoś takiego jak JavaObject.hashCode
, 9) użyj odbicia, aby określić, czy typ jest liczbą całkowitą i zwiększ go, jeśli tak, 10) użyj odbicia, aby przeanalizować stos wywołań i zrobić coś w oparciu o kontekst, w którym się nazywa, 11) prawdopodobnie wiele innych rzeczy, a na pewno dowolne kombinacje powyższych.Zatem właściwość, która do tego prowadzi, parametryczność, jest właściwością języka jako całości i istnieją jego silniejsze i słabsze odmiany. W przypadku wielu formalnych rachunku różniczkowego badanych w teorii typów nie może wystąpić żadne z powyższych zachowań. Na przykład dla Systemu F / czystego polimorficznego rachunku lambda, w którym najpierw badano parametryczność, żadne z powyższych zachowań nie może wystąpić. Po prostu nie ma wyjątków, stan zmienny,
null
,call/cc
, I / O, odbicie, i to mocno normalizacji więc nie może pętla zawsze. Jak wspomniał Gilles w komentarzu, artykuł Twierdzenia za darmo!Phila Wadlera jest dobrym wstępem do tego tematu, a jego odniesienia przejdą dalej do teorii, a zwłaszcza techniki relacji logicznych. Ten link zawiera również kilka innych artykułów Wadlera na temat parametryczności.Ponieważ parametryczność jest właściwością języka, udowodnienie, że wymaga ona formalnego sformułowania języka, a następnie stosunkowo skomplikowanego argumentu. Nieformalny argument w tym konkretnym przypadku, zakładając, że jesteśmy w polimorficznym rachunku lambda, jest taki, że ponieważ nic nie wiemy o tym,
t
nie możemy wykonać żadnych operacji na danych wejściowych (np. Nie możemy go zwiększyć, ponieważ nie wiemy, czy jest to liczba) lub utwórz wartość tego typu (dla wszystkich wiemyt
=Void
typ bez żadnych wartości). Jedynym sposobem na wygenerowanie wartości typut
jest zwrócenie tej, która została nam przekazana. Żadne inne zachowania nie są możliwe. Jednym ze sposobów, aby to zobaczyć, jest zastosowanie silnej normalizacji i wykazanie, że istnieje tylko jeden normalny termin tego typu.źródło
Dowód roszczenia jest dość złożony, ale jeśli tego naprawdę chcesz, możesz sprawdzić oryginalny artykuł Reynoldsa na ten temat.
Kluczową ideą jest to, że odnosi się do parametrycznie polimorficznych funkcji, gdzie ciało funkcji polimorficznej jest takie samo dla wszystkich monomorficznych instancji funkcji. W takim systemie nie można przyjąć żadnych założeń dotyczących typu parametru typu polimorficznego, a jeśli jedyna wartość w zakresie ma typ ogólny, nie ma z tym nic wspólnego, ale zwraca go lub przekazuje innym funkcjom Zdefiniowaliśmy, że z kolei nie można nic zrobić, tylko zwrócić go lub przekazać .. .etc. Ostatecznie wszystko, co możesz zrobić, to łańcuch funkcji tożsamości przed zwróceniem parametru.
źródło
Biorąc pod uwagę wszystkie zastrzeżenia, o których wspomina Derek, i ignorując paradoksy wynikające z zastosowania teorii mnogości, pozwól mi naszkicować dowód w duchu Reynoldsa / Wadlera.
Funkcja typu:
jest rodziną funkcji indeksowanych według typu t .ft t
Chodzi o to, że aby formalnie zdefiniować funkcje polimorficzne, nie powinniśmy traktować typów jako zbiorów wartości, ale raczej jako relacje. Podstawowe typy, takie jak
Int
indukują relacje równości - np. DwieInt
wartości są powiązane, jeśli są równe. Funkcje są powiązane, jeśli odwzorowują powiązane wartości na powiązane wartości. Ciekawym przypadkiem są funkcje polimorficzne. Mapują typy pokrewne na powiązane wartości.W naszym przypadku, chcemy nawiązać relację między dwie funkcje polimorficzne i g typu:f g
f
s
t
()
()
t
()
t
((), c)
c
t
()
()
c
c
()
c
t
f
id
Więcej szczegółów znajdziesz na moim blogu .
źródło
EDYCJA: Powyższy komentarz podał brakujący element. Niektórzy ludzie celowo bawią się w mniej niż kompletne języki. Wyraźnie nie dbam o takie języki. Naprawdę łatwy do opanowania język nie jest szalony i trudny do zaprojektowania. Cała reszta omawia to, co się dzieje, próbując zastosować te twierdzenia do pełnego języka.
Fałszywy!
gdzie
is
operator porównuje dwie zmienne dla tożsamości referencyjnej. Oznacza to, że zawierają tę samą wartość. Nie jest to wartość równoważna, ta sama wartość. Funkcjef
ig
są z definicji równoważne, ale nie są takie same.Jeśli ta funkcja zostanie przekazana sama, zwraca coś innego; w przeciwnym razie zwraca dane wejściowe. Coś innego ma ten sam typ, co sam, dlatego można je zastąpić. Innymi słowy,
f
tożsamość nie jest, ponieważf(f)
zwracag
, podczas gdy tożsamość powracaf
.Aby twierdzenie się utrzymało, musi ono przyjąć absurdalną zdolność redukcji
Jeśli zechcesz założyć, że możesz założyć, że łatwiejsze jest wnioskowanie o typie.
Jeśli spróbujemy ograniczyć domenę, dopóki twierdzenie się nie utrzyma, ostatecznie musimy ją okropnie ograniczyć.
raise
i nieexit
. Teraz zaczynamy się ograniczać.nil
. To zaczyna być problematyczne. Skończyły się nam sposoby radzenia sobie z 1 / 0.³Istnienie obu ostatnich dwóch ograniczeń okaleczyło język. Mimo że Turing jest kompletnym jedynym sposobem na uzyskanie z niego ogólnego celu, jest symulacja wewnętrznej platformy, która interpretuje język o luźniejszych wymaganiach.
¹ Jeśli uważasz, że kompilator może to wywnioskować, wypróbuj ten
² Dowód, że kompilator nie może tego zrobić, zależy od oślepienia. Możemy użyć wielu bibliotek, aby kompilator nie widział pętli jednocześnie. Ponadto zawsze możemy zbudować coś, co program działałby, ale nie można go skompilować, ponieważ kompilator nie może wykonać indukcji dostępnej pamięci.
³ Ktoś myśli, że możesz mieć ten zerowy zero bez arbitralnych typów ogólnych zwracających zero. To płaci okropną karę, za którą nie widziałem skutecznego języka, który mógłby ją zapłacić.
nie można kompilować. Podstawowym problemem jest to, że indeksowanie tablicy wykonawczej już nie działa.
źródło
foil
w ogóle jest kwantyfikator?) To wcale nie jest pomocne.