Własny (język programowania) - Self (programming language)

Samego siebie
Logo
Paradygmat zorientowane obiektowo ( oparte na prototypach )
Zaprojektowany przez David Ungar , Randall Smith
Deweloper David Ungar, Randall Smith, Uniwersytet Stanforda , Sun Microsystems
Po raz pierwszy pojawiły się 1987 ; 34 lata temu ( 1987 )
Wersja stabilna
Mandaryński 2017.1 / 24 maja 2017 ; 4 lata temu ( 24.05.2017 )
Dyscyplina pisania dynamiczny , mocny
Licencja Licencja podobna do BSD
Stronie internetowej www .selflanguage .org
Główne wdrożenia
Samego siebie
Wpływem
Smalltalk , APL
Pod wpływem
NewtonScript , JavaScript , Io , Agora , Squeak , Lua , Factor , REBOL

Self to obiektowy język programowania oparty na koncepcji prototypów . Self zaczęło się jako dialekt Smalltalk , dynamicznie typowane i wykorzystujące kompilację just -in-time (JIT), a także podejście do obiektów oparte na prototypach: po raz pierwszy został użyty jako eksperymentalny system testowy do projektowania języka w latach 80. i 90. . W 2006 roku Self było wciąż rozwijane w ramach projektu Klein, który był maszyną wirtualną Self napisaną w całości w Self. Najnowsza wersja to 2017.1 wydana w maju 2017 r.

Kilka technik kompilacji just-in-time zostało wprowadzonych i udoskonalonych w badaniach Self, ponieważ były one wymagane, aby umożliwić językowi zorientowanemu obiektowo bardzo wysokiego poziomu działanie nawet o połowę szybciej niż zoptymalizowane C. Znaczna część rozwoju Self miała miejsce w Słońcu Microsystems i techniki opracowane zostały one później wdrażane dla Java „s HotSpot maszynie wirtualnej .

W pewnym momencie w Self została zaimplementowana wersja Smalltalk. Ponieważ był w stanie użyć JIT, dało to również bardzo dobrą wydajność.

Historia

Self został zaprojektowany głównie przez Davida Ungara i Randalla Smitha w 1986 roku podczas pracy w Xerox PARC . Ich celem było przyspieszenie postępu w dziedzinie badań zorientowanych obiektowo na język programowania, gdy Smalltalk-80 został wydany przez laboratoria i zaczął być poważnie traktowany przez przemysł. Przenieśli się na Uniwersytet Stanforda i kontynuowali pracę nad językiem, budując pierwszy działający kompilator Self w 1987 roku. W tym momencie skupiono się na próbie stworzenia całego systemu dla Self, a nie tylko samego języka.

Pierwsze publiczne wydanie miało miejsce w 1990 roku, a w następnym roku zespół przeniósł się do Sun Microsystems, gdzie kontynuowali prace nad językiem. Pojawiło się kilka nowych wydań, aż w 1995 roku, wraz z wersją 4.0, w dużej mierze przeszło w stan uśpienia. Wersja 4.3 została wydana w 2006 roku i działała na systemach Mac OS X i Solaris . Nowa wersja w 2010 roku, wersja 4.4, została opracowana przez grupę składającą się z części oryginalnego zespołu i niezależnych programistów i jest dostępna dla systemów Mac OS X i Linux , podobnie jak wszystkie kolejne wersje. Uzupełnienie 4.5 zostało wydane w styczniu 2014 r., a trzy lata później wersja 2017.1 została wydana w maju 2017 r.

Self zainspirowało również wiele języków opartych na swoich koncepcjach. Najbardziej godne uwagi, być może, były NewtonScript dla Apple Newton i JavaScript używane we wszystkich nowoczesnych przeglądarkach. Inne przykłady to Io , Lisaac i Agora . IBM Tivoli ramowy system rozproszony obiekt „s, opracowany w 1990 roku, był na najniższym poziomie, system oparty obiektu prototypowego inspirowany Jaźni.

Języki programowania oparte na prototypach

