I przysięgam, że kiedyś T-shirt na sprzedaż gościnnie nieśmiertelne słowa:
W jakiej części
ty nie rozumiesz?
W moim przypadku odpowiedź brzmiałaby ... wszystko!
W szczególności często widzę taką notację w dokumentach Haskella, ale nie mam pojęcia, co to wszystko znaczy. Nie mam pojęcia, jaką to powinna być gałąź matematyki.
Rozpoznaję oczywiście litery greckiego alfabetu i symbole takie jak „∉” (co zwykle oznacza, że coś nie jest elementem zestawu).
Z drugiej strony nigdy wcześniej nie widziałem „⊢” ( Wikipedia twierdzi, że może to oznaczać „partycję” ). Nie jestem również zaznajomiony z użyciem winculum tutaj. (Zwykle oznacza ułamek, ale nie wydaje się , że tak jest w tym przypadku.)
Gdyby ktoś mógł mi przynajmniej powiedzieć, od czego zacząć szukać zrozumienia tego morza symboli, byłoby to pomocne.
źródło
Odpowiedzi:
:
oznacza ma typ∈
oznacza jest w . (Podobnie∉
oznacza „nie ma”).Γ
jest zwykle używany w odniesieniu do środowiska lub kontekstu; w tym przypadku można go traktować jako zestaw adnotacji typu, łącząc identyfikator z jego typem. Oznaczax : σ ∈ Γ
to, że środowiskoΓ
obejmuje fakt, żex
ma typσ
.⊢
można odczytać jako dowody lub ustalenia.Γ ⊢ x : σ
oznacza, że środowiskoΓ
określax
typσ
.,
to sposób uwzględnienia konkretnych dodatkowych założeń w środowiskuΓ
.Oznacza
Γ, x : τ ⊢ e : τ'
to, że środowiskoΓ
, przy dodatkowym, nadrzędnym założeniu, żex
ma typτ
, udowadnia, żee
ma typτ'
.Zgodnie z życzeniem: pierwszeństwo operatora, od najwyższej do najniższej:
λ x . e
,∀ α . σ
iτ → τ'
,let x = e0 in e1
i spacji do stosowania funkcji.:
∈
i∉
,
(lewostronny)⊢
źródło
:
i∈
są bardzo podobne, co oznacza, że jedna rzecz jest zawarta w innej rzeczy - zestaw zawiera elementy, a typ zawiera wartości, w pewnym sensie. Zasadniczą różnicą jest to, żex ∈ S
oznacza, że zestawS
dosłownie zawiera elementx
, natomiastΓ ⊢ x : T
środki, którex
mogą być wyprowadzona do zamieszkują typuT
w kontekścieΓ
. Biorąc to pod uwagę, reguła Var brzmi: »Jeśli x jest dosłownie zawarte w kontekście, można (trywialnie) z niego wywnioskować«.(Γ,(x:τ))⊢(x:σ)
, patrz overleaf.com/read/ddmnkzjtnqbd#/61990222Ta składnia, choć może wydawać się skomplikowana, jest w rzeczywistości dość prosta. Podstawowa idea pochodzi z logiki formalnej: całe wyrażenie jest implikacją, a górna połowa to założenia, a dolna połowa to wynik. Oznacza to, że jeśli wiesz, że górne wyrażenia są prawdziwe, możesz stwierdzić, że dolne wyrażenia również są prawdziwe.
Symbolika
Należy także pamiętać, że niektóre litery mają tradycyjne znaczenie; w szczególności Γ oznacza „kontekst”, w którym się znajdujesz - to znaczy, jakie są rodzaje innych rzeczy, które widziałeś. Więc coś w stylu
Γ ⊢ ...
oznacza „wyrażenie,...
gdy znasz typy każdego wyrażenia wΓ
.Ten
⊢
symbol oznacza, że możesz coś udowodnić. PodobnieΓ ⊢ ...
jest ze stwierdzeniem: „Mogę udowodnić...
w kontekścieΓ
. Te stwierdzenia nazywane są również osądami typu.Kolejna rzecz, o której należy pamiętać: w matematyce, podobnie jak ML i Scala,
x : σ
oznacza, żex
ma typσ
. Możesz przeczytać tak jak u Haskellax :: σ
.Co oznacza każda reguła
Tak więc, wiedząc o tym, pierwszy wyraz staje się łatwe do zrozumienia: jeśli wiemy, że
x : σ ∈ Γ
(to jest,x
ma jakiś typσ
w pewnym kontekścieΓ
), to wiemy, żeΓ ⊢ x : σ
(to jest wΓ
,x
ma typσ
). Tak naprawdę, to nie mówi ci niczego super interesującego; po prostu mówi ci, jak korzystać z kontekstu.Inne zasady są również proste. Na przykład weź
[App]
. Ta reguła ma dwa warunki:e₀
jest funkcją od pewnego typuτ
do jakiegoś typuτ'
ie₁
jest wartością typuτ
. Teraz wiesz, co wpisać dostaniesz stosujące₀
sięe₁
! Mam nadzieję, że to nie jest niespodzianka :).Następna reguła ma jeszcze nową składnię. W szczególności
Γ, x : τ
oznacza tylko kontekstΓ
i osądx : τ
. Tak więc, jeśli wiemy, że zmiennax
ma typ,τ
a wyrażeniee
ma typτ'
, znamy również typ funkcji, która przyjmujex
i zwracae
. To po prostu mówi nam, co zrobić, jeśli ustaliliśmy, jaki typ funkcja przyjmuje i jaki typ zwraca, więc nie powinno to być zaskakujące.Następny mówi po prostu, jak obsługiwać
let
instrukcje. Jeśli wiesz, że jakieś wyrażeniee₁
ma typτ
tak długo, jakx
ma typσ
, wówczaslet
wyrażenie, które lokalnie wiążex
się z wartością typu,σ
sprawi, że będziee₁
miało typτ
. Naprawdę, to po prostu mówi ci, że instrukcja let pozwala zasadniczo rozszerzyć kontekst o nowe powiązanie - co dokładnielet
działa![Inst]
Zasada dotyczy sub-pisania. Mówi, że jeśli masz wartość typuσ'
i jest ona podtypemσ
(⊑
reprezentuje relację częściowego uporządkowania), to wyrażenie jest również typuσ
.Ostatnia zasada dotyczy typów uogólnionych. Krótko na bok: wolna zmienna jest zmienną, która nie jest wprowadzana przez wyrażenie let ani lambda wewnątrz jakiegoś wyrażenia; Wyrażenie to zależy teraz od wartości zmiennej wolnej od kontekstu. Reguła mówi, że jeśli istnieje jakaś zmienna,
α
która nie jest „wolna” w jakimkolwiek kontekście, to można powiedzieć, że każde wyrażenie, którego typ znasze : σ
będzie miał ten typ dla dowolnej wartościα
.Jak korzystać z reguł
Teraz, kiedy rozumiesz symbole, co robisz z tymi zasadami? Cóż, możesz użyć tych reguł, aby dowiedzieć się, jakie są różne wartości. Aby to zrobić, spójrz na swoje wyrażenie (powiedzmy
f x y
) i znajdź regułę, która ma konkluzję (dolna część) pasującą do twojego wyrażenia. Nazwijmy rzecz, którą próbujesz znaleźć, „celem”. W takim przypadku spójrz na regułę, która kończy się nae₀ e₁
. Kiedy to znajdziesz, musisz znaleźć reguły potwierdzające wszystko powyżej linii tej reguły. Te rzeczy zasadniczo odpowiadają typom podwyrażeń, więc zasadniczo rekurencyjnie powtarzasz niektóre części wyrażenia. Po prostu rób to, dopóki nie skończysz drzewa dowodu, co daje dowód rodzaju wyrażenia.Tak więc wszystkie te reguły określają dokładnie - i w zwykłym matematycznie pedantycznym szczególe: P - jak rozróżnić rodzaje wyrażeń.
Teraz powinno to brzmieć znajomo, jeśli kiedykolwiek używałeś Prologa - w zasadzie obliczasz drzewo dowodu jak ludzki tłumacz Prolog. Istnieje powód, dla którego Prolog nazywa się „programowaniem logicznym”! Jest to również ważne, ponieważ pierwszy sposób, w jaki zapoznałem się z algorytmem wnioskowania HM, to wdrożenie go w Prologu. Jest to w rzeczywistości zaskakująco proste i wyjaśnia, co się dzieje. Z pewnością powinieneś spróbować.
Uwaga: Prawdopodobnie popełniłem kilka błędów w tym wyjaśnieniu i bardzo by mi się podobało, gdyby ktoś je wskazał. Za kilka tygodni zajmę się tym na zajęciach, więc będę bardziej pewny niż: P.
źródło
Γ = {x : τ}
)λy.x : σ → τ
do∀ σ. σ → τ
, ale nie do∀ τ. σ → τ
, ponieważτ
jest zmienną swobodną wΓ
. Artykuł Wikipedii na temat HM wyjaśnia to całkiem ładnie.[Inst]
jest nieco niedokładna. To jest tylko moje rozumienie tej pory, ale w Sigmas[Inst]
i[Gen]
przepisy nie odnoszą się do rodzaju, ale na schematach typu . Tak więc⊑
operator jest częściowym uporządkowaniem niezwiązanym z podtypami, jakie znamy z języków OO. Jest to związane z wartościami polimorficznymi, takimi jakid = λx. x
. Pełna składnia takiej funkcji byłabyid = ∀x. λx. x
. Teraz możemy oczywiście miećid2 = ∀xy. λx. x
, gdziey
nie jest używany. Zatemid2 ⊑ id
, tak[Inst]
mówi reguła.Zobacz „ Praktyczne podstawy języków programowania ”, rozdziały 2 i 3, na temat logiki opartej na osądach i pochodnych. Cała książka jest teraz dostępna na Amazon.
Rozdział 2
Definicje indukcyjne
Definicje indukcyjne są niezbędnym narzędziem w nauce języków programowania. W tym rozdziale opracujemy podstawowe ramy definicji indukcyjnych i podamy przykłady ich zastosowania. Definicja indukcyjna składa się z zestawu zasad dotyczących wydawania osądów lub twierdzeń o różnych formach. Wyroki są stwierdzeniami na temat jednego lub większej liczby obiektów składniowych określonego rodzaju. Przepisy określają niezbędne i wystarczające warunki dla ważności wyroku, a tym samym w pełni określają jego znaczenie.
2.1 Orzeczenia
Zaczynamy od pojęcia wyroku lub twierdzenia o obiekcie składniowym. Będziemy korzystać z wielu form osądu, w tym takich przykładów:
Wyrok stwierdza, że jeden lub więcej obiektów składniowych ma właściwość lub stoi w pewnym stosunku do siebie. Sama własność lub relacja nazywana jest formą osądu , a osąd, że przedmiot lub przedmioty mają tę własność lub stanowisko w tej relacji, jest uważany za przykład takiej formy osądu. Forma osądu jest również nazywana orzeczeniem , a przedmioty stanowiące instancję są jej podmiotami . Piszemy o J o wyroku twierdząc, że J ładowniach . Kiedy podkreślenie przedmiotu wyroku nie jest ważne (tekst ucina się tutaj)
źródło
Jak rozumiem zasady Hindleya-Milnera?
Hindley-Milner jest zbiorem reguł w postaci rachunku sekwencyjnego (a nie naturalnej dedukcji), który pokazuje, że możemy wywnioskować (najbardziej ogólny) typ programu z jego konstrukcji bez wyraźnych deklaracji typu.
Symbole i notacja
Najpierw wyjaśnijmy symbole i omówmy pierwszeństwo operatora
𝜆𝑥.𝑒 oznacza, że 𝜆 (lambda) to anonimowa funkcja, która przyjmuje argument 𝑥 i zwraca wyrażenie 𝑒 .
niech 𝑥 = 𝑒₀ w 𝑒₁ oznacza w wyrażeniu 𝑒₁ , zamień 𝑒₀ gdziekolwiek pojawia się 𝑥 .
⊑ oznacza, że poprzedni element jest podtypem (nieformalnie - podklasa) tego drugiego elementu.
Wszystko powyżej linii jest przesłanką, wszystko poniżej jest wnioskiem ( Per Martin-Löf )
Pierwszeństwo na przykładzie
Wziąłem niektóre bardziej złożone przykłady z reguł i wstawiłem zbędne nawiasy, które pokazują pierwszeństwo:
𝚪 ⊦ 𝑥 : 𝜎 można zapisać 𝚪 ⊦ ( 𝑥 : 𝜎 )
𝚪 ⊦ niech 𝑥 = 𝑒₀ w 𝑒₁ : 𝜏 jest równoważnie 𝚪 ⊦ (( niech ( 𝑥 = 𝑒₀ ) w 𝑒₁ ): 𝜏 )
𝚪 ⊦ 𝜆𝑥.𝑒 : 𝜏 → 𝜏 ' jest równoważnie 𝚪 ⊦ (( 𝜆𝑥.𝑒 ): ( 𝜏 → 𝜏' ))
Następnie duże spacje oddzielające stwierdzenia asertywne i inne warunki wstępne wskazują zestaw takich warunków wstępnych, a na koniec pozioma linia oddzielająca przesłankę od zakończenia przybliża koniec kolejności pierwszeństwa.
Zasady
Poniżej znajdują się angielskie interpretacje zasad, po których następuje luźne przekształcenie i wyjaśnienie.
Zmienna
Innymi słowy, w 𝚪 wiemy, że 𝑥 jest typu 𝜎, ponieważ 𝑥 jest typu 𝜎 w 𝚪.
Jest to w zasadzie tautologia. Nazwa identyfikatora to zmienna lub funkcja.
Zastosowanie aplikacji
Aby ponownie sformułować regułę, wiemy, że aplikacja funkcji zwraca typ 𝜏 ', ponieważ funkcja ma typ 𝜏 → 𝜏' i otrzymuje argument typu 𝜏.
Oznacza to, że jeśli wiemy, że funkcja zwraca typ, i zastosujemy ją do argumentu, wynikiem będzie instancja typu, o którym wiemy, że zwraca.
Abstrakcja funkcji
Ponownie, gdy widzimy funkcję, która przyjmuje 𝑥 i zwraca wyrażenie 𝑒, wiemy, że jest ona typu 𝜏 → 𝜏 ', ponieważ 𝑥 (a 𝜏) twierdzi, że 𝑒 jest 𝜏 ”.
Jeśli wiemy, że 𝑥 jest typu 𝜏, a zatem wyrażenie 𝑒 jest typu 𝜏 ', to funkcja 𝑥 zwracającego wyrażenie 𝑒 jest typu 𝜏 → 𝜏'.
Niech zmienna deklaracja
Luźno 𝑥 jest związany z 𝑒₀ w 𝑒₁ (a 𝜏), ponieważ 𝑒₀ jest 𝜎, a 𝑥 jest 𝜎, który twierdzi, że 𝑒₁ jest 𝜏.
Oznacza to, że jeśli mamy wyrażenie 𝑒₀, które jest 𝜎 (będące zmienną lub funkcją), i jakąś nazwę, also, również 𝜎 i wyrażenie 𝑒₁ typu 𝜏, wówczas możemy zastąpić 𝑒₀ zamiast 𝑥 wszędzie tam, gdzie pojawia się w środku z 𝑒₁.
Instancja
Wyrażenie 𝑒 jest typu nadrzędnego 𝜎, ponieważ wyrażenie 𝑒 jest podtypem 𝜎 ', a 𝜎 jest rodzicielskim typem 𝜎 ”.
Jeśli instancja jest typu, który jest podtypem innego typu, to jest to również instancja tego nadtypu - bardziej ogólnego typu.
Uogólnienie
Ogólnie rzecz biorąc, typ jest wpisywane 𝜎 dla wszystkich zmiennych argumentów (𝛼) zwracających 𝜎, ponieważ wiemy, że a jest 𝜎, a variable nie jest zmienną wolną.
Oznacza to, że możemy uogólnić program, aby akceptował wszystkie typy argumentów, które nie są jeszcze powiązane w zawierającym zakresie (zmienne, które nie są lokalne). Te powiązane zmienne są zastępowalne.
Kładąc wszystko razem
Biorąc pod uwagę pewne założenia (takie jak brak wolnych / niezdefiniowanych zmiennych, znane środowisko), znamy typy:
Wniosek
Łącznie te reguły pozwalają nam udowodnić najbardziej ogólny typ zapewnianego programu, bez wymagania adnotacji typu.
źródło
Notacja pochodzi z naturalnej dedukcji .
⊢ symbol nazywa się kołowrót .
6 zasad jest bardzo łatwe.
Var
reguła jest raczej trywialną regułą - mówi, że jeśli typ identyfikatora jest już obecny w twoim środowisku typowym, to aby wywnioskować typ, po prostu zabierz go ze środowiska bez zmian.App
Reguła mówi, że jeśli masz dwa identyfikatorye0
ie1
możesz wywnioskować ich typy, możesz wywnioskować rodzaj aplikacjie0 e1
. Reguła brzmi następująco: jeśli wiesz o tyme0 :: t0 -> t1
ie1 :: t0
(to samo t0!), To aplikacja jest dobrze napisana, a typ jestt1
.Abs
iLet
są zasadami wnioskowania o typach dla abstrakcji lambda i let-in.Inst
reguła mówi, że możesz zastąpić typ mniej ogólnym.źródło
Istnieją dwa sposoby myślenia o e: σ. Jeden to „wyrażenie e ma typ σ”, drugi to „uporządkowana para wyrażenia e i typ σ”.
Zobacz Γ jako wiedzę o typach wyrażeń, zaimplementowaną jako zestaw par wyrażeń i typów, e: σ.
Bramka ⊢ oznacza, że z wiedzy po lewej stronie możemy wywnioskować, co jest po prawej stronie.
Można zatem odczytać pierwszą zasadę [Var]:
jeśli nasza wiedza Γ zawiera parę e: σ, to możemy wywnioskować z Γ, że e ma typ σ.
Drugą zasadę [App] można odczytać:
jeśli my z Γ możemy wywnioskować, że e_0 ma typ τ → τ ', a my z Γ możemy wywnioskować, że e_1 ma typ τ, to z Γ możemy wywnioskować, że e_0 e_1 ma wpisz τ ”.
Często pisze się write, e: σ zamiast Γ ∪ {e: σ}.
Trzecią zasadę [Abs] można zatem odczytać:
jeśli z Γ rozszerzonej o x: τ możemy wywnioskować, że e ma typ τ ', to z Γ możemy wywnioskować, że λx.e ma typ τ → τ'.
Czwarta zasada [Let] pozostawia się jako ćwiczenie. :-)
Piątą zasadę [Inst] można odczytać:
jeśli możemy z Γ wywnioskować, że e ma typ σ ', a σ' jest podtypem σ, to z Γ możemy wywnioskować, że e ma typ σ.
Szóstą i ostatnią zasadę [Gen] można odczytać:
jeśli możemy z Γ wywnioskować, że e ma typ σ, a α nie jest zmienną dowolnego typu w żadnym z typów w Γ, wówczas z Γ możemy wywnioskować, że e ma typ ∀α σ.
źródło