Pobierz prezentację
Pobieranie prezentacji. Proszę czekać
OpublikowałZłota Ławecki Został zmieniony 11 lat temu
1
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 1 listopad 2004 Konstrukcja systemów obiektowych i rozproszonych Wykładowca: Kazimierz Subieta Polsko-Japońska Wyższa Szkoła Technik Komputerowych, Warszawa subieta@pjwstk.edu.pl Instytut Podstaw Informatyki PAN, Warszawa subieta@ipipan.waw.pl Wykład 8: Mocna kontrola typów, cz.2
2
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 2 listopad 2004 Metareguły wnioskowania o typie Dla operatorów niealgebraicznych oraz niektórych algebraicznych, takich jak, itd. reguły przedstawione poprzednio muszą być uogólnione. Operatory te działają na nieskończonej rodzinie sygnatur (są polimorficzne). Przykładowo, dla operatorów where, kropka, join i odpowiednie metareguły mogą mieć postać: T where bool T T1.bag{T2} bag{T2} bag{T1} join bag{T2} bag{struct{T1, T2}} bag{T1} bag{T2} if T1 = T2 then bag{T1} else bag{variant{T1, T2}}..... (T, T1, T2 są dowolnymi sygnaturami). Takie metareguły mogą być bardzo zróżnicowane i ich opracowanie dla danego modelu składu, języka oraz systemu typów jest wyzwaniem. W metaregułach może być wiele kombinacji sygnatur wejściowych, które warto wyróżnić, oraz wiele odpowiadających im wynikowych sygnatur.
3
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 3 listopad 2004 Złożone metareguły Metareguły wnioskowania o typie mogą przyjmować dość rozbudowaną postać. Rozpatrzmy np. operator kropki w zapytaniu q 1.q 2. Podane poprzednio wyjaśnienie semantyki ustala, że zarówno q 1, jak i q 2 muszą zwrócić bagi. Tak oczywiście nie musi być, jeżeli nie chcemy zmuszać użytkownika do komplikowania zapytania poprzez wstawienie do niego explicite funkcji konwersji typów. Typ wynikowy takiego zapytania zależy od wydedukowanych typów dla q 1 oraz q 2. Zarówno q 1, jak i q 2 mogą zwrócić pojedynczy element, bag lub sekwencję. Kombinacja tych przypadków daje 9 możliwości, które ułożymy w tabelę:
4
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 4 listopad 2004 Metareguła – tabela wnioskowania o typie (dla.) Typ q 1 Typ q 2 Wynik elementT1elementT2elementT2 elementT1bagbag{T2}bagbag{T2} elementT1sekwen.sequence{T2}sekwen.sequence{T2} bagbag{T1}elementT2bagbag{T2} bagbag{T1}bagbag{T2}bagbag{T2} bagbag{T1}sekwen.sequence{T2}błąd typu sekwen.sequence{T1}elementT2sekwen.sequence{T2} sekwen.sequence{T1}bagbag{T2}błąd typu sekwen.sequence{T1}sekwen.sequence{T2}sekwen.sequence{T2} Zapytanie q 1.q 2
5
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 5 listopad 2004 Komentarz do tabelki W tabelce tej ustaliliśmy, że jeżeli q 1 zwraca bag, zaś q 2 zwraca sekwencję, to nie ma dobrego sposobu wyznaczenia ostatecznej sekwencji, zatem jest to błąd typologiczny. Podobnie dla ustawienia sekwencja-bag. To założenie można nieco złagodzić przyjmując przesunięcie kontroli typologicznej na czas wykonania. Podobne tabelki można budować dla operatorów, where, join, as, kwantyfikatorów itd. Ich duża nieregularność, możliwość przesunięcia kontroli typologicznej na czas wykonania oraz arbitralność niektórych decyzji uzasadniają naszą niewiarę w skuteczność lub wręcz możliwość formalizacji mechanizmu mocnej kontroli typu. W najlepszym razie system formalny byłby uwikłany w nużące szczegóły, które podważyłyby estetykę i elegancję, czyli cechy, które są podstawową przyczyną i bazą psychologiczną zainteresowania metodami formalnymi w informatyce.
6
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 6 listopad 2004 Co to jest wewnętrzny system typów? Wewnętrzny system typów jest to: Graf schematu (podany uprzednio przy okazji optymalizacji) Tabela reguł wnioskowania o typie dla operatorów monomorficznych (działających na jednym lub kilku typach), takich jak +; Zestaw metareguł wnioskowania o typie dla operatorów polimorficznych (działających na nieskończonej rodzinie typów), takich jak podana uprzednio tabela. Na podstawie tych struktur oraz statycznych stosów S_ENVS i S_QRES procedura static_type_check, działając rekurencyjnie na drzewie syntaktycznym, jest w stanie wyprodukować listę błędów typologicznych.
7
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 7 listopad 2004 Graf schematu - metabaza (przypomnienie) Osoba 0..* Nazwisko 1 string Prac 0..* RokUr 1 int Wiek 1 void int NrP 1 int Stan 1..* string Zar 0..1 int ZarNetto 1 void int ZmieńZar 0 nowyZar:int void PracujeW 1 Kieruje 0..1 Zatrudnia 1..* Szef 1 Dział 0..* NrD 1 int Nazwa 1 string Lokacja 1..* string BudżetRoczny 1 void int Osoba[0..*] Nazwisko RokUr Wiek() Prac[0..*] NrP Stan[1..*] Zar[0..1] ZmieńZar(nowyZar) ZarNetto( ) Dział [0..*] NrD Nazwa Lokacja[1..*] BudżetRoczny() Kieruje [0..1] Szef PracujeW Zatrudnia [1..*]
8
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 8 listopad 2004 Procedura static_type_check Z koncepcyjnego punktu widzenia jest prawie identyczna z procedurą static_eval prezentowanej poprzednio. Obie działają na S_ENVS i S_QRES, identycznie zapełniają je sygnaturami i identycznie korzystają z reguł i metareguł wnioskowania o typie. static_type_check wykonuje następujące operacje: Kontroluje typologicznie drzewo syntaktyczne poprzez zasymulowanie na stosach S_ENVS i S_QRES działań mechanizmu ewaluacji zapytań. Wyprowadza na zewnątrz komunikaty o błędach typologicznych. Dokonuje zmian drzewa syntaktycznego w celu wprowadzenia tam operatorów automatycznej dereferencji oraz operatorów koercji. Dokonuje zmian drzewa syntaktycznego w celu wprowadzenie do niego operatorów dynamicznej kontroli typologicznej. Po wykryciu i zakomunikowaniu błędu typologicznego dokonuje naprawy drzewa syntaktycznego i/lub stosów statycznych, aby umożliwić wykrycie więcej niż jednego błędu typologicznego w ramach jednego przebiegu sprawdzania kodu. Procedura static_type_check powinna być uruchomiona wcześniej niż static_eval.
9
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 9 listopad 2004 Uruchomienie procedury static_type_check Procedura działa na drzewie syntaktycznym zapytania i jest zdefiniowana w sposób rekurencyjny. Przed uruchomieniem procedury stos S_ENVS musi posiadać statyczną sekcję bazową, zawierającą statyczne bindery do wszystkich startowych (korzeniowych) węzłów metabazy: np. dla podanej metabazy sygnatury bag{Osoba(i Osoba )}, bag{Prac(i Prac )}, bag{Dział(i Dział )}. Jeżeli środowisko ewaluacyjne zawiera więcej sekcji bazowych, wówczas każde z nich musi być odwzorowane przez sekcję ze statycznymi binderami startowymi.
10
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 10 listopad 2004 Postać procedury static_type_check (1) procedure static_type_check ( query ) { case query is l { //l jest literałem Przypisz literał do najbardziej prawdopodobnego typu atomowego na podstawie postaci tego literału oraz rodzaju operatora, który znajduje się bezpośrednio ponad nim w drzewie syntaktycznym; Wstaw ten typ atomowy jako sygnaturę na wierzchołek S_QRES. } case query is n { //n jest nazwą Poczynając od góry stosu S_ENVS szukaj statycznego bindera n(s) lub sygnatur bag{n(s)}, lub sequence{n(s)}; if taka sygnatura jest znaleziona then Wstaw sygnaturę s, bag{s} lub sequence{s} na wierzchołek S_QRES; else { Sygnalizuj błąd typologiczny błędna nazwa n; Reperuj proces kontroli typologicznej wstawiając na wierzchołek S_QRES najbardziej prawdopodobną sygnaturę; }
11
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 11 listopad 2004 Postać procedury static_type_check (2) case query is q 1 q 2 {// jest oper. alg. binarnym static_type_check (q1); static_type_check (q2); Zbadaj, czy sygnatura q 1 poniżej wierzchołka S_QRES pasuje do operatora ; jeżeli nie, próbuj wstawić do drzewa syntaktycznego q 1 operator dereferencji i/lub koercji; Zbadaj, czy sygnatura q 2 na wierzchołku S_QRES pasuje do operatora ; jeżeli nie, próbuj wstawić do drzewa syntaktycznego q 2 operator dereferencji i/lub koercji; Zastosuj regułę lub metaregułę na wierzchołku S_QRES zgodną z ; zdejmij dwa razy wierzchołek S_QRES i wstaw tam wynikową sygnaturę; Jeżeli reguła lub metareguła wskazuje na błąd typologiczny, sygnalizuj ten błąd; W razie błędu reperuj proces kontroli typologicznej poprzez zastąpienie sygnatury na wierzchołku S_QRES najbardziej prawdopodobną poprawną sygnaturą; } procedure static_type_check ( query ) {.....
12
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 12 listopad 2004 Postać procedury static_type_check (3) case query is q 1 q 2 { // jest oper. niealgebraicznym q1result, finalsignature: TypSygnatura; static_type_check(q 1 ); //rezultat q 1 jest na czubku S_QRES Zbadaj, czy sygnatura na wierzchołku S_QRES pasuje do operatora ; jeżeli nie, próbuj wstawić do drzewa syntaktycznego q 1 operator koercji, zmieniając jednocześnie na QRES jego sygnaturę; q1result := top(S_QRES); push(S_ENVS, static_nested(q1result));//otwarcie nowego zakresu na S_ENVS: Ewentualnie: włożenie sekcji nadklas na S_ENVS; static_type_check(q 2 ); //rezultat q 2 jest na czubku S_QRES Zbadaj czy sygnatura na wierzchołku S_QRES pasuje do operatora ; jeżeli nie, próbuj wstawić do drzewa syntaktycznego q 2 operator koercji, zmieniając jednocześnie na S_QRES jego sygnaturę; Zastosuj metaregułę na wierzchołku S_QRES zgodną z, czyli finalsignature:= metarule (q1result, top(S_QRES)); Jeżeli reguła lub metareguła wskazuje na błąd typologiczny, sygnalizuj go, następnie reperuj proces kontroli typologicznej; pop(S_ENVS); //obniżenie S_ENVS Ewentualnie: usunięcie sekcji nadklas z S_ENVS; pop(S_QRES);// usunięcie rezultatu q 2 pop(S_QRES);// usunięcie rezultatu q 1 push(S_QRES, finalsignature);//włożenie końcowego wyniku }
13
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 13 listopad 2004 Komentarz do static_type_check Powyższy pseudokod należy traktować jako zgrubny sposób postępowania. Konkretny algorytm zależy od przyjętego modelu składu obiektów, przyjętego zewnętrznego i wewnętrznego systemu typów, przyjętych założeń odnośnie do elips i automatycznych koercji itd. W przypadku każdego operatora (algebraicznego lub niealgebraicznego) należy bardzo dokładnie: rozpatrzyć możliwą postać jego argumentów (sygnatur), ustalić poprawność lub niepoprawność typologiczną, wyznaczyć wynikową sygnaturę, wyznaczyć najbardziej odpowiedni sygnał błędu typologicznego wyznaczyć sposób naprawy drzewa syntaktycznego i/lub wynikowej sygnatury. Należy oczekiwać, że algorytm ten będzie uwikłany w dużą liczbę dość arbitralnych decyzji koncepcyjnych i implementacyjnych.
14
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 14 listopad 2004 Uwzględnienie atrybutów sygnatur Kontrola typologiczna może dotyczyć kilku cech, które nie zostały dotąd uwzględnione w konstrukcji sygnatur i regułach wnioskowania o typie. Takie cechy będziemy nazywać atrybutami sygnatur. Atrybuty sygnatur uczestniczą w procesie wnioskowania o typie: Mutowalność (mutability) - atrybut określa, czy odpowiednia wartość czasu wykonania jest referencją do aktualizowalnego obiektu. Liczność (cardinality) - atrybut określa, ilu wartości odpowiadających danej sygnaturze należy oczekiwać podczas czasu wykonania. Przyjmuje wartość z 5-cio elementowej skali 0, 1, [0..1], [0..*], [1..*]. Rodzaj kolekcji - atrybut może przyjmować wartość element, bag, sequence, array lub inny. Nazwa typu - atrybut przyjmuje wartość będącą nazwą typu danego specyfikowanego bytu programistycznego. Wartość atrybutu jest określona (niejawnie) w grafie schematu lub wynika z deklaracji parametru procedury. Możliwe są dalsze atrybuty sygnatur.
15
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 15 listopad 2004 Atrybut mutowalności Ustala, czy byt o danej sygnaturze można zastosować w konstrukcjach, w których oczekiwana jest referencja. Jest ona oczekiwana w zdaniach imperatywnych update (z lewej strony podstawienia) insert i delete oraz w przypadku parametru procedury, funkcji lub metody wołanego przez referencję. Wartość atrybutu true oznacza więc automatycznie, że odpowiednia sygnatura ma postać identyfikatora węzła grafu schematu (i odwrotnie).. Atrybut mutowalności przyjmuje wartość true w momencie wiązania nazwy do statycznego bindera wskazującego na węzeł grafu schematu. Sygnatura zmienia atrybut mutowalności na false, po dereferencji. Jeżeli dla sygnatury lewej strony operatora podstawienia (update) wartość atrybutu mutowalności jest false, to należy zasygnalizować błąd typologiczny. Podobnie dla operatorów wstawiania i usuwania. Atrybut mutowalności jest również podstawą wstawienia do drzewa syntaktycznego operatorów dereferencji.
16
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 16 listopad 2004 Atrybut liczności Pozwala ustalić w wystarczającym przybliżeniu, jaka będzie liczność wartości zwracanych przez dane zapytanie. Dotychczas atrybut ten pojawiał się w podanych wyżej przykładach sygnatur w postaci słowa kluczowego bag. Taka kwalifikacja jest równoważna atrybutowi liczności [0..*] i może być zastąpiona przez tę liczność. Inne wartości liczności mogą również służyć do wnioskowania o poprawności typologicznej, np. wartość [0..1]. Znajomość tego atrybutu może być niezbędna dla pewnych optymalizacji, np. usuwania martwych podzapytań. Znajomość ta może być również konieczna dla prawidłowego przebiegu kontroli typologicznej. W szczególności, umożliwia wstawienie do drzewa syntaktycznego operatorów koercji z bagu do pojedynczego elementu lub odwrotnie. Dla dowolnych operatorów algebraicznych i niealgebraicznych należy przewidzieć wykorzystanie tego atrybutu do sygnalizacji błędu typologicznego, koercji i/lub dynamicznej kontroli typologicznej.
17
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 17 listopad 2004 Atrybut rodzaju kolekcji Atrybut rodzaju kolekcji był dotychczas zaznaczany przez nas w sygnaturach słowem bag lub sequence (lub brakiem tych kwalifikatorów). Atrybut ten może ulegać zmianie w wyniku działania pewnych operatorów, np. operator order by zmienia bag na sekwencję. Niektóre operatory są właściwe tylko dla sekwencji, np. konkatenacja lub odzyskanie i-tego elementu, wobec czego ten atrybut może być także podstawą sygnalizacji błędu typologicznego.
18
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 18 listopad 2004 Atrybut nazwy typu Atrybut nazwy typu jest istotny w przypadku, gdy decydujemy się na nazwową zgodność typu. Atrybut pozwala przypisać nazwę typu do danej sygnatury, co umożliwia sprawdzenie, czy wyliczona sygnatura jest zgodna z oczekiwaną. Np. jeżeli mamy definicję o postaci: typedef TypPesel int; wówczas deklaracja atrybutu obiektu w postaci: NrPesel: TypPesel; oznacza, że do sygnatury NrPesel(i NrPesel ) zostaje przypisany atrybut nazwy typu TypPesel. Atrybut ten może być sprawdzany w sytuacji, gdy procedura ma zadeklarowany parametr formalny x typu TypPesel, zaś przy wywołaniu na x podstawia się pewne zapytanie q. Jeżeli wyliczona sygnatura q nie ma atrybutu nazwy typu TypPesel, wówczas jest to błąd typologiczny.
19
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 19 listopad 2004 Trudności z nazwową zgodnością typu Przyjęcie nazwowej zgodności typu wiąże się z trudnością dotyczącą operatorów jawnie lub niejawnie przypisanymi do typu. Pascal przyjmuje, że w ten sposób nowo zdefiniowany typ dziedziczy wszystkie operatory z jego definicji, np. TypPesel dziedziczy z int. W ten sposób na wartościach typu TypPesel można wykonywać operacje +, -, *, /, co nie ma sensu. Jedynym sposobem usunięcia tej niedogodności jest specjalna składnia definicji typu, w której podaje się operatory dziedziczone do danego typu. Użycie operatorów, które nie są odziedziczone, oznacza błąd typologiczny. Nowe operatory dla nowo zdefiniowanego typu mogą być określone przez napisanie funkcji posiadających ten typ jako argument. Zgodność nazwowa implikuje także problem z dziedziczeniem i zasadą zastępowania (substitutability). Podtrzymanie tej zasady można jednak łatwo uzyskać.
20
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 20 listopad 2004 Czy mocna kontrola typów musi być taka mocna? Wyznawcy ortodoksyjnej statycznej kontroli typologicznej twierdzą, że programista jest zobowiązany do takiego sformułowania programów, aby typy były zawsze zgodne. Instrumentem do tego są różnorodne operatory zmiany typu, takie jak operatory rzutowania (cast). Odstępstwa od tej reguły, polegające na automatycznym stosowaniu operatorów koercji, są uważane za własność marginalną nieco negatywną. Ten stereotyp mocnej kontroli typów zdominował myślenie o typach jako o technice formalistycznego wymuszania bardzo regularnych formatów. Przykładem takiego myślenia jest standard ODMG 3.0, który z nabożeństwem traktuje konieczność określania przez programistów operatorów zmiany typu, np. zmianę bagu na wartość będącą elementem tego bagu. Podobnie, twórcy modeli danych określanych jako półstrukturalne (semi- structured), których przypadkiem jest XML, przyjmują niekiedy, że są to systemy beztypowe (typeless) lub bezschematowe (schemaless). Dominuje pogląd, że skoro modele te operują na nieregularnych danych, to mocna statyczna kontrola typologiczna jest dla nich niemożliwa.
21
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 21 listopad 2004 Wady formalistycznej kontroli typologicznej Odmienne stanowisko zajmują twórcy języków nie posiadających mocnej kontroli typologicznej lub posiadające wyłącznie kontrolę dynamiczną, takich jak SQL. W językach tych przyjmuje się koercje, czyli automatyczne zmiany typów jako oczywiste. Np. oficjalnie zdanie select języka SQL zwraca tabelę, czyli kolekcję wartości, ale nieoficjalnie takie zdanie można zanurzyć w klauzulę where i wtedy takie zdanie zwraca nie tabelę, ale pojedynczą wartość. Wadą formalistycznej kontroli typologicznej jest obciążenie kodu zapytania lub programu przez techniczny folklor utrudniający modelowanie pojęciowe i szybkie rozumienie semantyki zapytania. Pewne znaczenie ma także ponowne użycie i ewolucja schematu bazy danych: niektóre zapytania nie muszą się zmieniać, mimo zmian typów uczestniczących w nich bytów programistycznych.
22
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 22 listopad 2004 Koercje i dynamiczna kontrola typologiczna Bardzo rygorystyczne i formalistyczne traktowanie mocnej kontroli typów, lub odwrotnie, całkowity brak kontroli typów nie są jedynymi stanami. Mocną kontrolę typologiczną można stosować do bardzo rozluźnionych formatów, w których występują dane opcyjne (liczność [0..1]), dane powtarzalne (liczność [0..*] lub [1..*]) oraz warianty. W takich przypadkach kontrola typologiczna powinna uwzględniać automatyczne koercje oraz dynamiczne kontrole typów, Techniką implementacyjną dla automatycznych koercji i dynamicznych kontroli typów jest wstawianie do drzewa syntaktycznego węzłów, w efekcie których nastąpi zmiana typu i/lub dynamiczna kontrola typologicznej. Mocna kontrola typologiczna nie polega więc na trzymaniu się rygorystycznych schematów formalnych. Nie jest teoretyczną bądź estetyczną sztuką dla sztuki. Nie ma sensu z przyczyn formalnych komplikować życia programisty, jeżeli zagrożenie błędem jest minimalne i kiedy jawne koercje bardzo zamulają kod zapytania/programu.
23
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 23 listopad 2004 Rozluźniona kontrola typologiczna Dowolny formalnie wymyślony system kontroli typologicznej może nie spełnić swojej roli, o ile nie uwzględni faktu, że tylko programista, wraz z jego psychologią, przyzwyczajeniami i predyspozycjami, jest adresatem mocnej kontroli typów. Znaczne usztywnienie systemu typów oznacza większą uciążliwość przy pisaniu programów oraz dłuższy i mniej czytelny kod. Mocna kontrola typologiczna tak czy inaczej nie jest w stanie wykryć wszystkich błędów, które programista może popełnić. Jeżeli nieco luźniejszy, ale przyjazny dla programisty mechanizm kontroli typologicznej wykryje np. 80% wszystkich popełnianych przez niego błędów, zaś bardzo sztywny i nieprzyjazny wykryje 81%, to ten drugi nie ma sensu. Nie jest więc konieczne traktowanie np. zapytania o sygnaturze wejściowej string + int jako błędu typologicznego. Jeżeli odpowiada to naszemu wyczuciu zachowania się programisty, możemy wstawić do drzewa syntaktycznego operator zmiany stringu na int i dynamicznie sprawdzać poprawności tej operacji. Nie ma istotnych problemów technicznych lub koncepcyjnych z uwzględnieniem tego rodzaju własności w mechanizmie mocnej kontroli typologicznej.
24
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 24 listopad 2004 Przykłady koercji Zmiana bagu na element: Prac where Zarobek > ((Prac where Nazwisko = Kowalski).Zarobek) Zamiana elementu na bag (Pracownik.Nazwisko) union Kowalski
25
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 25 listopad 2004 Kontrola operatorów imperatywnych Przykładami takiej kontroli są operatory if oraz while, dla których wynikowa sygnatura zapytania będącego argumentem tego operatora powinna być bool. Są to łatwe przypadki. Trudniejszym przypadkiem jest operator podstawienia. Dla niego sygnatura lewej strony powinna mieć atrybut mutowalności o wartości true. Sygnatury prawej i lewej strony podstawienia powinny być zgodne. W przypadku danych złożonych operacja podstawienia jest trudniejsza i może przyjmować wiele wariantów semantycznych. Przykładowo, można przyjąć, że podstawienie jest możliwie tylko w przypadku, gdy sygnatury lewej i prawej strony podstawienia mają identyczny atrybut nazwy typu. Oznacza to zgodność nazwową. Przy zastosowaniu zgodności strukturalnej problem jest prosty, o ile sygnatura prawej i lewej strony jest identyczna (co oznacza, że obie są referencjami do tego samego węzła grafu schematu). W pozostałych przypadkach powstaje np. pytanie, czy na obiekt Prac można podstawiać obiekt Osoba, lub odwrotnie, i jakie miałoby to skutki? Podobne problemy należy rozstrzygnąć dla innych operatorów imperatywnych.
26
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 26 listopad 2004 Kontrola procedur – kontrola ciała Kontrola typologiczna procedur (do których zaliczamy także funkcje, metody i perspektywy) występuje w dwóch postaciach: kontrola ciała procedur i kontrola wywołania procedur. Te kontrole są od siebie niezależne. Kontrola ciała procedur odbywa się na podanych wcześniej zasadach. Pierwszym krokiem jest zbudowanie grafu schematu dla lokalnych obiektów. Różnicą w stosunku do poprzednio wyjaśnionego mechanizmu jest pojawienie się statycznych binderów nazwaParametru(nazwaTypu) lub nazwaParametru(i dWęzła ). Pierwszy binder pojawia się w przypadku wołania przez wartość, drugi w przypadku wołania przez referencję. Nazwa nazwaParametru jest nazwą parametru umieszczoną w deklaracji procedury. W przypadku strukturalnej zgodności typów pierwszy binder powinien być rozwinięty do pełnej postaci poprzez usunięcie nazw typów. Drugi binder ustala identyfikator węzła grafu schematu. Może on jednak odnosić się do węzła dowolnego grafu schematu, np. bazy danych lub modułu. Kontrola rozpoczyna się od wstawienia na wierzchołek S_ENVS nowej sekcji (statycznego zapisu aktywacyjnego) zawierającej wszystkie ww. statyczne bindery parametrów oraz bindery do wszystkich węzłów korzeniowych lokalnego grafu schematu. Po zakończeniu sprawdzania danej procedury sekcja ta jest usuwana.
27
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 27 listopad 2004 Kontrola procedur – kontrola parametrów Kontrola parametrów procedury polega na porównaniu zadeklarowanej sygnatury parametru formalnego (z uwzględnieniem atrybutu mutowalności) z wyliczoną sygnaturą zapytania będącego aktualnym parametrem. W przypadku zgodności nazwowej wystarczy porównać atrybuty nazwy typu na identyczność. Jeżeli nie są identyczne, należy sygnalizować błąd typu. Porównanie wartości atrybutu nazwy typu może uwzględnić związki dziedziczenia. W przypadku zgodności strukturalnej problem jest bardziej złożony. Łatwy przypadek występuje wtedy, gdy obydwie sygnatury są tym samym typem elementarnym lub tym samym identyfikatorem węzła grafu schematu. Trudniejszy przypadek dotyczy sytuacji, gdy porównywanie sygnatur musi być rekurencyjne, zgodnie z ich hierarchiczną budową. Idea tej kontroli wydaje się jasna od strony koncepcyjnej, ale rozstrzygnięcie wszystkich szczegółów może być problemem.
28
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 28 listopad 2004 Rozważmy następującą deklarację typu: Jeżeli potraktujemy tę definicję z punktu widzenia logiki, to mamy do czynienia z dość podejrzaną rekurencją: TypOsoba jest definiowany poprzez TypOsoba. W takich przypadkach logicy mówią, że mamy do czynienia nie z rekurencją, lecz z błędem logicznym ignotum per ignotum, czyli definiowanie nieznanego pojęcia przez nieznane pojęcia. Z drugiej strony, okazuje się, że takie definicje są bardzo powszechne w językach obiektowych, całkowicie zrozumiałe z praktycznego punktu widzenia i w pełni implementowalne. Czy może być implementowalne coś, co jest logicznie sprzeczne? Typy referencyjne typedef TypOsoba struct{ Nazwisko: string, RokUr: int, Dziecko: TypOsoba [0..*], czyJestKuzynem (innaOsoba: TypOsoba): bool, najstarsiKuzyni(): TypOsoba [0..*] };
29
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 29 listopad 2004 Wyjaśnienie sprzeczności Nie ma żadnej sprzeczności. Wymienione na poprzednim slajdzie typy takie jak Dziecko: TypOsoba [0..*] są po prostu typami referencyjnymi. W takich deklaracjach obowiązują niepisane umowy: Typ taki jak TypOsoba deklaruje nie obiekt typu Osoba, lecz referencję do obiektów typu Osoba Dodatkowo przyjmuje się, że referencje takie muszą prowadzić do obiektów o nazwie Osoba. Konsekwencja tych dwóch umów są takie, że deklaracja typedef, taka jak na poprzednim slajdzie, nie może mieć samodzielnego bytu semantycznego. Jest tylko napisem. Przy tych założeniach można zbudować normalny graf schematu, taki jak na następnym slajdzie.
30
© K.Subieta. Konstrukcja systemów obiektowych i rozproszonych 8, Folia 30 listopad 2004 Graf schematu powstały z deklaracji typu Deklaracja: Osoba: TypOsoba[0..*] KlasaOsoba 1 Osoba 0..* Nazwisko 1 string RokUr 1 int Dziecko 0..* czyJestKuzynem 1 innaOsoba: bool najstarsiKuzyni 0..* void
Podobne prezentacje
© 2024 SlidePlayer.pl Inc.
All rights reserved.