Tradycyjne języki obiektowe oparte na klasach są oparte na głęboko zakorzenionej dwoistości:

  1. Ćwiczenia definiują podstawowe cechy i zachowania przedmiotów.
  2. Instancje obiektów to szczególne przejawy klasy.

Załóżmy na przykład, że obiekty Vehicleklasy mają nazwę i możliwość wykonywania różnych czynności, takich jak dojazd do pracy i dostarczenie materiałów budowlanych . Bob's carjest konkretnym obiektem (instancją) klasy Vehicle, o nazwie „samochód Boba”. Teoretycznie można wtedy wysłać wiadomość do Bob's car, z poleceniem dostarczenia materiałów budowlanych .

Ten przykład pokazuje jeden z problemów związanych z tym podejściem: samochód Boba, który akurat jest samochodem sportowym, nie jest w stanie przewozić i dostarczać materiałów budowlanych (w jakimkolwiek sensownym sensie), ale jest Vehicleto modelowana zdolność . Bardziej użyteczny model wynika z użycia podklas do tworzenia specjalizacji Vehicle; na przykład Sports Cari Flatbed Truck. Tylko obiekty tej klasy Flatbed Truckmuszą posiadać mechanizm dostarczania materiałów budowlanych ; auta sportowe, które nie nadają się do takiej pracy, potrzebują jedynie szybkiej jazdy . Jednak ten głębszy model wymaga większego wglądu podczas projektowania, wglądu, który może ujawnić się dopiero w miarę pojawiania się problemów.

Ta kwestia jest jednym z czynników motywujących do tworzenia prototypów . Dopóki nie można przewidzieć z całą pewnością, jakie cechy zestaw obiektów i klas będzie miał w odległej przyszłości, nie można właściwie zaprojektować hierarchii klas. Zbyt często program wymagałby w końcu dodanych zachowań, a sekcje systemu musiałyby zostać przeprojektowane (lub zrefaktoryzowane ), aby wyodrębnić obiekty w inny sposób. Doświadczenie z wczesnymi językami OO, takimi jak Smalltalk, pokazało, że tego rodzaju problemy pojawiały się raz po raz. Systemy miały tendencję do rozrastania się do pewnego punktu, a następnie stania się bardzo sztywnymi, ponieważ podstawowe klasy głęboko poniżej kodu programisty stawały się po prostu „złe”. Bez możliwości łatwej zmiany oryginalnej klasy mogą pojawić się poważne problemy.

Języki dynamiczne, takie jak Smalltalk, pozwoliły na tego rodzaju zmiany za pomocą dobrze znanych metod w klasach; zmieniając klasę, oparte na niej obiekty zmieniłyby swoje zachowanie. Jednak takie zmiany musiały być wykonane bardzo ostrożnie, ponieważ inne obiekty oparte na tej samej klasie mogą spodziewać się tego „niewłaściwego” zachowania: „niewłaściwe” jest często zależne od kontekstu. (Jest to jedna z postaci problemu z delikatną klasą bazową .) Ponadto, w językach takich jak C++ , gdzie podklasy mogą być kompilowane oddzielnie od nadklas, zmiana w nadklasie może w rzeczywistości zepsuć prekompilowane metody podklas. (Jest to kolejna forma problemu z kruchą klasą bazową, a także jedna z form problemu z kruchym interfejsem binarnym .)

W Self i innych językach opartych na prototypach, dwoistość między klasami i instancjami obiektów jest wyeliminowana.

Zamiast mieć „instancję” obiektu opartego na jakiejś „klasie”, w Self tworzy się kopię istniejącego obiektu i zmienia go. Więc Bob's carbyłyby tworzone przez wykonanie kopii istniejącego obiektu „pojazd”, a następnie dodanie jazdy szybkiej metody modelowania fakt, że dzieje się Porsche 911 . Podstawowe obiekty, które są używane głównie do wykonywania kopii, nazywane są prototypami . Uważa się, że ta technika znacznie upraszcza dynamikę. Jeśli istniejący obiekt (lub zbiór obiektów) okaże się nieodpowiednim modelem, programista może po prostu stworzyć zmodyfikowany obiekt o poprawnym zachowaniu i użyć go zamiast tego. Kod wykorzystujący istniejące obiekty nie ulega zmianie.

Opis

Obiekty Self to zbiór „slotów”. Sloty to metody akcesorów, które zwracają wartości, a umieszczenie dwukropka po nazwie slotu ustawia wartość. Na przykład dla slotu o nazwie „nazwa”,

myPerson name

zwraca wartość w nazwie, a

myPerson name:'foo'

ustawia to.

Self, podobnie jak Smalltalk, używa bloków do kontroli przepływu i innych zadań. Metody są obiektami zawierającymi kod oprócz slotów (których używają dla argumentów i wartości tymczasowych) i mogą być umieszczone w slocie Self, tak jak każdy inny obiekt: na przykład liczbę. Składnia pozostaje taka sama w obu przypadkach.

Zauważ, że w Self nie ma rozróżnienia między polami i metodami: wszystko jest szczeliną. Ponieważ dostęp do slotów za pośrednictwem wiadomości stanowi większość składni w Self, wiele wiadomości jest wysyłanych do „self”, a „self” można pominąć (stąd nazwa).

Podstawowa składnia

Składnia dostępu do slotów jest podobna do tej w Smalltalk. Dostępne są trzy rodzaje wiadomości:

jednoargumentowy
receiver slot_name
dwójkowy
receiver + argument
słowo kluczowe
receiver keyword: arg1 With: arg2

Wszystkie komunikaty zwracają wyniki, więc odbiorca (jeśli jest obecny) i argumenty mogą same być wynikiem innych komunikatów. Po komunikacie kropka oznacza, że ​​Self odrzuci zwróconą wartość. Na przykład:

'Hello, World!' print.

To jest wersja Self programu Hello World . 'Składnia wskazuje dosłowne obiekt string. Inne literały to liczby, bloki i obiekty ogólne.

Grupowanie można wymusić za pomocą nawiasów. W przypadku braku wyraźnego grupowania, wiadomości jednoargumentowe są uważane za mające najwyższy priorytet, po których następują binarne (grupowanie od lewej do prawej), a słowa kluczowe mają najniższy priorytet. Użycie słów kluczowych do przypisania prowadziłoby do dodatkowego nawiasu, w którym wyrażenia miały również komunikaty słów kluczowych, więc aby uniknąć tego, że Self wymaga, aby pierwsza część selektora komunikatów słów kluczowych zaczynała się od małej litery, a kolejne części zaczynały się od wielkiej litery.

valid: base bottom
          between: ligature bottom + height
          And: base top / scale factor.

może być analizowany jednoznacznie i oznacza to samo co:

valid: ((base bottom)
            between: ((ligature bottom) + height)
            And: ((base top) / (scale factor))).

W Smalltalk-80 to samo wyrażenie wyglądałoby na napisane jako:

valid := self base bottom
             between: self ligature bottom + self height
             and: self base top / self scale factor.

przy założeniu base, ligature, heighti scalenie były zmienne instancji z self, ale były w rzeczywistości sposoby.

Tworzenie nowych obiektów

Rozważ nieco bardziej złożony przykład:

labelWidget copy label: 'Hello, World!'.

tworzy kopię obiektu "labelWidget" z wiadomością kopiowania (tym razem bez skrótu), a następnie wysyła wiadomość, aby umieścić "Hello, World" w slocie o nazwie "label". Teraz coś z tym zrobić:

(desktop activeWindow) draw: (labelWidget copy label: 'Hello, World!').

W tym przypadku (desktop activeWindow)wykonywane jest najpierw, zwracając aktywne okno z listy okien, o których wie obiekt pulpitu. Następnie (odczytaj wewnętrzną do zewnętrznej, od lewej do prawej) kod, który zbadaliśmy wcześniej, zwraca etykietęWidget. Na koniec widżet jest wysyłany do miejsca rysowania aktywnego okna.

Delegacja

Teoretycznie każdy obiekt Self jest samodzielną jednostką. Self nie ma ani klas, ani meta-klas. Zmiany w konkretnym obiekcie nie wpływają na żaden inny, ale w niektórych przypadkach pożądane jest, aby tak się stało. Zwykle obiekt może rozumieć tylko komunikaty odpowiadające jego lokalnym gniazdom, ale mając jeden lub więcej miejsc wskazujących obiekty nadrzędne , obiekt może delegować dowolny komunikat, którego sam nie rozumie, do obiektu nadrzędnego. Każdy slot może być wskaźnikiem nadrzędnym, dodając gwiazdkę jako przyrostek. W ten sposób Self radzi sobie z obowiązkami, które wykorzystywałyby dziedziczenie w językach opartych na klasach. Delegacji można również użyć do zaimplementowania funkcji, takich jak przestrzenie nazw i zakresy leksykalne .

Załóżmy na przykład, że zdefiniowano obiekt o nazwie „konto bankowe”, który jest używany w prostej aplikacji księgowej. Zwykle ten obiekt byłby tworzony za pomocą metod znajdujących się w środku, być może "depozyt" i "wypłata", oraz wszelkich wymaganych przez nie slotów danych. Jest to prototyp, który jest wyjątkowy tylko pod względem sposobu użytkowania, ponieważ jest to również w pełni funkcjonalne konto bankowe.

Cechy

Stworzenie klonu tego obiektu dla „konta Boba” spowoduje utworzenie nowego obiektu, który zaczyna się dokładnie tak, jak prototyp. W tym przypadku skopiowaliśmy sloty, w tym metody i wszelkie dane. Jednak bardziej powszechnym rozwiązaniem jest najpierw stworzenie prostszego obiektu zwanego obiektem cech, który zawiera elementy, które normalnie kojarzyłoby się z klasą.

W tym przykładzie obiekt „konto bankowe” nie miałby metody wpłaty i wypłaty, ale miałby obiekt nadrzędny, który to robi. W ten sposób można wykonać wiele kopii obiektu konta bankowego, ale nadal możemy zmienić ich zachowanie, zmieniając gniazda w tym obiekcie głównym.

Czym to się różni od tradycyjnych zajęć? Zastanów się nad znaczeniem:

myObject parent: someOtherObject.

Ten fragment zmienia „klasę” myObject w czasie wykonywania, zmieniając wartość powiązaną z gniazdem „rodzic*” (gwiazdka jest częścią nazwy gniazda, ale nie odpowiada jej komunikatom). W przeciwieństwie do dziedziczenia lub zakresu leksykalnego obiekt delegata można modyfikować w czasie wykonywania.

Dodawanie slotów

Obiekty w sobie można modyfikować, aby zawierały dodatkowe miejsca. Można to zrobić za pomocą graficznego środowiska programistycznego lub za pomocą prymitywnego '_AddSlots:'. Prymitywny ma taką samą składnię jak zwykła wiadomość słów kluczowych, ale jego nazwa zaczyna się od podkreślenia charakteru. Należy unikać operacji podstawowej _AddSlots, ponieważ jest pozostałością po wczesnych implementacjach. Jednak pokażemy to w poniższym przykładzie, ponieważ skraca to kod.

Wcześniejszy przykład dotyczył refaktoryzacji prostej klasy o nazwie Vehicle, aby móc rozróżnić zachowanie samochodów i ciężarówek. W Jaźni można to osiągnąć za pomocą czegoś takiego:

_AddSlots: (| vehicle <- (|parent* = traits clonable|) |).

Ponieważ odbiorca prymitywu '_AddSlots:' nie jest wskazany, jest to "self". W przypadku wyrażeń wpisywanych przy znaku zachęty jest to obiekt zwany „lobby”. Argumentem dla '_AddSlots:' jest obiekt, którego sloty zostaną skopiowane do odbiornika. W tym przypadku jest to obiekt dosłowny z dokładnie jednym miejscem. Nazwa slotu to 'vehicle', a jego wartość to kolejny dosłowny obiekt. Notacja "<-" implikuje drugi slot zwany 'vehicle:', który może być użyty do zmiany wartości pierwszego slotu.

Znak „=” wskazuje stały slot, więc nie ma odpowiadającego elementu „rodzic:”. Dosłowny obiekt, który jest początkową wartością „pojazdu”, zawiera jeden slot, dzięki czemu może zrozumieć komunikaty związane z klonowaniem. Naprawdę pusty obiekt, oznaczony jako (| |) lub prościej jako (), nie może w ogóle odbierać żadnych wiadomości.

vehicle _AddSlots: (| name <- 'automobile'|).

Tutaj odbiornikiem jest poprzedni obiekt, który teraz będzie zawierał pola 'name' i 'name:' oprócz 'parent*'.

_AddSlots: (| sportsCar <- vehicle copy |).
sportsCar _AddSlots: (| driveToWork = (''some code, this is a method'') |).

Chociaż wcześniej „pojazd” i „samochód sportowy” były dokładnie takie same, teraz ten drugi zawiera nowy slot z metodą, której nie ma w oryginale. Metody można umieszczać tylko w stałych slotach.

_AddSlots: (| porsche911 <- sportsCar copy |).
porsche911 name:'Bobs Porsche'.

Nowy obiekt „porsche911” zaczął się dokładnie tak samo jak „sportsCar”, ale ostatnia wiadomość zmieniła wartość jego slotu „name”. Zwróć uwagę, że oba nadal mają dokładnie te same gniazda, nawet jeśli jeden z nich ma inną wartość.

Środowisko

Jedną z cech Self jest to, że opiera się na tym samym rodzaju systemu maszyn wirtualnych , z którego korzystały wcześniejsze systemy Smalltalk. Oznacza to, że programy nie są samodzielnymi jednostkami, jak w językach takich jak C , ale potrzebują całego środowiska pamięci do działania. Wymaga to, aby aplikacje były dostarczane w kawałkach zapisanej pamięci, zwanych migawkami lub obrazami . Jedną z wad tego podejścia jest to, że obrazy są czasami duże i nieporęczne; jednak debugowanie obrazu jest często prostsze niż debugowanie tradycyjnych programów, ponieważ stan środowiska wykonawczego jest łatwiejszy do sprawdzenia i modyfikacji. (Różnica między programowaniem opartym na źródle a programowaniem opartym na obrazach jest analogiczna do różnicy między programowaniem opartym na klasach a prototypowym programowaniem obiektowym.)

Dodatkowo środowisko jest dostosowane do szybkiej i ciągłej zmiany obiektów w systemie. Refaktoryzacja projektu „klasy” jest tak prosta, jak przeciąganie metod z istniejących przodków do nowych. Proste zadania, takie jak metody testowe, można wykonać, tworząc kopię, przeciągając metodę do kopii, a następnie ją zmieniając. W przeciwieństwie do tradycyjnych systemów, tylko zmieniony obiekt ma nowy kod i nie trzeba nic przebudowywać, aby go przetestować. Jeśli metoda działa, można ją po prostu przeciągnąć z powrotem do przodka.

Występ

Własne maszyny wirtualne osiągnęły w niektórych testach wydajność o połowę mniejszą od zoptymalizowanego C.

Udało się to osiągnąć dzięki technikom kompilacji just-in-time , które były pionierskie i ulepszone w Self-badaniach, aby język wysokiego poziomu działał dobrze.

Zbieranie śmieci

Śmieciarza do samodzielnego korzysta pokoleń zbieranie śmieci , które segreguje przedmioty od wieku. Używając systemu zarządzania pamięcią do rejestrowania zapisów stron, można utrzymać barierę zapisu. Ta technika zapewnia doskonałą wydajność, chociaż po pewnym czasie działania może nastąpić pełne odśmiecanie, które zajmuje dużo czasu.

Optymalizacje

System czasu wykonywania selektywnie spłaszcza struktury wywołań. Daje to niewielkie przyspieszenie samo w sobie, ale umożliwia obszerne buforowanie informacji o typie i wiele wersji kodu dla różnych typów wywołujących. Eliminuje to potrzebę wykonywania wielu wyszukiwań metod i pozwala na wstawianie warunkowych instrukcji rozgałęzień i zakodowanych wywołań - często dając wydajność podobną do C bez utraty ogólności na poziomie języka, ale w systemie w pełni gromadzącym śmieci.

Zobacz też

Bibliografia

Dalsza lektura

Linki zewnętrzne