Wielkie umysły programowania. Jak myślą i pracują twórcy najważniejszych języków 9788324658664

W książce znajdziesz wywiady z autorami takich języków, jak: C++ Python APL Forth BASIC AWK Lua Haskell ML SQL Java C#

1,238 215 12MB

Polish Pages [578] Year 2010

Report DMCA / Copyright

DOWNLOAD FILE

Polecaj historie

Wielkie umysły programowania. Jak myślą i pracują twórcy najważniejszych języków
 9788324658664

Citation preview

POZNAJ Z BLISKA NAJKIEKSZE AUTORYTETY ŚWIATA INFORMATYKI! JAK POWSTAJĄ JĘZYKI PROGRAMOWANIA? / JAKA JEST ICH PRZYSZtOŚĆ? / JAK SZYBKO NAUCZYĆ S IĘ TAKIEGO JĘZYKA?

Wielkie umysły programowania Jak myślą i pracują twórcy najważniejszych języków

o

asieluałt if*

Federico Biancuzzi Shane Warden

H e lio n

OREILLY’

Przedmowa: Sir Tony Hoare

SPI S T REŚCI

SŁOWO WSTĘPNE

7

PRZEDMOWA

9

C+ +

13

Bjarne Stroustrup Decyzje projektowe

14

Używanie języka

19

Programowanie obiektowe i współbieżność

24

Przyszłość

29

Edukacja

33

PYTHON

37

Guido van Rossum Pythonowy styl

38

Dobry programista

47

Wiele wersji Pythona

53

Rozwiązania praktyczne i doświadczenie

59

APL

65 Adin D. Falkoff

Papier i ołówek

66

Podstawowe zasady

69

Współbieżność

76

Klasyka

80

FORTH

85

Charles H. Moore Język Forth a projektowanie języków

86

Sprzęt

95

Projektowanie aplikacji

10 0

BASIC

109

Thomas E. Kurtz Cele języka BASIC

11 0

Projektowanie kompilatorów

11 8

Język i praktyki programistyczne

122

Projekt języka

124

Cele pracy

13 0

3

6

AW K

135

Alfred V. Aho, Peter Weinberger i Brian Kernighan

7

Życie algorytm ów

136

Projekt języka

138

Unix i jego kultura

142

Rola dokumentacji

147

Informatyka

152

Hodowla niewielkich języków

154

Projektowanie nowego języka

16 0

Kultura tradycji

17 0

Technologie transformacji

17 4

Rzeczy, które zm ieniły wszechświat

179

Teoria i praktyka

187

Oczekiwanie na przełom

195

Programowanie przez przykład

201

LUA

207

Luiz Henrique de Figueiredo i Roberto lerusalimschy Siła skryptów

8

208

Doświadczenie

212

Projekt języka

21 7

HASKELL

227

Simon Peyton Jones, Paul Hudak, Philip Wadler i John Hughes Zespół języka funkcyjnego Trajektoria programowania funkcyjnego Język Haskell Nauczanie programowania (funkcyjnego)

9

228 231 23 9 247

Formalizm i ewolucja

249

ML

257 Robin Milner

10

Dowodzenie twierdzeń

258

Teoria znaczenia

268

Wykraczając poza informatykę

27 5

SQL

28 3 Don Chamberlin

4

SPIS

TREŚCI

W ażny dokum ent

284

Język

28 7

Uwagi i ewolucja języka

292

XQuery i XM L

299

11

OBJECTIVE-C

30 3

Brad Cox i Tom Love Inżynieria języka Objective-C

304

Rozwój języka

307

Edukacja i szkolenie

31 2

Zarządzanie projektem i oprogramowanie

12

odziedziczone

315

Język Objective-C i inne języki

323

Składniki, piasek i cegły

32 9

Jakość jako zjawisko ekonomiczne

33 7

Edukacja

340

JAVA

345

James Gosling

13

Siła prostoty

34 6

Rzecz gustu

350

Współbieżność

35 4

Projektowanie języka

35 6

Pętla sprzężenia zwrotnego

362

C#

365 Anders Hejlsberg

14

Język i jego projekt

36 6

Rozwój języka

37 3

C#

378

Przyszłość informatyki

38 5

UML

391

lvar Jacobson, James Rumbaugh i Grady Booch Uczenie się i nauczanie

392

Czynnik ludzki

39 9

UML

403

Wiedza

408

Przygotuj się na zmiany

411

Korzystanie z UML

417

W arstwy i języki

423

Trochę o wielokrotnym wykorzystywaniu

428

Relacje symetryczne

434

UML

438

Projekt języka

442

Szkolenie programistów

449

Kreatywność, udoskonalanie i wzorce

451

SPIS

TREŚCI

5

15

PERL

461

Larry Wall

16

Język rewolucji

462

Język

467

Społeczność

474

Ewolucja i rewolucja

478

POSTSCRIPT

485

Charles Geschke, John E. Warnock Zaprojektowany po to, żeby istnieć

17

486

Badania i edukacja

497

Interfejsy do długowieczności

502

Standardowe życzenia

507

EIFFEL

511

Bertrand Meyer Owocne popołudnie

512

Wielokrotne wykorzystywanie kodu i generyczność

6

SPIS

TREŚCI

521

Szlifowanie języków

52 6

Zarządzanie wzrostem i ewolucją

53 4

POSŁOWIE

541

WSPÓŁTWÓRCY

543

SKOROWIDZ

561

Słowo wstępne

P r o j e k t o w a n i e j ę z y k ó w p r o g r a m o w a n i a t o w s p a n i a ł y t e m a t . Istnieje bardzo wielu programistów, którzy uważają, że potrafią zaprojektować lepszy język programowania od tego, którego aktualnie używają. Istnieje również bardzo wielu naukow ców, którzy wierzą, że potrafią zaprojektow ać lepszy język program ow ania od jakiegokolwiek innego języka będącego w użyciu. Ich osądy są często uzasadnione, ale tylko kilka takich projektów opuściło dolną szufladę projektanta. O takich językach Czytelnik nie znajdzie informacji w tej książce.

Projektowanie języków program ow ania to poważny biznes. Niewielkie błędy w projekcie języka mogą być przyczyną dużych błędów w ostatecznym programie, a nawet niewielkie błędy w programach mogą mieć poważne konsekwencje i wiązać się z bardzo dużymi kosztami. Wrażliwe punkty powszechnie wykorzystywanego oprogram ow ania często umożliwiały ataki za pośrednictw em złośliwego oprogram owania. Czasami pow odow ało to w gospodarce światowej straty rzędu wielu miliardów dolarów. Bezpieczeństwo i zabezpieczenia języków programowania to motyw, który bardzo często pojawia się w niniejszej książce. Projektowanie języków program ow ania to nieprzewidyw alna przygoda. Języki projektowane pod kątem tworzenia uniwersalnych aplikacji, nawet jeśli są wspierane i sponsorow ane przez potężne organizacje, czasami kończą na rynku niszowym.

Z drugiej strony języki projektow ane z myślą o ograniczonych lub lokalnych zastosowaniach czasami zyskują liczną klientelę — także w środowiskach i aplikacjach, o których ich projektanci nigdy nawet nie marzyli. Niniejsza książka koncentruje się na językach tego drugiego typu. Języki, które odniosły sukces, mają jedną istotną cechę: każdy z nich powstał w głowie jednego człowieka lub jest efektem pracy małej grupy podobnie myślących entuzjastów. Projektanci tych języków to mistrzowie programowania. Mają doświadczenie, wizję, energię, wytrwałość oraz prawdziwy geniusz. Cechy te pozwalają im przeprowadzić język od wstępnej im plem entacji, poprzez ewolucję wynikającą z doświadczeń, aż do standaryzacji będącej efektem w ykorzystywania (standardy de facto) oraz przeprowadzanej oficjalnie (przez komitety standaryzacyjne). W niniejszej książce Czytelnik spotka się z wieloma geniuszami. Z każdym z nich został przeprowadzony obszerny wywiad na tem at historii języka oraz czynników, które wpłynęły na jego sukces. Wywiady te potwierdzają istotną rolę dobrych decyzji połączonych ze szczęściem. Publikacja słów wypowiadanych w wywiadzie pozwoli Czytelnikowi ocenić osobowość oraz motywacje projektantów. Jest to temat równie fascynujący jak sam projekt języka. — Sir Tony Hoare

Sir Tony Hoare, zdobywca nagrody Turinga stowarzyszenia ACM oraz nagrody Kyoto Award, od 50 lat jest liderem badań nad algorytmami komputerowymi oraz językami programowania. W swoim pierwszym akademickim artykule, który został opublikowany w 1969 roku, opisał ideę dowodzenia poprawności programów i zasugerował, aby celem projektu języka programowania było ułatwienie pisania poprawnych programów. Jest zachwycony tym, że idea ta została stopniowo zaakceptowana przez projektantów języków programowania.

8

SŁOWO

WSTĘPNE

Przedmowa

P is a n ie o p r o g r a m o w a n i a n ie n a l e ż y d o ł a t w y c h z a ję ć .

A już n a

p e w n o n ie j e s t ł a t w e

Oraz różnych środowisk. W ciągu ostatnich pięciu dekad w branży inżynierii oprogramowania nie tylko czyniono wysiłki, aby pisanie oprogram ow ania staw ało się coraz łatwiejsze, ale także projektow ano języki pod tym kątem. Dlaczego jednak pisanie oprogram ow ania w ogóle jest trudne? PISA N IE P R O G R A M Ó W , KTÓRE SPE Ł N IA JĄ W Y M A G A N IA T E S T Ó W , CZASU

W większości książek i artykułów zajmujących się tym problem em mówi się o architekturze, wym aganiach oraz podobnych zagadnieniach związanych z oprogramowaniem. A co, jeśli cała trudność polega na pisaniu? Mówiąc inaczej, zastanówmy się, jakby wyglądał problem, gdyby programiści postrzegali swoją pracę bardziej w kategoriach komunikacji — języka — a mniej w kategoriach inżynierii. Dzieci uczą się mówić przez pierwsze lata żyda, natomiast czytania i pisania zaczynają się uczyć dopiero wtedy, gdy skończą pięć, sześć lat. Nie znam żadnego wielkiego autora, który nauczył się czytać i pisać jako dorosły. Czy znacie jakiegoś programistę, który nauczył się programowania w zaawansowanym wieku? Jeśli dzieci znacznie łatwiej uczą się obcych języków niż dorośli, to co powiedzieć o uczeniu się programowania — czynności wymagającej poznawania nowego języka?

9

Wyobraźmy sobie, że uczymy się obcego języka i nie znamy nazwy jakiegoś przedmiotu. Potrafimy opisać go słowami, które znamy, z nadzieją, że nasz rozmówca zrozumie, co mamy na myśli. Czy to nie jest to samo, co robimy codziennie, pisząc programy? Opisujemy obiekt, który mamy na myśli, za pomocą języka programowania w nadziei, że nasz opis będzie wystarczająco czytelny dla kom pilatora bądź interpretera. Jeśli coś nie zadziała, jeszcze raz przywołujemy obraz obiektu i próbujem y zrozumieć, co pominęliśmy lub co opisaliśmy niewystarczająco dokładnie. Świadomy tych pytań postanow iłem przeprowadzić szereg badań na tem at tego, po co tworzy się języki programowania, w jaki sposób technicznie się je opracowuje, jak się ich naucza, jak są przyswajane oraz jak rozwijają się z biegiem lat. Shane i ja mieliśmy ho nor porozm aw iania z 27 wielkimi projektantam i języków. Osoby te przeprowadziły nas przez swoją pracę. Staraliśmy się zebrać ich wiedzę i doświadczenie, a następnie przedstawić je Czytelnikowi. W książce Masterminds o f Programming Czytelnik zapozna się z pewnymi schematami myślenia i czynnościami w ym aganym i do stworzenia sprawnego języka program ow ania. Dowie się, co wpływa na to, że język staje się popularny, oraz zapozna ze sposobami rozwiązywania bieżących problemów, z jakimi spotykają się programiści używający tego języka. Jeśli zatem Czytelnik chce się dowiedzieć, w jaki sposób projektować języki programowania zdolne do osiągnięcia sukcesu, ta książka z pewnością będzie mu pomocna. Osoby poszukujące inspiracji dotyczących oprogramowania i języków programowania będą potrzebowały kolorowego markera do zaznaczania interesujących fragmentów. Obiecuję, że na kartach niniejszej książki znajdą wiele informacji godnych zaznaczenia. — Federico Biancuzzi

Organizacja materiału Rozdziały w niniejszej książce uporządkow ano w taki sposób, aby przekazywać Czytelnikom różne poglądy i prowokować. Polecamy smakować poszczególne wywiady i często do nich powracać. Rozdział 1., „C++”, wywiad z Bjarne’em Stroustrupem. Rozdział 2., „Python”, wywiad z Guidem van Rossumem. Rozdział 3., „APL”, wywiad z Adinem D. Falkoffem. Rozdział 4., „Forth”, wywiad z Charlesem H. Moore’em. Rozdział 5., „BASIC”, wywiad z Thomasem E. Kurtzem. Rozdział 6., „AWK”, wywiad z Alfredem Aho, Peterem Weinbergerem i Brianem Kernighanem.

10

PRZEDMOWA

Rozdział 7., „Lua” , wywiad z Luizem Henriąue de Figueiredo i Robertem Ierusalimschym. Rozdział 8., „Haskell”, wywiad z Simonem Peytonem Jonesem, Paulem Hudakiem, Philipem Wadlerem i Johnem Hughesem. Rozdział 9., „ML”, wywiad z Robinem Milnerem. Rozdział 10., „SQL”, wywiad z Donem Chamberlinem. Rozdział 11., „Objective-C” , wywiad z Tomem Love’em i Bradem Coksem. Rozdział 12., ,Java”, wywiad z Jamesem Goslingiem. Rozdział 13., „C#”, wywiad z Andersem Hejlsbergiem. Rozdział 14., „UML” , wywiad z Ivarem Jacobsonem, Jamesem Rumbaughem i Gradym Boochem. Rozdział 15., „Perl”, wywiad z Larrym Wallem. Rozdział 16., „PostScript”, wywiad z Charlesem Geschkem i Johnem Warnockiem. Rozdział 17., „Eiffel” , wywiad z Bertrandem Meyerem. W dodatku „W spółtwórcy” zamieszczono biografie wszystkich rozmówców.

Konwencje stosowane w książce W niniejszej książce zastosowano następujące konwencje typograficzne: Kursywa Oznacza nowe pojęcia, adresy URL, nazwy plików i narzędzi.

Czcionka o stałej szerokości Oznacza zawartość plików kom puterow ych oraz — ogólnie rzecz biorąc — wszystkiego, co można znaleźć w programach.

PRZEDMOWA

tt

P R Z E D M O W A

ROZDZIAŁ

PIERWSZY

C+ +

C+ + zajm uje interesujące miejsce wśród języków: jest zbudowany na bazie języka C, im plem entuje mechanizmy obiektowe pochodzące z języka Simula, jest ustandaryzowany przez ISO oraz zaprojektow any według dwóch idei: „nie płacisz za to, czego nie używasz" oraz „typy definiowane przez użytkownika powinny być obsługiwane na równi z wbudowanym i". Chociaż język ten został spopularyzowany w latach osiemdziesiątych i dziewięćdziesiątych jako narzędzie programowania obiektowego używane do tworzenia programów z graficznym interfejsem użytkownika (GUI), to jedną z jego największych zasług na polu rozwoju oprogramowania są techniki programowania generycznego udostępnione w bibliotece STL (ang. Standard Template Library). Próbowano zastąpić język C + + nowszymi językam i, takim i ja k Java czy C#, tymczasem do kolejnej wersji standardu C+ + dodano nowe, długo oczekiwane własności. Twórcą języka C+ + i jednym z jego gorących orędowników jest Bjarne Stroustrup.

13

Decyzje projektowe Dlaczego zdecydował się pan rozszerzyć istniejący język, zamiast stworzyć nowy?

Bjarne Stroustrup: Kiedy zaczynałem — w 1979 roku — moim celem była pomoc program istom w budow aniu systemów. W dalszym ciągu przyświeca mi taki cel. Aby język programowania stanowił prawdziwą pomoc w rozwiązywaniu problemów, a nie był tylko akademickim ćwiczeniem, musi być kompletny w zakresie narzędzi do tw orzenia aplikacji. A zatem potrzebny jest prosty język do rozwiązywania problemów. Problemy, które starałem się rozwiązać, dotyczyły projektowania systemów operacyjnych, obsługi sieci i symulacji. Ja i moi koledzy potrzebowaliśmy języka, za pomocą którego można by prezentować organizację programu, tak jak to można robić w języku Simula (potrzebne nam zatem były techniki obiektowe). Jednocześnie potrzebow aliśm y takiego języka, za pom ocą którego m ożna pisać w ydajny kod niskopoziomowy — tak jak w języku C. W 1979 roku nie było języka, który posiadałby obie te cechy, a przynajmniej ja takiego nie znałem. W łaściwie nie chciałem projektow ać nowego języka programowania. Chciałem tylko pomóc w rozwiązaniu kilku problemów. Bazowanie na istniejącym języku programowania ma bowiem sens. Z języka bazowego bierzemy podstawową strukturę składni i semantyki, wykorzystujemy z niego użyteczne biblioteki i stajemy się częścią kultury. Gdybym nie wykorzystał języka C, oparłbym język C++ na jakimś innym języku. Dlaczego C? Miałem Dennisa Ritchiego, Briana K ernighana i innych wielkich tw órców Uniksa w odległości piętra lub pokoju w Computer Science Research Center — Bell Labs, a zatem pytanie to może wydawać się bezzasadne. Ja jednak potraktowałem je zupełnie serio. W szczególności system typów języka C był nieformalny i niezbyt dobrze przestrzegany (jak powiedział Dennis Ritchie: „C jest językiem o ścisłej typizacji (ang. strongly typed) i słabo kontrolowanych typach”). Cecha „słabo kontrolowane” mnie martwiła, zresztą do dziś sprawia ona programistom języka C++ wiele problemów. Język C nie był wtedy powszechnie używany, tak jak to się dzieje dziś. Oparcie języka C++ na języku C było wyrazem wiary w m odel przetw arzania będący podstaw ą języka C (cecha „ścisła typizacja” ) oraz wyrazem zaufania do m oich kolegów. W yboru dokonałem na podstaw ie wiedzy na tem at większości języków wyższego poziom u wykorzystywanych w tamtych czasach do tworzenia systemów (znałem je zarówno jako użytkownik, jak i praktykujący programista). W arto pamiętać, że był to czas, w którym większość prac „blisko sprzętu” oraz wymagających dobrej wydajności było w ykonywanych w asemblerze. Powstanie systemu Unix stanow iło poważny przełom pod wieloma względami — jednym z nich było użycie języka C naw et do najbardziej wymagających zadań programowania systemowego. W związku z tym wybrałem wykorzystywany w języku C prosty model maszyny, który uznałem za bardziej wartościową cechę od lepszej kontroli typów występującej w innych językach. Tym, czego naprawdę potrzebowałem jako ramy (ang. framework)

14

ROZDZIAŁ

PI E R W S Z Y

do tw orzenia program ów , były klasy dostępne w Simuli. Dlatego przeniosłem je na model pamięci i przetw arzania właściwy językowi C. W efekcie pow stało niezwykle ekspresywne i elastyczne narzędzie, które pod względem szybkości działania m ogło rywalizować z asemblerem i nie wym agało stosow ania rozbudow anego środowiska wykonawczego. Dlaczego zdecydował się pan na wspieranie wielu paradygmatów?

Bjarne: Ponieważ kombinacja stylów programowania często prowadzi do stworzenia lepszego kodu, przy czym „lepszy” oznacza kod, który w bardziej bezpośredni sposób odzwierciedla projekt, działa szybciej, jest łatwiejszy w zarządzaniu itp. Dążąc do tw orzenia lepszego kodu, ludzie albo próbują zdefiniować swój ulubiony styl program ow ania zawierający wszystkie użyteczne konstrukcje (na przykład „program ow anie generyczne jest po prostu formą programowania obiektowego”), albo wyłączają pewne obszary aplikacji (na przykład zakładają, że „każdy ma maszynę z zegarem 1GHz i pamięcią 1 GB”). Język Java koncentruje się wyłącznie na programowaniu obiektowym. Czy to powoduje, że kod Javy jest w pewnych przypadkach — tam, gdzie w języku C+ + można skorzystać z programowania generycznego — bardziej złożony?

Bjarne: No cóż, projektanci Javy — a może raczej osoby zajmujące się marketingiem — promowali programowanie obiektowe do granic absurdu. Kiedy Java pojawiła się po raz pierwszy, a jej twórcy podkreślali czystość i prostotę tego kodu, przewidziałem, że w przypadku sukcesu Java znacznie się rozrośnie i wzrośnie jej złożoność. Tak też się stało. I tak wykorzystanie rzutow ania do konwersji z typu Object podczas pobierania wartości z kontenera (na przykład (Apple)c.get(i )) jest absurdalną konsekwencją braku możliwości wyspecyfikowania typu, jaki powinny mieć obiekty w kontenerze. To sposób rozwlekły i nieefektywny. Teraz Java ma typy generyczne, zatem jest tylko nieco wolniejsza. Innymi przykładami zwiększonej złożoności języka (w celu pomocy programistom) są typy wyliczeniowe (enumeracje), odbicia oraz klasy wewnętrzne. Faktem jest, że złożoność musi się gdzieś pojawić. Jeśli nie ma jej w definicji języka, to będzie w niezliczonych aplikacjach i bibliotekach. Na podobnej zasadzie obsesja, by w Javie każdy algorytm (operację) implementować w postaci klasy, prowadzi do absurdów — na przykład klas bez danych, które składają się w całości z funkcji statycznych. Istnieją powody, dla których w matematyce stosuje się zapisy f(x) i f(x.y) zamiast x .f ( ), x.f(y) czy też (x .y ).f ( ) — zapis (x.y) , f ( ) jest próbą wyrażenia idei „prawdziwie obiektowego sposobu” przedstawiania dwóch argumentów oraz uniknięcia asymetrii właściwej dla zapisu x. f(y). Dzięki połączeniu abstrakcji danych i technik programowania generycznego język C++ rozwiązuje wiele problemów dotyczących logiki i notacji wynikających z podejścia obiektowego. Klasycznym przykładem jest zapis vector, w którym T może być

dowolnym typem możliwym do kopiowania — włącznie z typami wbudowanymi, wskaźnikami na hierarchie obiektowe oraz typami definiowanymi przez użytkowników — na przykład ciągami znaków i liczbami zespolonymi. Wszystko to osiągnięto bez dodatkowych kosztów w fazie działania, nakładania ograniczeń na rozmieszczenie danych w pamięci czy też stosowania specjalnych reguł dotyczących standardowych kom ponentów bibliotecznych. Innym przykładem, który nie pasuje do klasycznego m odelu pojedynczej dyspozycji (ang. single dispatch) charakterystycznego dla program ow ania obiektowego, jest operacja wymagająca dostępu do dwóch klas, na przykład operator*(Macierz .Wektor). Operacja ta naturalnie nie może być metodą żadnej z klas. Jedną z podstawowych różnic pomiędzy językami C+ + i Javq jest sposób implementacji wskaźników. W pewnym sensie można powiedzieć, że język Java nie posiada prawdziwych wskaźników. Jakie różnice występują pomiędzy tymi dwoma podejściami?

Bjame: Cóż, w języku Java oczywiście są wskaźniki. W rzeczywistości niemal wszystko w Javie to niejaw ne wskaźniki. Tyle że nazw ano je referencjami. Niejawność wskaźników ma zarówno zalety, jak i wady. Podobnie istnieją zarówno zalety, jak i wady rzeczywistego występowania lokalnych obiektów (tak jak w C++). Podejście zastosow ane w C++, polegające na w spieraniu zm iennych lokalnych alokowanych na stosie oraz rzeczywistych zmiennych członkowskich dowolnego typu, gwarantuje wygodną jednolitą semantykę, kom paktowy układ i minimalne koszty dostępu oraz stanowi podstawę dla obsługi ogólnego zarządzania zasobami w języku C++. To jest bardzo ważne, a wszechobecne i niejaw ne stosowanie w skaźników w Javie (zwanych tam referencjami) zamyka drzwi do wszystkich tych mechanizmów. Przeanalizujmy sprawy związane z rozmieszczeniem danych w pamięci. W języku C++ konstrukcja vector(10) jest reprezentowana jako uchwyt do tablicy 10 liczb zespolonych w wolnej pamięci. W sumie zajmuje ona 25 słów: 3 słowa na wektor plus 20 słów na liczby zespolone oraz dodatkow o 2 słowa na nagłów ek tablicy w wolnej pamięci (stercie). O dpow iednik w Javie (dla zdefiniowanego przez użytkownika kontenera zawierającego obiekty typów niestandardowych) zająłby 56 słów: 1 na referencję do kontenera plus 3 na kontener plus 10 na referencje do obiektów plus 20 na obiekty plus 24 na przechowywane w wolnej pamięci nagłówki 12 niezależnie alokowanych obiektów. Oczywiście te liczby są przybliżone, ponieważ koszty obsługi wolnej pamięci (sterty) są zdefiniowane w implementacji obu języków. Konkluzja jest jednak oczywista: przez wszechobecne i niejawne stosowanie referencji w Javie uproszczono model programowania i implementację mechanizmu odśmiecania (ang. garbage collector), ale jednocześnie dramatycznie zwiększono koszty pamięciowe, podwyższono koszty dostępu do pamięci (z powodu większej liczby pośrednich operacji dostępu) oraz proporcjonalnie zwiększono koszty alokacji.

16

ROZDZIAŁ

PI E R W S Z Y

Tym, czego Java nie m a — i dobrze dla Javy — jest możliwość niepraw idłow ego używania wskaźników w działaniach arytmetycznych na wskaźnikach. Jednak dobrze napisanego kodu w C++ ten problem i tak nie dotyka: zamiast wykonywać działania na wskaźnikach, programiści używają bardziej wysokopoziomowych abstrakcji, takich jak strumienie wejścia-wyjścia, kontenery, lub stosują zaawansowane algorytmy. W zasadzie wszystkie tablice — to samo dotyczy większości wskaźników — pozostają ukryte głęboko w implementacjach, czyli w miejscach, których większość programistów nie musi widzieć. Niestety, istnieje również wiele źle napisanego i niepotrzebnie niskopoziomowego kodu w języku C++. Jest jednak istotne miejsce, w którym wskaźniki i działania na nich są wielką zaletą: bezpośrednie i w ydajne prezentow anie struktur danych. Referencje Javy nie dają takich samych możliwości — na przykład w Javie nie można przedstawić operacji wymiany. Inny przykład to użycie wskaźników do niskopoziomowego bezpośredniego dostępu do pamięci (fizycznej). W każdym systemie trzeba to robić w jakimś języku i często tym językiem jest C++. Z występowaniem wskaźników (i tablic w stylu języka C) wiąże się oczywiście ryzyko niewłaściwego wykorzystania: przepełnienia bufora, użycia wskaźników w odniesieniu do usuniętej pamięci, wystąpienia wskaźników niezainicjowanych itp. Jednak w dobrze napisanym kodzie C++ nie stanowi to wielkiego problemu. Problem ten po prostu nie występuje w przypadku wskaźników i tablic wykorzystywanych wewnątrz abstrakcji (na przykład vector, string, map itp.). Zarządzanie zasobami z wykorzystaniem zasięgów (ang. scope) załatwia większość potrzeb. Pozostałe problemy można rozwiązać dzięki zastosowaniu inteligentnych wskaźników i specjalizowanych uchwytów. Programistom z doświadczeniem głównie w języku C lub C++ starego stylu tru dno będzie w to uwierzyć, ale zarządzanie zasobami bazujące na zasięgach to niezwykle mocne narzędzie. Zasięgi definiow ane przez użytkow nika z odpow iednim i operacjam i pozwalają rozwiązywać klasyczne problem y za pom ocą mniejszej ilości kodu w porównaniu ze starymi niebezpiecznymi sposobami. Oto na przykład najprostsza postać klasycznego problem u przepełnienia bufora stwarzającego zagrożenie dla bezpieczeństwa:

char buf[MAX_BUF]: gets (buf); //Oops! Wystarczy skorzystać ze standardowej biblioteki string, a problem zniknie:

string s: Cin » S; // odczytznaków oddzielanych spacjami Są to oczywiście trywialne przykłady, ale odpowiednie ciągi znaków i kontenery można zbudować w taki sposób, by spełniały właściwie wszystkie potrzeby. Dobrym miejscem do rozpoczęcia poszukiwań jest standardowa biblioteka.

Co pan rozumie pod pojęciami „semantyka wartości" oraz „ogólne zarządzanie zasobami"?

Bjame: „Semantyka wartości” to termin powszechnie używany w odniesieniu do klas, w których obiekty mają właściwość dającą po skopiowaniu dwie niezależne kopie obiektów (z tą samą wartością). Na przykład:

X X

xl = a; x2 = xl: / / Teraz x l == x2 xl = b : / / Zmienia się x l , ale nie x2 II teraz x I! = x2 (pod warunkiem że X(a)! = X(b))

Taka cecha dotyczy oczywiście zwykłych typów numerycznych, jak liczby int, double, liczby zespolone oraz matematyczne abstrakcje, na przykład wektory. Jest to najbardziej użyteczna własność. C++ obsługuje ją dla typów wbudowanych oraz dla dowolnych typów definiowanych przez użytkow nika, dla których chcemy ją mieć. Pod tym względem język C++ różni się od Javy — tu typy wbudow ane, takie jak char i int, posiadają tę własność, ale typy definiowane przez użytkowników jej nie mają i nie mogą mieć. Tak jak w języku Simula, wszystkie typy definiowane przez użytkownika w Javie mają semantykę referencji. W C++ programista może zastosować dowolną spośród tych dwóch semantyk, zgodnie z wymaganiami typu. Język C# naśladuje język C++ (choć nie do końca) w obsłudze typów użytkownika z semantyką wartości. Termin „ogólne zarządzanie zasobami” odnosi się do popularnej techniki posiadania zasobu przez obiekt (na przykład uchw ytu do pliku lub blokady). Jeśli ten obiekt jest zm ienną zdefiniowaną w pewnym zasięgu, to czas życia zmiennej wprowadza maksymalny limit czasu, przez jaki utrzymywany jest zasób. Zazwyczaj konstruktor alokuje zasób, a destruktor go zwalnia. Takie działanie, często określane terminem RAII (od ang. Resource Acquisition Is Initialization — dosł. zdobycie zasobu to inicjalizacja), doskonale się integruje z obsługą błędów z wykorzystaniem wyjątków. Oczywiście nie każdy zasób można obsłużyć w ten sposób, ale w wielu przypadkach jest to możliwe. Wtedy zarządzanie zasobami staje się niejawne i wydajne. Wydaje się, że zasada „blisko sprzętu" była wiodącą regułą podczas projektowania języka C + + . Czy można powiedzieć, że język C+ + został zaprojektowany w układzie dół-góra, podczas gdy wiele języków programowania jest projektowanych w układzie góra-dół, w tym sensie, że dostarczają one abstrakcyjnie racjonalnych konstrukcji i zmuszają kompilator do tego, by dopasowywał te konstrukcje do dostępnego środowiska przetwarzania?

Bjarne: Uważam, że określenia góra-dół i dół-góra to złe sposoby charakteryzowania tych decyzji projektowych. W kontekście języka C++ i innych języków „blisko sprzętu” oznacza, że jest przyjęty model obliczeń danego komputera — sekwencje obiektów w pamięci oraz operacje definiowane na obiektach o stałym rozmiarze zamiast jakiejś

18

ROZDZIAŁ

PI E R W S Z Y

abstrakcji m atem atycznej. Tak jest zarów no w przypadku języka C++, jak i Javy, ale w odniesieniu do języków funkcyjnych jest inaczej. C++ różni się od Javy tym, że pod spodem jest maszyna fizyczna, a nie maszyna abstrakcyjna. Prawdziwy problem polega na tym, jak przejść od ludzkiego sposobu postrzegania problemów i rozwiązań do ograniczonego świata maszyny. Można zignorować ludzkie rozterki i stworzyć kod maszynowy (lub gloryfikowany kod maszynowy w postaci złego kodu w języku C). M ożna też zignorować rozterki maszyny i uzyskać piękne abstrakcje, które pozwalają na wykonywanie dowolnych operacji olbrzymim kosztem i (lub) bez ograniczeń zdrow orozsądkow ych. Język C++ to próba uzyskania bezpośredniego dostępu do sprzętu (na przykład za pośrednictw em wskaźników i tablic) tam, gdzie jest taka potrzeba, z jednoczesnym dostarczeniem rozbudowanych mechanizmów abstrakcyjnych pozwalających na wyrażanie idei wysokopoziomowych (na przykład hierarchii klas i szablonów). W związku z takim i założeniam i podczas tw orzenia języka C++ i jego bibliotek zwracano baczną uwagę na wydajność kodu wynikowego i zarządzanie pamięcią. Te aspekty przenikają zarówno podstawowe mechanizmy języka, jak i mechanizmy abstrakcyjne w sposób wyróżniający język C++ spośród innych języków.

Używanie języka W ja k i sposób debuguje pan swój kod? Czy ma pan jakieś wskazówki dla programistów C++?

Bjarne: Przez wnikliwą analizę. Studiuję program i oglądam go mniej lub bardziej systematycznie tak długo, aż mam wystarczającą wiedzę do tego, by w inteligentny sposób odgadnąć miejsce wystąpienia błędu. Testowanie to zupełnie inna sprawa. Podobnie jak odpowiedni projekt zmierzający do zminimalizowania liczby błędów. Bardzo nie lubię debugowania i robię wszystko, by go uniknąć. Jeśli jestem programistą fragmentu oprogramowania, buduję go na bazie interfejsów i niezmienników, dlatego trudno mi uzyskać kod z dużą liczbą błędów, który nie daje się skompilować i uruchomić. Następnie bardzo się staram, aby kod można było przetestować. Testowanie jest systematycznym poszukiwaniem błędów. Trudno testować w sposób systematyczny systemy, które mają nieprawidłową strukturę, dlatego jeszcze raz zalecam dbałość o czytelną strukturę kodu. Testowanie m ożna zautomatyzować i wykonywać wielokrotnie, podczas gdy w przypadku debugowania nie da się tego uzyskać. Wykorzystanie stada gołębi, które losowo siadają na ekranie, po to, by zobaczyć, czy uda im się złamać aplikację okienkową, nie jest sposobem na zapewnienie wysokiej jakości systemów. Moja rada? Trudno dawać uniw ersalne rady, poniew aż najlepsze techniki często zależą od tego, co jest wykonalne w danym systemie oraz środowisku projektowym. Jeśli jednak mogę coś radzić: należy zidentyfikować kluczowe interfejsy, które można

systematycznie testować, a następnie napisać skrypty testowe, które to robią. Należy stosować automatyzację tam, gdzie to możliwe, i często uruchamiać takie automatyczne testy. Warto też często wykonywać testy regresji. Należy dążyć to tego, by każdy punkt wejścia do systemu i wyjścia z systemu był systematycznie przetestowany. System powinien być złożony z kom ponentów o wysokiej jakości: monolityczne programy są niepotrzebnie skomplikowane, przez co są trudne do zrozumienia i przetestowania. Na jakim poziomie konieczna jest poprawa bezpieczeństwa oprogramowania?

Bjarne: Po pierwsze, bezpieczeństwo jest problemem systemowym. Żadne lokalne lub częściowe rozwiązanie nie zapewni sukcesu. Należy pamiętać, że nawet gdyby cały kod był perfekcyjny, to i tak udałoby się uzyskać dostęp do zapisanych sekretów kom uś, kto ukradłby nasz kom puter lub nośnik z kopią zapasową. Po drugie, bezpieczeństwo jest kom prom isem pom iędzy kosztami a korzyściami: doskonałe bezpieczeństwo prawdopodobnie jest poza zasięgiem większości z nas. Można jednak zabezpieczyć system na tyle mocno, aby źli chłopcy woleli poświęcić swój czas na próby w łam ania do innego systemu. Sam dążę do tego, by nie przechowywać ważnych sekretów online, a problem y dotyczące pow ażnych zabezpieczeń pozostaw iam ekspertom. Co jednak z językami program ow ania i technikam i program ow ania? Istnieje niebezpieczna tendencja do zakładania, że każda linijka kodu musi być bezpieczna (cokolwiek by to oznaczało). Niektórzy przyjmują nawet, że w ramach tego samego zespołu mogą działać osoby o złych intencjach, które próbują manipulować innymi częściami systemu. To bardzo niebezpieczne podejście, którego efektem jest zaśmiecanie kodu wieloma testami chroniącymi przed wyimaginowanymi zagrożeniami. W ten sposób kod staje się brzydki, wielki i powolny. Jeśli kod jest brzydki, to łatwo chowają się w nim błędy. Jeśli jest wielki, to z pewnością nie będzie dobrze przetestowany, a pow olność zachęca do używania skrótów i złych praktyk. Te ostatnie należą do najczęstszych źródeł luk w zabezpieczeniach. Uważam, że jedynym trwałym rozwiązaniem problemów zabezpieczeń jest prosty model bezpieczeństwa stosowany systematycznie przez wysokiej jakości sprzęt i/lub oprogram ow anie w odniesieniu do w ybranych interfejsów. Musi istnieć obszar za barierą, gdzie można pisać kod prosto, elegancko i wydajnie bez obaw, że losowe fragmenty kodu zniszczą losowe fragmenty innego kodu. Tylko w takim przypadku będziemy mogli skupić się na popraw ności, jakości i prawdziwej wydajności. Zakładanie, że każdy może spreparować niezaufane wywołanie zwrotne, wtyczkę (ang. plug-iń), przeciążoną metodę czy cokolwiek innego, jest po prostu głupie. Musimy odróżnić kod, który jest zabezpieczony przed oszustwami, od kodu, który zabezpiecza się przed sytuacjami nadzwyczajnymi. Nie sądzę, aby można było stworzyć język programowania, który byłby całkowicie bezpieczny, a jednocześnie przydatny w praktycznych zastosowaniach. Oczywiście wszystko zależy od definicji słów „bezpieczeństwo” i „system ” . Być może łatwiej

20

ROZDZIAŁ

PIERWSZY

uzyskać bezpieczeństwo systemów w języku specyficznym dla konkretnej dziedziny. Moją dziedziną zainteresowania jest jednak programowanie systemowe (w bardzo szerokim znaczeniu tego term inu), włącznie z program ow aniem systemów wbudowanych. Uważam, że w stosunku do tego, co oferuje C++, można by poprawić bezpieczeństwo typologiczne (ang. type safety) i z pewnością będzie ono poprawione, ale to tylko część problem u: bezpieczeństwo typologiczne nie jest tożsame z bezpieczeństwem w ogóle. Osoby program ujące w C++, które używają wielu nieopakowanych tablic, operacji rzutowania oraz niestrukturalnych operacji new i delete, same proszą się o kłopoty. Osoby te pozostały na etapie stylu programowania z lat osiemdziesiątych. Aby dobrze korzystać z C++, trzeba stosować styl programowania, w którym jest jak najmniej naruszeń bezpieczeństwa typologicznego, a zasoby (łącznie z pamięcią) są zarządzane w prosty i systematyczny sposób. Czy poleciłby pan język C+ + do tworzenia niektórych systemów, na przykład oprogramowania systemowego i aplikacji wbudowanych, mimo że praktycy niechętnie wykorzystują go w tych zastosowaniach?

Bjarne: Oczywiście, że poleciłbym C++, a poza tym nie wszyscy są niechętni temu językowi. Właściwie nie zauważyłem zbytniej niechęci wykorzystywania C++ w tych obszarach, poza n aturaln ą niechęcią do w ypróbow yw ania czegoś nowego w instytucjach o ugruntowanej pozycji. Powiedziałbym nawet, że obserwuję stały i znaczący wzrost popularności języka C++. Dla przykładu — pom agałem pisać wskazówki kodowania dla kluczowego oprogramowania myśliwca Joint Strike Fighter (JSF) firmy Lockheed Martin. To samolot, którego oprogramowanie jest napisane w całości w C++. Czytelnicy najpraw dopodobniej nie znają się na wojskowych samolotach, ale w sposobach używania języka C++ nie ma nic szczególnie militarnego. W niespełna rok z moich stron internetowych ściągnięto grubo ponad 100 000 kopii reguł kodowania JSF++. Z mojej wiedzy wynika, że informacje te interesowały głównie programistów niewojskowych systemów wbudowanych. C++ jest używany do projektowania systemów wbudowanych od 1984 roku. W tym języku stworzono wiele przydatnych gadżetów, a liczba zastosowań języka dynamicznie wzrasta. Przykładami m ogą być telefony kom órkowe z systemami Symbian lub Motorola, urządzenia iPod oraz systemy GPS. Osobiście szczególnie podoba mi się zastosowanie C++ w lądownikach Mars Rovers, w których język C++ wykorzystano do analizy scen i autonomicznych podsystemów kontroli jazdy, większości naziemnych systemów komunikacyjnych oraz przetwarzania obrazów. Osoby przekonane o tym, że język C musi być bardziej wydajny niż C++, odsyłam do mojego artykułu „Learning Standard C++ as a New Language” 1 [C/C++ Users Journal, maj 1999], w którym zamieściłem krótki opis filozofii projektu i zaprezentowałem wyniki prostych eksperymentów. Techniczny raport na tem at

1 http://www.open-std.org/JTCl/sc22/wg21/docs/TR18015.pdf.

wydajności opublikował także komitet standaryzacyjny ISO zajmujący się językiem C++. W raporcie podjęto kwestie wielu problem ów i m itów związanych z takimi zastosowaniami C++, w których wydajność ma znaczenie. Autorzy artykułu zwrócili szczególną uwagę na problematykę systemów wbudowanych. (Tekst można znaleźć w internecie — wystarczy poszukać frazy Technical Report on C++ Performance). Jądra systemów Linux lub BSD w dalszym ciągu są pisane w języku C. Dlaczego ich projektanci nie zdecydowali się przejść na C + + ? Czy istnieje jakiś problem z paradygmatem obiektowym?

Bjarne: W większości przypadków powodem jest konserwatyzm i siła inercji. Poza tym kompilator GCC wolno dojrzewał. Wydaje się, że niektóre osoby ze społeczności języka C wykazują niemal celową ignorancję popartą wieloletnim doświadczeniem. Od dziesięcioleci język C++ jest używany do tw orzenia systemów operacyjnych, oprogram ow ania systemowego, a naw et twardych systemów czasu rzeczywistego oraz programów o kluczowym znaczeniu dla bezpieczeństwa. Oto kilka przykładów: Symbian, OS/400 i K42 firmy IBM, BeOS oraz częściowo Windows. Istnieje również wiele programów open source napisanych w C++ (na przykład KDE). Widzę, że utożsamia pan język C++ z program owaniem obiektowym. C++ nie jest i nigdy nie miał być językiem, w którym stosuje się wyłącznie techniki programowania obiektowego. W 1995 roku napisałem artykuł „Why C++ is not just an Object-Oriented Programming Language” . Jest on dostępny w internecie2. Ideą języka C++ było — i jest nią nadal — wspieranie wielu stylów programowania (paradygmatów, jeśli woli pan używać trudnych słów) oraz ich kombinacji. W kontekście wysokiej wydajności i bliskości sprzętu oprócz program ow ania obiektowego najważniejszym spośród innych paradygmatów jest używanie technik programowania generycznego (na jego określenie czasami używa się skrótu GP — od ang. Generic Programming). Typowa biblioteka C++ standardu ISO — STL — zawierająca fram ew ork dla algorytm ów i kontenerów to w większym stopniu techniki GP niż OO (od ang. Object Oriented). Programowanie generyczne, w typowym dla języka C++ stylu opartym na intensywnym wykorzystywaniu szablonów, stosuje się powszechnie wszędzie tam, gdzie jest potrzebna zarówno abstrakcja, jak i wydajność. Nigdy nie spotkałem się z programem, który byłby lepiej napisany w C niż w C++. Nie sądzę, aby taki program w ogóle mógł istnieć. W najgorszym razie można pisać kod C++ w stylu zbliżonym do języka C. Nic nie zmusza programistów do przesadnego wykorzystywania wyjątków, hierarchii klas czy szablonów. Dobry program ista wykorzystuje zaawansowane własności tam , gdzie pozwalają one na bardziej bezpośrednie wyrażanie idei i nie zmuszają do ponoszenia zbędnych kosztów.

2 http://www.research.att.com/~bs/oopsla.pdf.

22

ROZDZIAŁ

PIERWSZY

Dlaczego programiści mieliby przenosić kod z języka C do C ++? Jakie korzyści mogą wyniknąć z zastosowania C + + jako języka programowania generycznego?

Bjarne: Wydaje mi się, że zakłada pan, jakoby kod najpierw był pisany w języku C, a programista zaczynał pisanie wyłącznie w języku C. W wielu przypadkach programów C++ i programistów C++ (myślę, że już od długiego czasu dotyczy to większości sytuacji) tak nie jest. Niestety podejście polegające na wychodzeniu od języka C zdarza się w wielu kręgach, ale na szczęście nie jest już czymś, co przyjmuje się za pewnik. Ktoś może przejść z języka C na C++ dlatego, że obsługa stylu programowania, którą realizował wcześniej w języku C, jest lepsza w C++ niż w C. Kontrola typów w języku C++ jest bardziej ścisła (nie m ożna zapom nieć zadeklarować funkcji lub typów jej argum entów ), istnieje także bezpieczne typologicznie wsparcie notacji wielu popularnych operacji, takich jak tworzenie obiektów (włącznie z inicjalizacją) czy stałych. Znam programistów, którzy właśnie z tych powodów przeszli na język C++ i są bardzo zadowoleni, że udało im się pozbyć wielu problemów. Zwykle takiemu przejściu towarzyszy adopcja niektórych bibliotek języka C++, czasami obiektowych — na przykład standardowej biblioteki obsługi wektorów, biblioteki obsługi interfejsu GUI oraz niektórych bibliotek specyficznych dla aplikacji. Użycie niestandardowego typu, na przykład vector, string czy complex, nie wymaga zmiany paradygmatu. Można po prostu, jeśli się tego chce, korzystać z nich tak jak z typów wbudowanych. Czy ktoś, kto stosuje konstrukcję std::vector, używa technik OO? Powiedziałbym, że nie. Czy ktoś, kto używa mechanizmów obsługi interfejsu GUI języka C++, ale nie dodaje nowych funkcji, używa technik OO? Skłaniam się do odpowiedzi twierdzącej, ponieważ używanie ich zwykle wymaga od użytkowników zrozumienia i posługiwania się mechanizmami dziedziczenia. Wykorzystanie C++ jako języka programowania generycznego daje programiście dostęp do gotowych standardow ych kontenerów i algorytmów (w ram ach standardowej biblioteki). Jest to wielkie udogodnienie w przypadku wielu zastosow ań i wejście na wyższy poziom abstrakcji w porównaniu z językiem C. Poza tym programiści mogą korzystać z bibliotek, na przykład Boost, oraz stosować niektóre techniki program ow ania funkcyjnego właściwe programowaniu generycznemu. Myślę jednak, że pytanie jest trochę mylące. Nie chcę przedstawiać języka C++ jako języka OO lub języka GP. Chciałbym raczej pokazać, że jest to język dający dostęp do własności takich jak: •

programowanie w stylu języka C,



abstrakcja danych,



programowanie obiektowe,



programowanie generyczne.

C++

23

Co najważniejsze, język ten obsługuje wiele stylów programowania (programowanie w ieloparadygm atow e — jeśli ktoś woli) i jest ukierunkow any na program ow anie systemowe.

Programowanie obiektowe i współbieżność Przeciętna złożoność i rozmiary (licząc w wierszach kodu) oprogramowania wydają się rozrastać z roku na rok. Czy techniki programowania obiektowego dobrze komponują się w tej rzeczywistości, czy też tylko bardziej wszystko komplikują? Mam wrażenie, że dążenie do tworzenia obiektów wielokrotnego użytku komplikuje wiele rzeczy, a poza tym podwaja pracochłonność. Po pierwsze, trzeba zaprojektować narzędzie dające się wielokrotnie wykorzystać. Później, kiedy zajdzie konieczność wprowadzenia zmian, trzeba będzie napisać coś, co dokładnie wypełni lukę pozostawioną przez poprzednią wersję, a to oznacza ograniczenia dla rozwiązania.

Bjarne: To dobry opis poważnego problemu. OO to zbiór technik gwarantujących duże możliwości. Mogą one być pomocne, ale żeby były pomocne, muszą być dobrze wykorzystywane i stosowane w odniesieniu do takich problemów, dla których techniki te mają coś do zaoferowania. Zaprojektowanie dobrej klasy bazowej (interfejsu dla wielu nieznanych wcześniej klas) wymaga umiejętności przewidywania i doświadczenia. Jest to poważny problem podczas tworzenia kodu w całości bazującego na dziedziczeniu z w ykorzystaniem interfejsów kontrolow anych statycznie. Skąd projektant klasy bazowej (klasy abstrakcyjnej, interfejsu czy jak to nazwiemy) ma wiedzieć, że uwzględnił wszystko, co jest potrzebne dla wszystkich klas, które w przyszłości będą dziedziczyły z klasy bazowej? Skąd ma wiedzieć, że to, co zaprojektuje, będzie można sensownie zaimplementować we wszystkich klasach, które w przyszłości będą dziedziczyły z jego klasy bazowej? Jak ma się dowiedzieć, że zaprojektowana klasa bazowa nie będzie poważnie kolidowała z czymś, co jest wymagane przez pewne klasy, które w przyszłości będą dziedziczyły z jego klasy bazowej? Ogólnie rzecz biorąc, nie ma możliwości, by się tego dowiedzieć. W środowisku, w którym możemy wymusić nasz projekt, programiści dostosują się — często poprzez pisanie mało eleganckich obejść. Jeśli nie ma organu koordynującego, powstaje wiele niekompatybilnych interfejsów dla tego samego zestawu funkcji. Nie można rozwiązać tych problemów w sposób ogólny, ale programowanie generyczne wydaje się być dobrym rozwiązaniem w wielu ważnych obszarach, w których podejście obiektowe się nie sprawdza. Przykładem godnym przytoczenia są proste kontenery: za pomocą hierarchii dziedziczenia nie można dobrze wyrazić pojęcia bycia elementem. Nie można również dobrze wyrazić pojęcia bycia kontenerem. Relacje te mogą jednak dostarczyć skutecznych rozwiązań za pom ocą program ow ania generycznego. Przykładem może być biblioteka STL (wchodząca w skład standardowej biblioteki C++).

24

ROZDZIAŁ

PIERWSZY

Czy to jest problem specyficzny dla języka C + + , czy też w równym stopniu dotyczy on innych języków programowania?

Bjarne: Problem jest wspólny dla wszystkich języków, które bazują na statycznie kontrolow anych interfejsach hierarchii klas. Przykładem są języki C++, Java i C#. Problem ten nie dotyczy języków z dynamiczną kontrolą typów, takich jak Smalltalk czy Python. W języku C++ tę kwestię można rozwiązać za pomocą programowania generycznego. Dobrym przykładem są kontenery C++ i algorytmy należące do standardowej biblioteki. Kluczową cechą języka są w tym przypadku szablony dostarczające model późnej kontroli typów. Jest to odpow iednik m echanizm u dynamicznej kontroli typów, tyle że w fazie kompilacji, a nie wykonywania programu. W prow adzone ostatnio do języków Java i C# typy generyczne są próbą pójścia w kierunku wyznaczonym przez C++. Według mojej opinii często niesłusznie mówi się o nich jako o usprawnieniu szablonów. Szczególnie popularną techniką jest refaktoryzacja, która polega na próbie rozwiązania problem u techniką siłową poprzez przeorganizowanie kodu, w przypadku gdy początkowy projekt interfejsu był nieprawidłowy. Jeśli jest to ogólny problem programowania obiektowego, to jak ą mamy pewność, że zalety programowania 0 0 są większe niż wady wynikające ze stosowania tej techniki? Być może problem z trudnością uzyskania dobrego projektu obiektowego jest źródłem wszystkich innych problemów.

Bjarne: Istnienie problemu w niektórych lub nawet w wielu przypadkach nie zmienia faktu, że wiele doskonałych, wydajnych i łatwych do zarządzania systemów napisano w językach obiektowych. Techniki obiektowe należą do podstawowych sposobów projektow ania systemów, a interfejsy kontrolow ane statycznie oprócz w ad mają również zalety. W dziedzinie wytwarzania oprogramowania nie istnieje jedna recepta na wszystkie problemy. Projektowanie jest trudne pod wieloma względami. Programiści często nie doceniają intelektualnych i praktycznych trudności związanych z tworzeniem rozbudow anych systemów zawierających elementy oprogram ow ania. Tworzenie takich systemów nie sprowadza się i nigdy nie będzie się sprowadzać do mechanicznego procesu produkcyjnego. Do stworzenia stosunkow o dużego systemu niezbędne są kreatywność, stosowanie zasad inżynierskich oraz ewolucyjne zmiany. Czy istnieją powiązania pomiędzy paradygmatem programowania obiektowego a współbieżnością? Czy coraz większe potrzeby poprawy współbieżności doprowadzą do zmiany implementacji, czy natury projektów obiektowych?

Bjarne: Od bardzo długiego czasu istnieje powiązanie pomiędzy programowaniem obiektowym a współbieżnością. W Simuli 67, języku program ow ania, w którym po raz pierwszy były bezpośrednio dostępne techniki programowania obiektowego, istniały również mechanizmy do przedstawiania operacji współbieżnych.

Pierwszą biblioteką C++ była biblioteka obsługująca m echanizm , który dziś nazwalibyśmy wątkami. W Bell Labs już w 1988 roku wykorzystywaliśmy język C++ na kom puterze sześcioprocesorowym i nie byliśmy jedynymi, którzy używali go w takich zastosow aniach. W latach dziewięćdziesiątych istniało co najmniej kilkadziesiąt eksperymentalnych dialektów języka C++ oraz bibliotek zajmujących się problem am i program ow ania rozproszonego i równoległego. W spółczesne zachłyśnięcie się systemami wielordzeniowymi nie jest moim pierwszym kontaktem ze współbieżnością. Przetwarzanie rozproszone było tematem mojej pracy doktorskiej i od tamtych czasów śledzę tę dziedzinę. Ludzie, którzy po raz pierwszy stykają się ze współbieżnością, wieloma rdzeniami itp., często robią błąd, nie doceniając kosztów uruchamiania operacji na innym procesorze. Koszt uruchomienia operacji na innym procesorze (rdzeniu) oraz dostępu tej operacji do danych w pamięci procesora wywołującego (kopiowanie bądź też zdalny dostęp) może być tysiąc (lub więcej) razy większy niż koszt zwykłego wywołania funkcji. Poza tym ryzyko popełnienia błędów okazuje się dużo wyższe, jeśli zastosuje się współbieżność. Aby skutecznie wykorzystać udostępniane przez sprzęt możliwości przetwarzania równoległego, trzeba przemyśleć organizację oprogramowania. Na szczęście możemy wykorzystać wyniki badań prowadzonych przez dziesięciolecia (które jednak mogą nas również wprowadzić w błąd). Ogólnie rzecz biorąc, jest tak wiele badań, że niemal niemożliwe okazuje się stwierdzenie, które są wartościowe, a tym bardziej — które są najlepsze. Dobrym punktem wyjścia może być artykuł na tem at języka Emerald, wygłoszony na konferencji HOPL-III. Język ten był pierwszym, który zajmował się interakcją pomiędzy problemami języka a problemami systemowymi z uwzględnieniem kosztów. W ażną rzeczą jest również rozróżnienie pom iędzy rów noległym przetw arzaniem danych, stosowanym od dziesięcioleci do w ykonyw ania obliczeń naukow ych (głównie w języku FORTRAN), a urucham ianiem komunikujących się ze sobą jednostek zwykłego sekwencyjnego kodu (na przykład procesów lub wątków) na wielu procesorach. Uważam, że aby język programowania mógł zdobyć powszechną akceptację we współczesnym świecie wielu rdzeni i klastrów, powinien obsługiwać oba rodzaje współbieżności, a najlepiej jeśli obsługiwałby po kilka odm ian każdego z nich. Nie jest to łatwe, a problemy wykraczają poza tradycyjne sprawy dotyczące języków program ow ania — trzeba łącznie uwzględnić problemy związane z językiem, systemami i aplikacjami. Czy język C+ + jes t przygotowany do obsługi współbieżności? Oczywiście można stworzyć biblioteki, które będą obsługiwały wszystko, ale czy język i standardowa biblioteka wymagają poważnych zmian pod kątem obsługi współbieżności?

Bjarne: Jest prawie przygotowany. Wersja C++0x będzie przygotowana. Aby język mógł obsługiwać współbieżność, musi przede wszystkim mieć dokładnie zdefiniowany model pamięci. Dzięki temu autorzy kompilatora mogą skorzystać z nowoczesnego sprzętu (wyposażonego w potoki, duże pamięci podręczne, bufory przewidywania

26

ROZDZIAŁ

PIERWSZY

rozgałęzień, mechanizmy statycznego i dynamicznego przestawiania instrukcji itp.). W tedy wystarczą niewielkie rozszerzenia języka: lokalna pamięć masowa z obsługą wątków oraz atomowe typy danych. Następnie można dodać obsługę współbieżności w postaci bibliotek. Naturalnie pierwszą nową biblioteką standardową będzie biblioteka obsługi wątków, umożliwiająca przenośne program owanie pomiędzy systemami, na przykład Linux i W indows. Oczywiście takie biblioteki istnieją od wielu lat, ale nie są to biblioteki standardowe. Wątki wraz z pewną formą blokowania mającą na celu uniknięcie wyścigów to jeden z najgorszych sposobów bezpośredniej obsługi współbieżności. Jednak w języku C++ taki mechanizm jest potrzebny, aby m ożna było obsłużyć istniejące aplikacje oraz aby język mógł spełnić swoją rolę — rolę systemowego języka program ow ania w tradycyjnych systemach operacyjnych. Prototypy takiej biblioteki istnieją — powstały na podstawie wielu lat aktywnego użytkowania języka. Jednym z kluczowych problemów współbieżności jest sposób opakowania zadania, by m ogło ono być w ykonane współbieżnie z innym i zadaniam i. Przewiduję, że w języku C++ rozwiązaniem tego problemu będzie obiekt funkcyjny. Obiekt może zawierać potrzebne dane i przekazywać je według potrzeb. Standard C++98 dobrze obsługuje ten m echanizm dla nazw anych operacji (nazwanych klas, z których egzemplifikujemy obiekty funkcyjne). Technika ta jest też powszechnie stosowana do parametryzacji w bibliotekach generycznych (na przykład STL). W standardzie C++0x ułatw iono pisanie prostych jednorazowych obiektów funkcyjnych poprzez wprow adzenie funkcji lam bda, które m ogą być pisane w kontekstach wyrażeń (na przykład jako argum enty funkcji) i odpow iednio generują obiekty funkcji (domknięcia). Następne kroki są bardziej interesujące. Natychmiast po opublikowaniu standardu C++0x komisja planuje wydanie technicznego raportu na tem at bibliotek. Niemal na pewno biblioteki będą obsługiwać pule w ątków oraz jakąś formę „wykradania” pracy. Mam tu na myśli to, że będzie istniał standardowy m echanizm pozwalający użytkownikowi na współbieżne wykonywanie stosunkowo niewielkich jednostek pracy (zadań). Dzięki korzystaniu z tego mechanizmu użytkownik nie będzie się musiał martwić tworzeniem wątków, ich niszczeniem, blokadami itp. Mechanizmy te będą prawdopodobnie wbudowane w obiekty funkcyjne jako zadania. Poza tym użytkowni k będzie mógł korzystać z mechanizmów komunikacji między geograficznie zdalnymi procesami za pom ocą gniazd, strum ieni wejścia-wyjścia itp., podobnych do tych oferowanych w bibliotece boost: :networking. W mojej opinii większość interesujących elementów współbieżności pojawi się w postaci wielu bibliotek obsługujących logicznie rozłączne modele współbieżności.

Wiele współczesnych systemów jest podzielonych na komponenty i rozproszonych w sieci. Era aplikacji webowych i aplikacji mashup może podkreślić ten trend. Czy w języku powinny się znaleźć mechanizmy obsługujące te aspekty pracy sieciowej?

Bjarne: Istnieje wiele form współbieżności. Celem niektórych z nich jest poprawa przepustowości lub czasu odpowiedzi programu na pojedynczym komputerze bądź klastrze, niektóre m ają na celu obsługę geograficznej dystrybucji, a jeszcze inne znajdują się poniżej poziomu, jakim zwykle zajmują się programiści (potoki, pamięć podręczna itp.). Standard C++0x dostarczy zbioru m echanizm ów i gwarancji zabezpieczających programistów przed niskopoziomowymi szczegółami. Wprowadza on model maszyny, który będzie mógł pełnić rolę kontraktu pomiędzy architektami komputera a autorami kompilatorów. Dostarczy również bibliotekę obsługi wątków, w której znajdzie się proste odw zorowanie kodu na procesory. Skorzystanie z tych podstaw pozwala dostarczyć inne modele za pośrednictwem bibliotek. Chciałbym, aby w bibliotece standardu C++0x znalazły się prostsze w użytkow aniu, wyżejpoziomowe m odele współbieżności, ale teraz wydaje się to m ało praw dopodobne. Później — m am nadzieję, że wkrótce po opublikowaniu standardu C++0x — pojawi się więcej bibliotek wyspecyfikowanych w raporcie technicznym: pule wątków i obiekty futurę, a także biblioteka obsługi strumieni wejścia-wyjścia w sieci rozległej (na przykład TCP/IP). Takie biblioteki istnieją, ale nie wszyscy uważają, że są one na tyle dobrze wyspecyfikowane, aby mogły stać się standardowe. Wiele lat temu miałem nadzieję, że standard C++0x zajmie się starymi dla języka C++ problemami dystrybucji poprzez wyspecyfikowanie standardowej formy serializacji, ale tak się nie stało. A zatem społeczność programistów języka C++ będzie zmuszona rozwiązywać bardziej wysokopoziomowe problemy przetwarzania rozproszonego i aplikacji rozproszonych za pom ocą niestandardow ych bibliotek i/lub platform framework (na przykład CORBA lub .NET). Pierwsza biblioteka języka C++ (w rzeczywistości pierwszy kod w języku C z obsługą klas) dostarczała lekkiej obsługi współbieżności. Przez lata w C++ pow stały setki bibliotek i fram eworków obsługujących przetwarzanie współbieżne, równoległe i rozproszone, ale społeczność nie zdołała się porozum ieć co do standardów . Przypuszczam, że częściowo problem ten wynika z tego, że zrobienie czegoś poważnego w tej dziedzinie wymaga znacznych nakładów finansowych, a wielcy gracze wolą wydawać pieniądze na własne zastrzeżone biblioteki, frameworki i języki. Nie jest to dobre dla społeczności języka C++ jako całości.

28

ROZDZIAŁ

PIERWSZY

Przyszłość Czy kiedykolwiek powstanie standard C+ + 2.0?

Bjame: To zależy, co pan rozumie przez „C++ 2.0” . Jeśli oczekuje pan nowego języka, stworzonego prawie od podstaw , zawierającego wszystko, co najlepsze w C++, i pozbawionego wszystkiego tego, co złe (dla określonych definicji tego, co dobre, i tego, co złe), to moja odpowiedź brzmi: „Nie wiem” . Chciałbym, aby powstał nowy język wywodzący się z tradycji C++, ale nie widzę takiego na horyzoncie, zatem pozwoli pan, że skoncentruję się na następnym standardzie ISO języka C++, znanym pod nazwą C++0x. Dla wielu osób będzie to C++ 2.0, ponieważ dostarczy now ych własności języka i nowych standardowych bibliotek, ale jednocześnie będzie niemal w 100% zgodny z C++98. Nazwaliśmy go C++0x z nadzieją, że nazwa ta przekształci się w C++09. Jeśli się spóźnim y — w związku z czym x będzie m usiał przyjąć wartość cyfry szesnastkowej — to zarów no ja, jak i inni członkowie zespołu będziemy sm utni i zakłopotani. Standard C++0x będzie niem al w 100% zgodny z C++98. Naszym celem nie jest doprow adzenie do tego, by istniejący kod przestał działać. Najbardziej znaczące niezgodności wynikają z użycia kilku now ych słów kluczowych, takich jak static assert, constexpr i concept. Staraliśmy się zminimalizować ich oddziaływanie na kod, wybraliśmy więc nowe słowa kluczowe, które nie są często wykorzystywane. Najważniejsze usprawnienia to: •

Obsługa nowoczesnych architektur komputerów i współbieżności: model maszyny, biblioteka wątków, lokalna pamięć masowa wątków i operacje atomowe oraz mechanizmy asynchronicznego zwracania wartości (obiekty future).



Lepsza obsługa programowania generycznego: typ concept (system typologiczny dla typów, kombinacji typów oraz kombinacji typów i liczb integer) umożliwiający lepszą kontrolę definicji i zastosowań szablonów oraz lepsze przeciążanie szablonów. Dedukcja typów bazująca na inicjalizatorach (auto), uogólnione listy inicjalizacyjne, uogólnione wyrażenia stałe (constexpr), wyrażenia lambda i wiele innych.



Wiele drobnych rozszerzeń języka, takich jak statyczne asercje, sem antyka przenoszenia (ang. move semantics), poprawione enumeracje, nazwa pustego wskaźnika (nullptr) itp.



Nowe biblioteki standardowe obsługi dopasowywania wyrażeń regularnych, tablic skrótów (na przykład unordered map), inteligentnych wskaźników itp.

Szczegółowe informacje m ożna znaleźć w w itrynie internetow ej kom itetu standaryzacyjnego C++3. Sumaryczne zestawienie zamieściłem na prowadzonej przeze mnie stronie internetowej C++0x FAQ4. 3 http://vuww.open-std.org/jtcl/sc22/wg21/. 4 http://www.research.att.com/~bs/C++0xFAQ.htm l.

W arto zwrócić uwagę, że kiedy mówię o zachow aniu działania kodu, odnoszę się do rdzenia języka oraz biblioteki standardowej. Stary kod przestanie oczywiście działać, jeśli wykorzystuje niestandardowe rozszerzenia od jakiegoś dostawcy kom pilatora lub antyczne niestandardowe biblioteki. Z mojego doświadczenia wynika, że jeśli ktoś skarży się na to, że kod przestał działać lub że jest niestabilny, zazwyczaj przyczyną są zastrzeżone własności i biblioteki. Jeśli na przykład zmienimy system operacyjny, a w program ie nie skorzystaliśmy z jednej z przenośnych bibliotek GUI, praw dopodobnie będziemy zmuszeni do w ykonania pewnych operacji z kodem interfejsu użytkownika. Co pana powstrzymuje przed stworzeniem całkowicie nowego języka?

Bjarne: Natychmiast pojawia się kilka kluczowych pytań: •

Jakie problemy miałby rozwiązywać nowy język?



Czyje problemy rozwiązywałby ten język?



Jakie nowości można by wprowadzić (w porównaniu z każdym z istniejących języków)?



Czy nowy język może być skutecznie wdrożony (w świecie, w którym istnieje wiele języków o mocnej pozycji)?



Czy praca nad nowym językiem ma być tylko przyjem nym oderw aniem się od ciężkiej pracy polegającej na w spom aganiu tw orzenia lepszych narzędzi i systemów?

Dotychczas nie udało mi się w zadowalający mnie sposób odpowiedzieć na te pytania. Nie oznacza to jednak, że uw ażam C++ za perfekcyjny w swojej klasie. Nie jest on perfekcyjny. Jestem przekonany, że można by zaprojektować język, który miałby rozmiary nieprzekraczające jednej dziesiątej rozmiaru C++ (niezależnie od sposobu mierzenia rozmiaru) i który w mniejszym lub w większym stopniu dawałby te same możliwości co C++. Tymczasem tworzenie nowego języka to coś więcej niż tylko powielenie operacji wykonywanych w istniejącym języku, tyle że nieco lepiej i nieco bardziej elegancko. Jakie wnioski z lekcji na temat powstania, dalszego rozwoju i przystosowywania się pańskiego języka do warunków współczesnych mogą wyciągnąć programiści, którzy tworzą systemy komputerowe dziś oraz będą je tworzyć w najbliższej przyszłości?

Bjarne: To doskonałe pytanie — czy potrafim y uczyć się z historii? Jeśli tak, to jak i czego m ożem y się nauczyć? W początkowej fazie tw orzenia języka C++ sform ułowałem zbiór reguł oczywistych. M ożna je znaleźć w książce The Design and Evolution o f C++ [Addison-Wesley]. Omówiłem je także w dwóch referatach

30

ROZDZIAŁ

PIERWSZY

wygłoszonych na konferencji HOPL. Oczywiste jest, że każdy poważny projekt języka program ow ania wymaga zbioru zasad, które pow inny być sform ułow ane jak najwcześniej. To są właśnie wnioski z doświadczeń tw orzenia języka C++. Nie sform ułow ałem zasad projektow ych języka C++ dostatecznie wcześnie i nie doprow adziłem do tego, aby zasady te zostały dostatecznie rozpowszechnione. W efekcie wiele osób opracow ało w łasne reguły projektow ania w C++. Niektóre z nich były dość zabawne i w prow adziły sporo zamieszania. Do dziś niektórzy postrzegają C++ jako raczej nieudaną próbę zaprojektowania języka przypominającego Smalltalk (nie, język C++ nie miał przypominać Smalltalka, C++ naśladuje model program ow ania obiektowego z języka Simula) lub jako tylko próbę rozwiązania niektórych wad pisania w języku C (nie, język C++ nie miał być tylko poprawką języka C). Celem języka program owania (jeśli nie jest to język eksperymentalny) jest pomoc w budowaniu dobrych systemów. Pojęcia projektowania systemów i projektowania języka są ze sobą blisko związane. Moja definicja słowa „dobry” w tym kontekście brzmi: „poprawny, łatwy w pielęgnacji i zużywający zasoby na akceptow alnym poziom ie” . Oczywistym brakującym komponentem jest „łatwy do pisania”, ale dla tego rodzaju systemów, o których przede wszystkim myślę, ma to drugorzędne znaczenie. Ideologia szybkiego wytwarzania aplikacji (ang. RAD development) nie jest moim ideałem. Czasami stwierdzenie tego, co nie jest podstaw ow ym celem, okazuje się równie ważne jak to, co nim jest. Na przykład nie mam nic przeciwko szybkiemu wytwarzaniu oprogramowania — nikt o zdrowych zmysłach nie chce poświęcać projektowi więcej czasu, niż to konieczne — ale za ważniejszy od szybkiego wytwarzania uważam brak ograniczeń w pewnych obszarach aplikacji oraz brak ograniczeń wydajności. Moim celem podczas tworzenia języka C++ było i jest bezpośrednie wyrażenie idei, czego wynikiem jest kod wydajny zarówno pod względem czasu działania, jak i zajmowanego miejsca. Języki C i C++ gwarantują stabilność na dziesięciolecia. Ma to niezwykłe znaczenie dla strategicznych użytkowników języka. Znam wiele małych programów, które nie były zmieniane od początku lat osiemdziesiątych. Taka stabilność ma swoją wartość, ale języki, które jej nie zapewniają, po prostu nie nadają się do stosowania w dużych, długo realizowanych projektach. Języki korporacyjne oraz języki, które próbują gonić trendy, zupełnie się tu nie sprawdzają, a próby ich stosowania powodują wiele szkód. Prowadzi to do myślenia o właściwym sposobie zarządzania ewolucją. Ile m ożna zmieniać? Jaka powinna być szczegółowość zmian? Zmienianie języka co rok, w takim tempie, w jakim powstają nowe wydania produktów, jest zbyt gwałtowne i prowadzi do stosowania wielu rozwiązań częściowych, niedokończonych bibliotek i konstrukcji języka i/lub konieczności aktualizacji na masową skalę. Poza tym rok to po prostu zbyt krótki okres inkubacji dla ważnych własności. Takie podejście prowadzi zatem do powstawania rozwiązań połowicznych oraz martwych punktów. Z drugiej strony

dziesięcioletni cykl ISO dla języków standaryzow anych, takich jak C lub C++, jest zbyt długi i powoduje zastój części społeczności języka (a także części komitetu standaryzacyjnego). Wokół języka, który odnosi sukces, tworzy się społeczność, która współdzieli techniki, narzędzia i biblioteki. Języki korporacyjne m ają tu wielką przewagę: korporacje mogą kupić udział w rynku za pomocą technik marketingowych, konferencji oraz darmowych bibliotek. Taka inwestycja może doprowadzić do rozwoju społeczności — staje się ona liczniejsza i bardziej aktywna. Wysiłki firmy Sun dotyczące języka Java pokazały, jak amatorskie i niedostatecznie finansowane były wcześniejsze próby stworzenia języka ogólnego przeznaczenia. Ostrym kontrastem dla działań firmy Sun mogą być wysiłki Departamentu Obrony USA do nadania dominującej roli językowi Ada oraz niedostatecznie dofinansow ane próby nadania takiej roli językowi C++ (przeze mnie i moich kolegów). Nie mogę powiedzieć, że popieram wszystkie posunięcia firmy Sun związane z językiem Java — na przykład kwestionuję sprzedaż typu góra-dół (ang. selling top-down) firmom nieprogramistycznym — choć na tej podstawie łatwo zobaczyć, co można zrobić. Spośród społeczności języków niekorporacyjnych sukces odniosły te, które są skupione wokół Pythona i Perlą. Sukcesów społeczności użytkowników języka C++ było zbyt mało i były one zbyt ograniczone, jeśli wziąć pod uwagę rozmiar społeczności. Konferencje ACCU są doskonałe. Dlaczego jednak mniej więcej od 1986 roku nie przeprowadza się dorocznych międzynarodowych konferencji na temat języka C++? Biblioteki Boost są świetne, dlaczego jednak nie stworzono centralnego repozytorium bibliotek C++ (co również można było zrobić około 1986 roku)? W użytku są tysiące bibliotek typu open source napisanych w C++. Komercyjnych bibliotek jest chyba jeszcze więcej. Nie będę teraz odpowiadał na te pytania. Chcę jedynie wskazać, że każdy nowy język musi jakoś zarządzać swoimi zasobami w dużej społeczności. Jeśli tego nie zrobi, poniesie poważne konsekwencje. Język ogólnego przeznaczenia potrzebuje informacji i aprobaty wielu społeczności — na przykład programistów branżowych, wykładowców, akademickich pracowników naukowych, osób zajmujących się badaniami naukowymi w ośrodkach przemysłowych oraz społeczności open source. Społeczności te nie są rozłączne. Często jednak pojedyncze podspołeczności postrzegają siebie jako grupę samowystarczalną, która posiada wiedzę na temat tego, co jest prawidłowe, oraz pozostaje w konflikcie z innymi społecznościami, które z jakiegoś pow odu tego nie rozumieją. Może stąd wynikać znaczący problem praktyczny. Na przykład część społeczności open source sprzeciwia się używaniu języka C++, ponieważ „to język firmy Microsoft” (a to nieprawda) lub „jest własnością AT&T” (to także nieprawda), natom iast niektórzy kluczowi gracze w branży uważają, że problemem języka C++ jest to, że nie są jego właścicielami. Zasadniczym problemem jest to, że wiele podspołeczności propaguje ograniczony i zaściankowy pogląd na to, czym naprawdę jest programowanie oraz co jest naprawdę potrzebne. Gdyby wszyscy robili wszystko tak, jak należy, problemu by nie było. Trzeba

32

ROZDZIAŁ

PIERWSZY

dążyć do zrów now ażenia różnych potrzeb w celu stworzenia większej i bardziej zróżnicowanej społeczności. Dla bardziej doświadczonych programistów uniwersalność i elastyczność języka są ważniejsze od dostarczania optym alnych rozwiązań ograniczonego zakresu problemów. Wróćmy do spraw technicznych. W dalszym ciągu uważam, że elastyczny i uniwersalny system bazujący na typach statycznych jest świetny. Moje doświadczenie w języku C++ jeszcze wzmacnia ten pogląd. Jestem również gorącym zwolennikiem prawdziwych zmiennych lokalnych typów definiowanych przez użytkowników: stosowane w języku C++ techniki obsługi ogólnych zasobów na podstawie zm iennych definiowanych w zasięgach nie m iały sobie rów nych, jeśli chodzi o wydajność. K onstruktory i destruktory, często używane razem z technikami RAII (od ang. Resource Acquisition Is Initialization), pozwalają na uzyskanie bardzo eleganckiego i w ydajnego kodu.

Edukacja Opuścił pan branżę, by zostać pracownikiem akademickim. Dlaczego?

Bjarne: Właściwie do końca nie opuściłem branży, ponieważ utrzymuję kontakty z AT&T Labs oraz z ludźmi z AT&T oraz co roku spędzam sporo czasu z ludźmi z branży. Moje związki z branżą uważam za kluczowe, ponieważ to one pozwalają umiejscowić moją pracę w realiach. Pięć lat tem u zacząłem pracować jako wykładowca na Uniwersytecie Texas A&M (po prawie 25 latach pracy dla AT&T Labs), ponieważ odczuwałem potrzebę zmiany i uważałem, że w edukacji mam coś do zaoferowania. Poważnie rozważałem także dość idealistyczne pomysły, by zacząć bardziej gruntowne badania po wielu latach prowadzenia praktycznych badań i projektów. Większość badań w informatyce albo jest zbyt odległych od codziennych problemów (nawet od hipotetycznych problem ów przyszłości), albo są one tak zagłębione w codzienne problemy, że stają się czymś, co niewiele odbiega od transferu technologii. Oczywiście nie mam nic przeciwko transferowi technologii (bardzo go potrzebujemy), ale pow inno istnieć silne sprzężenie zwrotne pomiędzy praktyką branżow ą a zaawansowanymi badaniami. Krótkowzroczność planowania wielu osób w branży oraz wymagania powstawania publikacji akademickich prowadzą do odwrócenia uwagi od kluczowych problemów. Czego dowiedział się pan podczas swojej pracy akademickiej o nauczaniu programowania początkujących?

Bjarne: Najbardziej konkretnym rezultatem mojej pracy na uniwersytecie (oprócz obowiązkowych artykułów) jest now y podręcznik nauczania program ow ania skierowany do osób, które nigdy wcześniej nie program ow ały: Programming: Principles and Practice Using C++ [Addison-Wesley].

To m oja pierwsza książka dla początkujących. Zanim stałem się wykładowcą akademickim, po prostu nie znałem wystarczająco dużo niedoświadczonych osób, abym mógł napisać taką książkę. Uważałem jednak, że zbyt wielu programistów było źle przygotow anych do w ykonyw ania zadań w branży i w innych obszarach. Teraz, kiedy mam za sobą doświadczenie w nauczaniu ponad 1200 początkujących program istów, zyskałem nieco większą pewność, że moje idee w tym obszarze będą skalowalne. Książka dla początkujących musi spełniać kilka celów. Przede wszystkim powinna zapewniać dobrą podstawę do dalszej nauki (jeśli podręcznik odniesie sukces, jego lektura będzie stanowiła pu nkt wyjścia do długofalowego procesu), a także uczyć pewnych praktycznych umiejętności. Trzeba też pamiętać, że programowanie, a ściślej — wytwarzanie oprogramowania, nie jest wyłącznie umiejętnością teoretyczną. Nie jest też czymś, co można robić dobrze bez przyswojenia pewnych podstawowych pojęć. Niestety, nazbyt często w procesie nauczania zapomina się o zachowaniu równowagi pomiędzy teorią (zasadami) a praktyką (technikami). W konsekwencji spotykamy ludzi, którzy właściwie gardzą program ow aniem (uznają wyłącznie kodowanie) i uważają, że oprogram ow anie m ożna tworzyć po zapoznaniu się z pierwszymi zasadami, bez żadnych praktycznych umiejętności. Z drugiej strony są ludzie, którzy uważają, że każdy kod jest dobry i wszystko można osiągnąć poprzez szybki wgląd w podręcznik online oraz trochę wycinania i wklejania. Spotkałem programistów, którzy implementację K&R języka C uważali za zbyt skomplikowaną i teoretyczną. W mojej opinii oba podejścia są zbyt ekstremalne i prowadzą do powstawania kodu o złej strukturze, niewydajnego i trudnego do pielęgnacji, mimo że czasami udaje się stworzyć działające fragmenty aplikacji. Jaka jest pana opinia o przykładach kodu zamieszczanych w podręcznikach? Czy powinny uwzględniać one kontrolę błędów (obsługę wyjątków)? Czy powinny to być kompletne programy, które można skompilować i uruchomić?

Bjarne: Jestem zwolennikiem przykładów, które za pomocą jak najmniejszej liczby wierszy pozwalają zilustrować ideę. Takie fragm enty program ów często są niekom pletne, chociaż sądzę, że moje skompilują się i uruchom ią, jeśli osadzę je na odpowiedniej ramie. Ogólnie mój styl prezentacji kodu wywodzi się z implementacji K&R. W mojej nowej książce wszystkie przykłady kodu będą dostępne w postaci możliwej do skom pilowania. Niewielkie fragm enty kodu umieszczone w tekście opisującym ich działanie przeplatam z dłuższymi, bardziej kom pletnymi fragm entam i kodu. W kluczowych miejscach wykorzystuję obie techniki dla pojedynczego przykładu, tak aby czytelnik mógł dwukrotnie przyjrzeć się kluczowym instrukcjom. Niektóre przykłady pow inny zawierać mechanizmy kontroli błędów, a wszystkie powinny odzwierciedlać projekt, który da się sprawdzić. Oprócz opisów błędów i ich obsługi, zamieszczonych w różnych miejscach książki, znalazły się w niej również

34

ROZDZIAŁ

PIERWSZY

osobne rozdziały poświęcone obsłudze błędów i testow aniu. Preferuję przykłady pochodzące z realnie działających programów. Nie znoszę sztucznych przykładów w rodzaju drzew dziedziczenia zwierząt oraz głupich łamigłówek matematycznych. Być może powinienem opatrzyć moją książkę etykietą: „Przykłady w tej książce nie zawierają aktów maltretowania zwierząt” .

c + +

35

ROZD ZIAŁ

P I ERWSZY

ROZDZIAŁ

DRUGI

Python

Python jest nowoczesnym, wysokopoziomowym językiem ogólnego przeznaczenia opracowanym przez Guido van Rossuma w efekcie prac nad językiem programowania ABC. Filozofia Pythona to pragmatyka. Jego użytkownicy często stosują tzw. Zen of Python, preferując jeden oczywisty sposób wykonania dowolnego zadania. Istnieją porty Pythona dla maszyn wirtualnych, na przykład CLR firmy Microsoft oraz JVM, ale podstawową implementacją jest CPython w dalszym ciągu rozwijany przez van Rossuma i ochotników. Zespół ten właśnie wydał wersję Python 3.0 — zgodne wstecz ulepszenie części języka oraz jego podstawowych bibliotek.

37

Pythonowy styl Jaka jest różnica pomiędzy tworzeniem języka programowania a pracą nad zwykłym projektem programowym?

G uido van Rossum: W większym stopniu niż w przypadku innych projektów tworzenia oprogram ow ania najważniejszymi użytkownikami są sami programiści. W związku z tym projekt języka obejmuje wysoki poziom treści meta. W drzewie zależności projektów oprogramowania języki programowania znajdują się na samym dole — wszystko pozostałe zależy od jednego bądź kilku języków. Z tego powodu również trudno modyfikuje się języki — niekompatybilna zmiana wpływa na tak wiele zależności, że zwykle jest po prostu niemożliwa do wykonania. Mówiąc inaczej, wszystkie pomyłki po wydaniu kamienieją. Przykładem może być tu C++, który jest obłożony wymaganiami zgodności do tego stopnia, że wymaga się, aby kod napisany nawet 20 lat wcześniej był w dalszym ciągu prawidłowy. W ja k i sposób debuguje się język?

Guido: Nie debuguje się. Projektowanie języków to jeden z obszarów, w którym m etodologie program ow ania zwinnego po prostu nie m ają sensu. Do m om entu ustabilizowania języka niewiele osób chce go wykorzystywać, tymczasem trudno w definicji języka znaleźć błędy, dopóki język ma niewielu użytkowników; gdy zaś jest ich wystarczająco dużo, na wprowadzanie zmian jest już za późno. Oczywiście implementację można debugować tak jak każdy program, ale sam projekt języka wymaga uważnego projektow ania zawczasu, ponieważ koszty błędów są olbrzymie. W ja k i sposób podejmuje się decyzję o tym, że określona własność powinna trafić do biblioteki w postaci rozszerzenia lub powinna być obsługiwana przez rdzeń języka?

Guido: Dawniej miałem dość dobrą odpowiedź na to pytanie. Już dawno zauważyłem, że każdy chce, aby jego ulubiona własność została dodana do języka, a większość osób nie ma zbyt wielkiego doświadczenia w projektow aniu języków. Wiele osób proponuje: „Dodajmy to do języka”, „Przydałaby się instrukcja, która robi X”. Zwykle odpowiedź brzmi: „Cóż, możesz przecież już teraz zrobić X lub coś bardzo podobnego do X, wystarczy, że napiszesz dwie bądź trzy linijki kodu. To przecież nie jest takie trudne” . Można skorzystać ze słownika lub stworzyć listę, krotkę, wyrażenie regularne czy napisać niewielką metaklasę — istnieje wiele możliwości. Takiej odpowiedzi udzielił mi Linus, który — jak się wydaje — wyznawał podobną filozofię. Udzielenie informacji, że coś m ożna zrobić, i pokazanie, jak m ożna to zrobić, jest pierwszą linią obrony. Druga odpowiedź może brzmieć następująco: „To przydatna rzecz. Możemy napisać własny m oduł bądź klasę i zaimplementować ten fragment abstrakcji” . Następna linia obrony: „OK. To wygląda na tak interesujące i przydatne, że możemy to zaakceptować jako nowy dodatek do standardowej biblioteki — będzie

38

ROZDZIAŁ

DRUGI

to czysty Python” . I na koniec — istnieją rzeczy, których napisanie w czystym Pythonie nie jest łatwe. M ożemy wtedy zasugerować lub polecić przekształcenie ich na rozszerzenie języka C. Rozszerzenia języka C to ostatnia linia obrony, zanim będziemy musieli przyznać: „Masz rację. To jest bardzo przydatne. A ponieważ nie możesz tego zrobić, musimy zmodyfikować język” . Istnieją także inne kryteria decydujące o tym, czy większy sens ma dodanie czegoś do języka, czy też do biblioteki. Jeśli wprowadzenie nowej własności ma związek z semantyką przestrzeni nazw lub czymś w tym rodzaju, to oprócz zmiany języka właściwie nie można wiele zrobić. Z drugiej strony mechanizm rozszerzeń stworzono w sposób na tyle rozbudow any, by przy użyciu kodu w języku C m ożna było w prowadzić wiele rozszerzeń w bibliotekach, a naw et dodać now ą w budow aną w łasność, bez rzeczywistego m odyfikow ania języka. Nie zm ienia się parser. Nie zmienia się drzewo parsow ania. Nie zmienia się dokum entacja języka. Wszystkie narzędzia w dalszym ciągu działają, a pomimo to nowa własność języka zostaje dodana. Przypuszczam, że istnieją własności, które pan analizował i nie mógł ich zaimplementować w Pythonie inaczej niż przez zmodyfikowanie języka, ale je pan odrzucił. Jakie kryteria pan stosuje, by stwierdzić, że coś jest pythonowe, a coś innego nie jest pythonowe?

Guido: To znacznie trudniejsze. W wielu przypadkach jest to w większym stopniu sprawa intuicji niż czegokolwiek innego. Ludzie bardzo często mówią, że coś jest pythonowe, a coś innego nie jest pythonowe. Nikt jednak nie potrafi ściśle określić, co to znaczy, że coś jest bądź nie jest pythonowe. Istnieje zbiór reguł — Zen o f Python. A czy jest coś poza tym?

Guido: To wymaga wiele interpretacji, jak każda dobra, święta księga. Kiedy widzę złą lub dobrą propozycję, potrafię powiedzieć, że jest to dobra bądź zła propozycja. Trudno jednak napisać zbiór reguł, które pom ogą komuś innem u odróżnić dobre propozycje modyfikacji zmian od złych. Brzmi to prawie tak, jakb y było w większym stopniu sprawą gustu niż czegokolwiek innego.

Guido: W pierwszym odruchu zawsze próbuje się zaprzeczyć. Można wtedy zobaczyć, czy proponujący zm iany poradzą sobie ze swoim problem em bez konieczności modyfikowania języka. W zadziwiająco wielu przypadkach taki sposób się sprawdza. Jest to praktyczny dowód słuszności tezy, według której modyfikowanie języka nie jest konieczne. Jeśli język będzie stały, ludzie i tak znajdą sposób zrobienia tego, co chcą zrobić. Poza tym często proponowane zmiany dotyczą specyficznego przypadku użycia — różnych obszarów, w których brakuje operacji specyficznych dla aplikacji. Jeśli jest jakaś interesująca własność dla aplikacji webowych, nie oznacza to, że jest dobrym

PYTHON

39

kandydatem na dodatek do języka. Jeżeli jakiś mechanizm okazał się naprawdę dobry do pisania krótszych funkcji lub pisania klas, które są łatwiejsze w pielęgnacji, może być dobrym kandydatem na dodatek do języka. Musi to być coś, co jest niezwykle ważne w dziedzinie zastosowań — sprawia, że wszystko staje się prostsze i bardziej eleganckie. Modyfikacja języka dotyczy wszystkich. Nie ma takich własności, które można na tyle dobrze ukryć, by większość osób nie mogła się o nich dowiedzieć. Wcześniej czy później ludzie odkryją nową własność w kodzie pisanym przez kogoś innego lub napotkają jakiś niejasny przypadek i będą musieli się z nią zapoznać, ponieważ coś nie zadziała zgodnie z oczekiwaniami. Często obserw atorzy zwracają uwagę na elegancję. O statnio byłem świadkiem ożywionej wymiany zdań na jednej z list dyskusyjnych poświęconych Pythonowi

— chodziło o to, czy użycie słowa kluczowego dollar jest bardziej eleganckie niż self-dot. Myślę, że według zw olenników krótszego zapisu o elegancji świadczyła liczba znaków. Istnieją dyskusje dotyczące oszczędności, ale w dużej mierze w kontekście indywidualnych gustów.

Guido: Elegancja, prostota i uniwersalność to cechy, które w dużym stopniu zależą od osobistego gustu. Coś, co według m nie pokryw a szerokie spektrum , może nie pokrywać wystarczającego obszaru dla kogoś innego i vice versa. W ja k i sposób powstał proces PEP (ang. Python Enhancement Proposal)?

Guido: To bardzo interesująca historia. Proces ten zapoczątkował i przewodniczył mu Barry Warsaw — jeden z głównych deweloperów. Zaczęliśmy ze sobą pracować około 1995 roku. Gdzieś około 2000 roku Barry zasugerował, że potrzebny jest bardziej formalny proces wprowadzania zmian w języku. Ja nie jestem tak dynamiczny w tych sprawach. Mam na myśli, że to nie ja byłem osobą, która odkryła potrzebę stworzenia listy mailingowej. To nie ja odkryłem, że lista mailingowa jest już niewystarczająca i potrzebna staje się grupa dyskusyjna. To nie ja zaproponowałem , że jest potrzebna witryna internetowa. Ja również nie byłem osobą, która zauważyła, że potrzebny jest proces dyskutowania i opracowywania zmian w języku oraz zabezpieczania się przed popełnianiem przypadkowych pomyłek w sytuacji, w której jakaś propozycja została szybko zaakceptowana bez przemyślenia wszystkich jej konsekwencji. W latach 1995 - 2000 Barry, ja i kilku innych głównych programistów, między innymi Fred Drake i przez chwilę Ken Manheimer, pracowaliśmy w CNRI (od ang. Corporation for National Research Initiatives). Organizacja ta zajm ow ała się między innym i organizowaniem spotkań IETF. CNRI miała niewielki oddział — biuro organizacji konferencji — który ostatecznie się od niej odłączył. Jego jedynym klientem było

40

ROZDZIAŁ

DRUGI

stowarzyszenie IETF. Później biuro to organizow ało przez jakiś czas konferencje poświęcone Pythonowi. Z tego powodu dość łatwo mogliśmy wejść na spotkania IETF, nawet jeśli nie były to spotkania lokalne. Bardzo spodobał mi się proces przetwarzania dokumentów RFC: spotkania robocze i fazy tworzenia standardów. Barry’emu również bardzo się to podobało. Kiedy zaproponował zrobienie czegoś podobnego dla Pythona, nie musieliśmy zbyt długo dyskutować. Świadomie zdecydowaliśmy, że nasz proces zmian nie może przebiegać tak powoli jak w przypadku przetwarzania dokumentów RFC. Standardy internetowe, przynajmniej niektóre z nich, dotyczą bowiem znacznie większej liczby dziedzin, osób i programów niż zmiany w Pythonie. Ale bezspornie wzorowaliśmy się na dokumentach RFC. Barry jest geniuszem w wymyślaniu dobrych nazw, dlatego jestem prawie pewien, że PEP to była jego propozycja. W tam tym czasie projekt Pythona m iał taki m echanizm jako jeden z pierwszych projektów open source. Został on stosunkowo szybko skopiowany do wielu projektów. Społeczność Tcl/Tk w zasadzie zmieniła tylko nazwę i stosowała ten sam dokument definiujący oraz taki sam sposób przetwarzania. W innych projektach zastosowano podobne mechanizmy. Czy uważa pan, że wprowadzenie nieco formalizmu rzeczywiście pomaga w krystalizowaniu decyzji projektowych dotyczących ulepszeń w Pythonie?

Guido: Myślę, że stało się to konieczne, kiedy społeczność rozrosła się do pewnych rozmiarów i kiedy straciłem zdolność samodzielnej oceny każdej propozycji. Dla mnie bardzo wygodne było to, że inne osoby spierają się na tem at pewnych szczegółów, a następnie przedstawiają czytelne wnioski. Czy udawało się osiągnąć konsensus, kiedy ktoś poprosił o rozważenie konkretnego skrystalizowanego zbioru oczekiwań i propozycji?

Guido: Tak. Często działało to w ten sposób, że inspirowałem PEP, mówiąc: „Wygląda na to, że mamy tu problem. Zobaczmy, czy komuś uda się znaleźć dobre rozwiązanie” . W odpowiedzi często otrzymywałem garść czytelnych wniosków na tem at sposobu rozw iązania problem u, a także trochę otw artych kwestii. Czasami w ewnętrzne przeczucie pomaga mi zamykać te otwarte kwestie. Jestem bardzo aktywny w procesie PEP, kiedy jest to obszar, który m nie interesuje — gdybyśmy mieli dodać now ą instrukcję pętli, nie chciałbym, żeby projektow ały to inne osoby. Od niektórych tem atów trzym am się jednak dość daleko — na przykład od interfejsów API baz danych. Co tworzy potrzebę powstania nowej wersji głównej?

Guido: To zależy, co pan rozumie pod pojęciem „główna” . W Pythonie, ogólnie rzecz biorąc, za główne uważamy wydania o numerach 2.4, 2.5 czy 2.6. Są one publikowane co 1 8 - 2 4 miesiące. To jedyne okazje do wprowadzenia nowych własności. Dawniej wydania były publikowane według zachcianek programistów (w szczególności mnie).

PYTHON

41

Na początku tej dekady użytkow nicy zaczęli domagać się przewidywalności — sprzeciwiali się dodaw aniu lub m odyfikow aniu własności w w ydaniach pomocniczych (na przykład temu, żeby w wersji 1.5.2 znalazły się nowe własności w porównaniu z wersją 1.5.1). Żądali też, aby główne wydania były obsługiwane przez określony m inimalny okres (18 miesięcy). Obecnie mamy zatem mniej lub bardziej uporządkowany czasowo harmonogram wydawania głównych wersji: dużo wcześniej planujemy ciąg dat prowadzących do wydania głównego (na przykład ustalamy, kiedy będą wydane wersje alfa i beta oraz wersje będące kandydatam i wydań). Planując te daty, bierzemy pod uwagę takie aspekty jak dostępność menedżera wydań oraz nakłaniamy deweloperów, by wykonywali swoje modyfikacje na długo przed ostateczną datą wydania. Własności wybierane jako dodatki do wydań zazwyczaj są zatwierdzane przez głównych deweloperów po (często długiej) dyskusji dotyczącej zalet własności oraz jej dokładnej specyfikacji. Na tym właśnie polega proces PEP (Python Enhancement Proposal — dosł. propozycje ulepszeń w Pythonie), czyli udokumentowany proces podobny do przetwarzania dokum entów RFC organizacji IETF lub procesu JSR w środowisku Javy. Różnica polega na tym, że nie jesteśmy tak bardzo sformalizowani, ponieważ mamy znacznie mniej liczną społeczność deweloperów. W przypadku przedłużającego się braku zgody (co do zalet własności lub specyficznych szczegółów) czasami wkraczam, by przeciąć węzeł. Algorytm przecinania węzłów, który stosuję, jest dość intuicyjny, ponieważ w czasie gdy jest on wywoływany, okazuje się, że racjonalna dyskusja już dawno wymknęła się spod kontroli. Najwięcej sporów wzbudzają zazwyczaj własności języka widoczne dla użytkowników. Dodatki do bibliotek są zwykle łatwe (ponieważ nie dotyczą użytkowników, którzy się nimi nie interesują). Wewnętrzne usprawnienia również nie wzbudzają zbyt wielu emocji, choć podlegają ograniczeniom przez ścisłą wsteczną zgodność na poziomie API języka C. Ponieważ deweloperzy zwykle należą do najaktywniejszych użytkowników, trudno mi stwierdzić, kiedy własność jest proponowana przez użytkowników, a kiedy przez deweloperów. Zazwyczaj deweloperzy proponują własności na podstawie potrzeb zgłaszanych przez znanych im użytkowników. Kiedy użytkownik proponuje nową własność, rzadko kończy się to sukcesem, poniew aż bez dokładnej znajomości implementacji (oraz ogólnego projektu języka) jest niemal niemożliwe prawidłowe zaproponow anie nowej własności. Zwykle prosim y użytkow ników o objaśnianie swoich problemów bez wskazywania konkretnego rozwiązania. Następnie deweloperzy proponują rozwiązania i dyskutują zalety różnych wariantów z użytkownikami. Jest również koncepcja wersji radykalnych lub inaczej przełomowych, na przykład 3.0. Dawniej wersja 1.0 była bliska wersji 0.9, a wersję 2.0 dzielił stosunkowo niewielki krok od wersji 1.6. Obecnie, przy znacznie liczniejszej społeczności użytkowników, wersje przełomowe powstają nader rzadko. Tylko one mogą być rzeczywiście niezgodne

42

ROZDZIAŁ

DRUGI

z wersjami poprzednimi. Wersje główne są zgodne wstecz z poprzednimi wersjami głównymi dzięki wykorzystaniu specjalnego mechanizmu wycofywania własności wybranych do usunięcia. Jak pan w padł na pomysł obsługi liczb całkowitych dowolnej dokładności (razem ze wszystkimi zaletam i tej metody) zamiast wykorzystywania starego (i bardzo popularnego) podejścia polegającego na sprzętowej obsłudze liczb całkowitych?

Guido: Ideę tę zapożyczyłem od poprzednika Pythona — języka ABC. W języku ABC wykorzystywano liczby rzeczywiste dowolnej precyzji. Ponieważ jednak liczby rzeczywiste niezbyt mi się podobały, przerzuciłem się na liczby całkowite. Do obsługi liczb rzeczywistych w Pythonie stosow ana jest standardow a reprezentacja liczb zmiennoprzecinkowych obsługiwanych sprzętowo (podobnie było w języku ABC). Pierwotnie w Pythonie występowały dwa typy liczb całkowitych: zwyczajowa odmiana 32-bitowa (int) oraz osobna odmiana liczby całkowitej dowolnej precyzji (long). Takie rozwiązanie stosuje się w wielu językach, choć odmiana dowolnej precyzji jest zwykle przenoszona do biblioteki, na przykład Bignum w Javie i Perlu lub GNU MP w języku C. W Pythonie te dwa typy (prawie) zawsze istniały obok siebie w rdzeniu języka. Użytkownicy musieli wybierać właściwą wersję poprzez dodanie przyrostka L do liczby. Z czasem w łasność ta staw ała się coraz bardziej uciążliwa. W wersji 2.2 Pythona wprowadziliśmy automatyczną konwersję na typ 1ong w przypadku, gdy matematycznie poprawny wynik działania na danych typu int nie mógł być reprezentowany jako liczba typu int (na przykład 2**100). Wcześniej takie działanie spowodowałoby zgłoszenie wyjątku OverflowError. Był moment, w którym wynik takiej operacji zostawał po cichu (bez przekazywania użytkownikowi żadnych informacji) obcinany, ale zmieniłem to działanie na zgłaszanie w yjątku, zanim ktokolw iek otrzym ał szansę użycia języka. Na początku lat dziewięćdziesiątych zmarnowałem popołudnie na debugowanie krótkiego, napisanego przeze mnie programu demo. Program ten był implementacją algorytmu realizującego specyficzne działania na bardzo dużych liczbach całkowitych. Takie sesje debugowania mają bardzo istotne znaczenie. Ciągle jednak zdarzały się przypadki, w których dwa typy liczbowe zachowywały się nieco inaczej. I tak wyświetlanie liczby typu int w postaci szesnastkowej lub ósemkowej zwracało wynik bez znaku (na przykład -1 wyświetlało się jako FFFFFFFF), z kolei w ykonanie tego samego działania na matematycznie równoważnej liczbie typu long zwracało wynik ze znakiem (w tym przypadku -1). W pracy nad wersją 3.0 Pythona podejmujemy radykalny krok polegający na wspieraniu tylko jednego typu liczb całkowitych. Typ będzie się nazywał int, ale implementacja w większości jest zgodna ze starym typem long.

PYTHON

43

Dlaczego nazywa pan to radykalnym krokiem?

Guido: Głównie dlatego, że jest to spore odstępstwo od obecnej praktyki w Pythonie. Wiele na ten temat dyskutowano. Proponowane były różne alternatywy, na przykład wewnętrzne wykorzystanie dwóch (lub więcej) reprezentacji liczb w sposób ukryty przed końcowymi użytkownikami (ale nie przed autorami rozszerzeń w języku C). Takie rozwiązanie m ogłoby być nieco wydajniejsze, ale w iązało się z tym bardzo dużo pracy, a w ew nętrzne posługiwanie się dwiema reprezentacjam i liczb tylko zwiększyłoby wysiłki zmierzające do poprawnego jej wykonania. Zapewnienie interfejsu do tych reprezentacji z poziom u kodu C było jeszcze trudniejsze. Obecnie mamy nadzieję, że problem wydajności jest drugorzędny oraz że da się poprawić wydajność za pomocą innych technik, na przykład poprzez zastosowanie pamięci podręcznej. W ja k i sposób zaadaptowaliście filozofię „powinien istnieć jeden — a najlepiej tylko jeden — oczywisty sposób wykonania tej operacji"?

Guido: Początkowo założenie to było przyjęte podświadomie. Kiedy Tim Peters zebrał zasady (które pan cytuje) w Zen of Python, sformułował jawnie wiele reguł, które stosowałem, nie będąc nawet tego świadomy. Reguła, o którą pan pyta (często naruszana za m oją zgodą), wywodzi się w prost z ogólnego dążenia do elegancji w matematyce i informatyce. Zasadę tę stosowali również autorzy języka ABC, dążąc do mniejszej liczby typów lub pojęć ortogonalnych. Idea ortogonalności jest wzięta wprost z matematyki. Odnosi się ona do definicji mówiącej o jednym sposobie (lub jednym prawidłowym sposobie) na wyrażenie czegoś. Na przykład współrzędne XYZ dowolnego punktu w przestrzeni 3D są określone w sposób unikatowy, jeśli określi się początek układu oraz trzy wektory bazowe. Myślę również, że wyświadczam przysługę wielu użytkownikom, skoro nie wymagam od nich konieczności wybierania pom iędzy podobnym i do siebie alternatywam i. Dla kontrastu zobaczmy, jak to jest w Javie. Jeśli ktoś chce się posłużyć strukturą danych w formie listy, w standardowej bibliotece ma wiele wersji do dyspozycji (lista jednokierunkow a, tablica i inne). Z kolei w języku C użytkow nik sam musi zdecydować, w jaki sposób chce zaimplementować własny listowy typ danych. Jaka jest pana opinia o typach statycznych w porównaniu z dynamicznymi?

Guido: Chciałbym móc powiedzieć coś prostego, na przykład że statyczne typy są złe, a dynamiczne dobre, ale to nigdy nie jest takie proste. Istnieją różne podejścia do typów dynamicznych — od Lispa po Pythona — oraz różne podejścia do typów statycznych — od C++ po Haskell. W językach takich jak C++ i Java typom statycznym praw dopodobnie n adano złą nazwę, poniew aż w ymagają one wielokrotnego przekazywania kompilatorowi tych samych informacji. Z kolei w takich językach jak Haskell i ML wykorzystywane jest wnioskowanie typów — m echanizm całkowicie różny, dający pewne korzyści właściwe typom dynamicznym, jak na przykład bardziej spójne wyrażanie idei w kodzie. Paradygmat funkcyjny wydaje się jednak trudny

44

ROZDZIAŁ

DRUGI

do w ykorzystania sam w sobie — takie pojęcia jak wejście-wyjście lub interakcje z interfejsem GUI niezbyt dobrze pasują do tej formuły i zwykle są rozwiązywane za pomocą mostu do bardziej tradycyjnego języka — na przykład C. W niektórych sytuacjach rozwlekłość Javy jest uw ażana za plus. Pozwala ona na tworzenie rozbudowanych narzędzi do przeglądania kodu, zdolnych do udzielenia odpowiedzi na pytanie, gdzie jest m odyfikow ana ta zm ienna lub kto w yw ołał tę m etodę. W językach dynam icznych udzielenie odpowiedzi na te pytania jest trudniejsze, ponieważ zwykle niełatwo znaleźć typ argumentu metody bez analizowania wszystkich ścieżek w całym kodzie. Nie jestem pewien, w jaki sposób takie narzędzia są realizowane w językach funkcyjnych, na przykład w Haskellu. Może to być tak, że użytkow nik jest zm uszony do używania praktycznie tej samej techniki, co w przypadku języków dynamicznych. Podobne działanie realizuje przecież i tak mechanizm wnioskowania typów — przynajmniej tak to rozumiem. Czy zmierzamy w kierunku typów hybrydowych?

Guido: Myślę, że m ożna by wiele powiedzieć o pewnego rodzaju hybrydach. Zauważyłem, że większość dużych systemów napisanych w językach o typach statycznych zawiera znaczący podzbiór komponentów napisanych z wykorzystaniem typów dynamicznych. Na przykład zbiór widżetów GUI oraz interfejsów API obsługi bazy danych w Javie często sprawia wrażenie, jakby zwalczał typy statyczne na każdym kroku, ponieważ przenosi większość mechanizmów kontroli poprawności do fazy działania programu. Język hybrydowy, zawierający aspekty funkcyjne i dynamiczne, m ógłby być dość interesujący. Powinienem dodać, że choć w Pythonie istnieje wsparcie dla pewnych narzędzi funkcyjnych, na przykład map i 1 ambda, to język ten nie zawiera podzbioru języka funkcyjnego: nie ma wnioskowania typów ani okazji do wykorzystywania mechanizmów współbieżnych. Dlaczego zdecydował się pan na wspieranie wielu paradygmatów?

Guido: Właściwie nie podejmowałem takiej decyzji. Python obsługuje do pewnego stopnia programowanie proceduralne i do pewnego stopnia programowanie obiektowe. Te dwa obszary nie różnią się od siebie aż tak bardzo, a na proceduralny styl Pythona silnie oddziałują obiekty (ponieważ wszystkie podstawowe typy danych są obiektami). Python obsługuje niewielki podzbiór program ow ania funkcyjnego, choć nie przypom ina to żadnego rzeczywistego języka funkcyjnego i nigdy nie będzie go przypominać. W językach funkcyjnych dąży się do tego, by jak najwięcej operacji było wykonywanych w fazie kompilacji — aspekt „funkcyjny” oznacza, że kompilator może optymalizować operacje przy mocnej gwarancji braku efektów ubocznych, o ile nie zostaną jawnie zadeklarowane. Python posiada najprostszy i najmniej inteligentny kom pilator, jaki m ożna sobie wyobrazić. Oficjalna sem antyka fazy działania program ów aktywnie zniechęca do inteligentnych działań kompilatora w rodzaju zrównoleglenia pętli lub przekształcania rekurencji w pętle.

PYTHON

45

Przekonanie, jakoby Python wspierał program ow anie funkcyjne, bazuje na tym, że dołączono do języka takie słowa kluczowe, jak lambda, map, fil ter i reduce. W mojej opinii jest to jednak jedynie osłoda syntaktyczna, a nie podstawowe bloki budulcowe, jak w innych językach funkcyjnych. Bardziej podstawową własnością, którą Python współdzieli z Lispem (językiem, który także nie jest funkcyjny), jest to, że funkcje są obiektam i i m ożna się nim i posługiwać tak jak standardow ym i obiektami. W połączeniu z zagnieżdżonymi zasięgami oraz ogólnym podejściem do stanu funkcji w stylu Lisp pozwala to na łatwą implementację pojęć, które pozornie przypominają pojęcia z języków funkcyjnych, na przykład currying, m apow anie i redukowanie. Prymitywne operacje konieczne do zaimplementowania tych pojęć są wbudowane w Pythonie, natom iast w językach funkcyjnych pojęcia te występują jako operacje prym itywne. Operację reduce( ) m ożna napisać za pom ocą kilku linijek kodu w Pythonie. W języku funkcyjnym jest zupełnie inaczej. Czy podczas tworzenia języka zastanawiał się pan nad tym, jakich programistów może on przyciągnąć?

Guido: Tak, ale prawdopodobnie moja wyobraźnia nie sięgała dostatecznie daleko. Myślałem o profesjonalnych programistach Uniksa lub środowisk uniksopodobnych. W pierwszych wersjach samouczka języka Python użyłem stwierdzenia: „Python stanowi most pomiędzy językiem C a programowaniem powłoki”, ponieważ do tego używaliśmy Pythona — ja i ludzie z mojego bezpośredniego otoczenia. Nie sądziłem, że Python może być dobrym językiem do tworzenia aplikacji, dopóki nie zaczęto mnie o to pytać. Fakt, że język ten okazał się przydatny do nauczania podstaw programowania w szkole średniej lub college’u czy do samodzielnej nauki, był jedynie szczęśliwym zbiegiem okoliczności wynikającym z utrzym ania wielu własności języka ABC. Język ABC był przeznaczony specjalnie do nauczania osób, które nie zajm owały się programowaniem. W ja ki sposób zapewnił pan równowagę pomiędzy wymaganiami języka, który powinien być łatwy do nauki dla początkujących, a wymaganiami języka na tyle rozbudowanego, aby doświadczeni programiści mogli za jego pomocą wykonywać przydatne operacje? Czy ta dwoistość jest fałszywa?

Guido: Równowaga to dobre słowo. Istnieje kilka dobrze znanych pułapek, których należy unikać. Na przykład elementy, które mają pomóc początkującym, ale denerwują ekspertów, oraz elementy wymagane przez ekspertów, a wprowadzające początkujących w błąd. Pomiędzy w ym aganiam i jednych i drugich jest na tyle dużo przestrzeni, aby obie strony mogły być zadowolone. Można również zastosować inną strategię: eksperci wykonują zaawansowane operacje, których początkujący nigdy nie odkryją — na przykład język obsługuje metaklasy, ale nie ma pow odu, by początkujący się o nich uczyli.

46

ROZDZIAŁ

DRUGI

Dobry programista W ja k i sposób rozpoznaje pan dobrego programistę?

Guido: Rozpoznanie dobrego program isty wymaga czasu. Na przykład bardzo trud no odróżnić dobrego program istę od złego podczas jednogodzinnej rozmowy kwalifikacyjnej. Kiedy jednak pracuje się z kimś i rozwiązuje różne problemy, zwykle staje się jasne, którzy programiści są dobrzy. Nie potrafię podać konkretnych kryteriów — ogólnie rzecz biorąc, dobrzy programiści wykazują kreatywność, szybko się uczą, błyskawicznie tworzą działający kod i nie muszą wprowadzać wielu zmian, zanim stanie się on gotowy do opublikowania. W arto zwrócić uwagę, że różne osoby mogą wykazywać talent w różnych aspektach programowania. Są specjaliści od algorytmów i struktur danych, integracji na wielką skalę, projektowania protokołów, testowania, projektow ania API, tw orzenia interfejsów użytkow nika oraz innych aspektów programowania. Jaką metodę zastosowałby pan przy zatrudnianiu programistów?

Guido: Na podstawie moich doświadczeń związanych z przeprowadzaniem rozmów kwalifikacyjnych w przeszłości sądzę, że nie sprawdziłbym się w zatrudnianiu w tradycyjny sposób — moje umiejętności prowadzenia rozmów kwalifikacyjnych prawie nie istnieją — i to niezależnie od tego, po której stronie biurka się znajduję! Przypuszczam, że wykorzystałbym coś w rodzaju systemu czeladniczego. Przez pewien czas współpracowałbym z grupą osób, aż w końcu wyrobiłbym sobie zdanie na temat ich silnych i słabych stron. W podobny sposób działają projekty open source. Czy istnieją cechy, które mają zasadnicze znaczenie w procesie poszukiwania świetnych programistów Pythona?

Guido: Obawiam się, że zadaje pan to pytanie z perspektywy typowego menedżera, którego jedynym celem jest zatrudnienie kilku program istów Pythona. Nie sądzę, aby istniała prosta odpowiedź na to pytanie, a poza tym uważam, że pytanie jest źle sform ułow ane. Nie zatrudnia się program istów Pythona. Z atrudnia się osoby inteligentne, kreatywne i samomotywujące się. Niem al w każdym ogłoszeniu o zatrudnieniu dla programistów można znaleźć zdanie o zdolności do pracy w zespole. Jaka jest pana opinia na temat roli zespołu w programowaniu? Czy uważa pan, że jest miejsce dla doskonałego programisty, który nie potrafi pracować z innymi?

Guido: Zgadzam się z ogłoszeniami o zatrudnieniu program istów pod jednym względem. Świetni programiści, którzy nie potrafią pracować w zespole, nie powinni być zatrudniani na tradycyjnych stanowiskach dla programistów. Zatrudnienie takiej osoby będzie katastrofą dla wszystkich osób zaangażow anych w projekt, a kod tw orzony przez tego rodzaju osobę okaże się koszmarem dla każdego, kto będzie

PYTHON

47

musiał go analizować. Uważam, że jeśli ktoś nie potrafi pracować w zespole, to nie można o nim powiedzieć, że jest świetny. Obecnie istnieją sposoby na to, by nauczyć się pracy z innymi ludźmi, a jeśli ktoś jest napraw dę świetny, nie pow inien mieć kłopotów z szybkim nauczeniem się pracy zespołowej. Przy odpowiednim nastawieniu to napraw dę nie jest tak trudne jak zaim plem entow anie w ydajnego algorytm u obliczania szybkiej transformaty Fouriera. Czy to, że jest pan twórcą Pythona, daje panu przewagę nad innymi utalentowanymi programistami programującymi w Pythonie?

Guido: Nie wiem — nad językiem i maszyną w irtualną pracowało tak wiele osób, że czasami sam jestem zaskoczony tym, jak szczegółowo działają niektóre mechanizmy. Jeśli można mówić o mojej przewadze nad innymi programistami, to prawdopodobnie w większym stopniu wynika ona z tego, że używam języka dłużej niż ktokolwiek inny, a nie z tego, że sam go napisałem. Przez ten długi czas, kiedy posługiwałem się językiem, miałem okazję zbadać i przekonać się, jakie operacje są szybsze, a jakie wolniejsze — na przykład mogę wiedzieć lepiej niż inni użytkownicy, że zmienne lokalne są szybsze od globalnych (przez co to inni, a nie ja, odczują skutki ich używania). Wiem też, że w ywołania funkcji lub m etod są kosztowne (bardziej niż w języku C lub Javie) oraz że najszybszy typ danych to tupie. Jeśli chodzi o wykorzystanie standardowej biblioteki oraz inne aspekty programowania, często m am wrażenie, że inni mają nade m ną przewagę. Na przykład co kilka lat piszę o tej samej aplikacji webowej, a dostępne technologie za każdym razem się zmieniają. W związku z tym za każdym razem piszę swoją „pierwszą” aplikację webową z wykorzystaniem nowego frameworku lub nowego podejścia. Poza tym dotychczas nie m iałem okazji w ykonania w Pythonie poważniejszych operacji z formatem XML. Wydaje się, że jed n ą z cech charakterystycznych Pythona jes t jego zwięzłość. W ja k i sposób wpływa to na łatwość pielęgnacji kodu?

Guido: Słyszałem o badaniach oraz dowodach na to, że liczba błędów przypadająca na odpowiednią liczbę wierszy kodu jest w przybliżeniu stała, niezależnie od użytego języka programowania. W związku z tym zastosowanie języka Python, którego aplikacje są znacznie mniejsze w porów naniu z implementacją tego samego zestawu funkcji w C + + lub Javie, powinno doprowadzić do ułatwienia pielęgnacji. Oczywiście można z tego wysnuć wniosek, że pojedynczy programista jest odpowiedzialny za większy podzbiór funkcji. To osobny problem, ale to także przemawia na korzyść Pythona: większa produktywność pojedynczego programisty prawdopodobnie oznacza mniej programistów w zespole. To z kolei oznacza mniejsze koszty komunikacji. O ile dobrze pamiętam z książki The Mythical Man-Month [Frederick P. Brooks; Addison-Wesley Professional], koszty te wzrastają proporcjonalnie do liczby osób w zespole podniesionej do kwadratu.

48

ROZDZIAŁ

DRUGI

Jakie powiązania widzi pan pomiędzy łatwością tworzenia prototypów w Pythonie a wysiłkami koniecznymi do stworzenia kompletnej aplikacji?

Guido: Nigdy nie chciałem, aby Python był językiem prototypowym. Nie uważam, aby m usiało istnieć klarowne rozgraniczenie pom iędzy językami prototypow ym i a produkcyjnymi. Są sytuacje, w których najlepszym sposobem stworzenia prototypu jest napisanie krótkiego program u w C. Innym razem m ożna stworzyć prototyp całkowicie bez program ow ania — na przykład za pomocą arkusza kalkulacyjnego lub zbioru poleceń fi nd i grep. Moją pierw otną intencją podczas tw orzenia Pythona było opracow anie języka używanego wtedy, kiedy zastosowanie języka C byłoby przesadą, natomiast użycie skryptów pow łoki okazałoby się zbyt kłopotliwe. W tym opisie mieści się wiele prototypów, ale także wiele aplikacji realizujących logikę biznesową (jak to się dziś określa), które nie są szczególnie zachłanne, jeśli chodzi o zasoby, ale wymagają pisania znacznej ilości kodu. Powiedziałbym, że większość kodu w Pythonie to nie są prototypy, ale aplikacje wykonujące konkretne operacje. W większości przypadków Python spełnia swoje zadania. Nie trzeba zmieniać zbyt wiele, aby uzyskać aplikację w ostatecznym kształcie. Standardowy proces pow staw ania aplikacji przebiega w ten sposób, że do prostej aplikacji stopniow o są dodaw ane nowe funkcje, przez co kilkakrotnie wzrasta jej złożoność. Nie istnieje możliwość wyznaczenia precyzyjnego punktu podziału między prototypem i aplikacją w ostatecznym kształcie. Na przykład kod aplikacji (którą zacząłem pisać w firmie Google) służącej do przeglądania kodu M ondrian od pierwszego wydania prawdopodobnie rozrósł się dziesięciokrotnie, mimo to jest ona w całości napisana w Pythonie. Oczywiście zdarzały się również sytuacje, w których Python został ostatecznie zastąpiony jakimś szybszym językiem. Na przykład pierwsze crawlery, indeksatory Google były (w większości) napisane w Pythonie. Przypadki zastępowania Pythona szybszymi językami to jednak wyjątki, a nie reguła. W ja k i sposób bezpośrednie pisanie aplikacji w Pythonie wpływa na proces projektowania?

Guido: Bardzo często sam tak pracuję, i w moim przypadku sposób ten doskonale się sprawdza. Prawdą jest, że piszę mnóstwo kodu, który wyrzucam, ale jest go znacznie mniej, niż napisałbym w dow olnym innym języku. Pisanie kodu (nawet bez uruchamiania) często bardzo pomaga mi w zrozumieniu istoty problemu. Myślenie o tym, w jaki sposób należy przeorganizować kod, aby rozwiązać problem optymalnie, bardzo często pom aga mi w jego rozważaniu. Oczywiście nie chcę używać tych argum entów jako wymówki i tłumaczyć, dlaczego unikam używania tablicy do rysowania projektu, architektury, interakcji czy też innych starszych technik projektow ania. Sztuka polega na używaniu właściwych narzędzi do rozwiązania właściwego zadania. Czasami jest to ołówek i serwetka, a innym razem okno edytora Emacs i wiersz polecenia powłoki.

PYTHON

49

Czy uważa pan, że projektowanie programów typu dół-góra bardziej pasuje do Pythona?

Guido: Nie uważam, że techniki dół-góra i góra-dół wzajemnie się wykluczają. W każdym procesie wytwarzania oprogramowania czasami używa się projektowania dół-góra, a innym razem góra-dół. Stosowanie techniki góra-dół zwykle oznacza, że m am y do czynienia z problem em , który należy uważnie przeanalizować i zaprojektować przed przystąpieniem do kodowania. Z kolei projektowanie dół-góra oznacza budow anie now ych abstrakcji na bazie już istniejących — na przykład tworzenie nowych interfejsów API. Nie twierdzę, że do kodowania interfejsów API należy przystępować bez posiadania obrazu projektu, ale często jest tak, że nowy interfejs API wywodzi się logicznie z dostępnego API niższego poziomu, a proces projektowania następuje równocześnie z pisaniem kodu. Kiedy według pana programiści najbardziej doceniają dynamiczną naturę języka?

Guido: Dynamiczne własności języka często są najbardziej przydatne podczas rozpoznawania dużego problemu lub przestrzeni rozwiązań, kiedy nie znamy drogi, którą chcielibyśmy podążać. M ożna w ykonać szereg eksperym entów, z których każdy kolejny bazuje na wiedzy zdobytej wcześniej, bez pisania dużej ilości kodu, przywiązującej nas do konkretnego rozwiązania. W tym przypadku wystarczy, jeśli napiszemy bardzo zwięzły kod w Pythonie. Napisanie 100 wierszy w Pythonie w celu uruchom ienia eksperym entu co jakiś czas to znacznie wydajniejsze rozwiązanie od pisania frameworku w Javie składającego się z 1000 linii, zwłaszcza jeśli później okaże się, że rozwiązywaliśmy niewłaściwy problem! Co oferuje Python programistom z punktu widzenia bezpieczeństwa?

Guido: To zależy od ataków, których się obawiamy. Python jest wyposażony w automatyczne mechanizmy alokacji pamięci, zatem programy w Pythonie nie są podatne na pewne typy błędów charakterystyczne dla kodu pisanego w C lub C++, jak przepełnienie buforów lub używanie niezaalokowanej pamięci. Takie błędy od daw na są podstaw ą wielu ataków na oprogram ow anie Microsoft. Oczywiście samo środowisko uruchom ieniow e Pythona jest napisane w C i w ciągu wielu lat znaleziono w nim słabe punkty. Ponadto istnieją możliwości wyjścia poza ramy środowiska wykonawczego Pythona — na przykład użycie m odułu ctypes, który pozwala na wywoływanie dowolnego kodu w C. Czy dynamiczna natura języka w jakiś sposób pomaga w zapewnieniu bezpieczeństwa, czy raczej przeszkadza?

Guido: Nie sądzę, aby dynam iczny charakter języka pom agał lub przeszkadzał. Z łatwością można zaprojektować dynamiczny język, który będzie miał wiele słabych punktów, oraz język statyczny, który będzie ich pozbawiony. Jednak wykorzystanie środowiska wykonawczego lub maszyny wirtualnej (jak się to teraz modnie nazywa)

50

ROZDZIAŁ

DRUGI

pomaga zapewnić bezpieczeństwo dzięki ograniczeniu dostępu do fizycznej maszyny. Jest to jeden z powodów, dla których Python stał się pierwszym językiem obsługiwanym przez Google App Engine — projekt, w którym obecnie uczestniczę. W ja k i sposób programista Pythona może sprawdzić bezpieczeństwo swojego kodu oraz je poprawić?

Guido: Myślę, że programiści Pythona nie pow inni zbytnio m artw ić się bezpieczeństwem. Oczywiście nie pow inni zapom inać o określonym m odelu możliwych ataków. Najważniejszy aspekt, na który należy zwracać uwagę, jest taki sam we wszystkich językach: należy uważnie postępować z danymi dostarczanymi przez osoby, którym nie ufamy (w przypadku serwera WWW oznacza to każdy bajt wchodzącego żądania WWW — włącznie z nagłówkami). Szczególnej uwagi wymagają wyrażenia regularne — z łatwością można napisać wyrażenie regularne, które jest przetw arzane bardzo długo. W związku z tym aplikacje webowe, w których zaim plem entow ano m echanizm y wyszukiwania pozwalające użytkow nikom na wprowadzanie wyrażeń regularnych, powinny zawierać mechanizmy ograniczające czas ich działania. Czy istnieje jakieś podstawowe pojęcie (ogólna zasada, punkt widzenia, pogląd, reguła), które poleciłby pan osobom chcącym uzyskać biegłość w posługiwaniu się Pythonem?

Guido: Powiedziałbym, że pragmatyzm . Jeśli ktoś zagłębia się zbytnio w takie teoretyczne zagadnienia, jak ukrywanie danych, kontrola dostępu, abstrakcje lub specyfikacje, nie jest prawdziwym program istą Pythona. Zamiast używać języka (i mieć z tego przyjemność), wiele czasu zmarnuje na walkę z nim. Istnieje również zagrożenie, że będzie go używał niewydajnie. Python jest dobrym narzędziem dla kogoś takiego jak ja — kto chce mieć szybkie efekty. Sprawdza się w przypadku osób lubiących programowanie ekstremalne lub inne techniki projektowania zwinnego, choć nawet w tych obszarach polecałbym umiar. Co pan rozumie przez pojęcie „walki z językiem"?

Guido: To zwykle oznacza próby kontynuow ania przyzwyczajeń, które dobrze sprawdzały się w innym języku. Wiele propozycji pozbycia się jawnego wskaźnika self pochodzi od osób, które niedawno zaczęły używać Pythona i jeszcze się do niego nie przyzwyczaiły. Staje się to dla nich obsesją. Czasami nowicjusze proponują modyfikowanie języka, innym razem wymyślają nader skomplikowane metaklasy, które w pewien sposób sprawiają, że wskaźnik self staje się niejawny. Zazwyczaj takie modyfikacje są bardzo niewydajne, nie działają w środowisku wielow ątkowym lub okazują się skrajne pod innym i względami. Czasami ci ludzie są tak sfrustrowani koniecznością pisania tych czterech znaków, że chcieliby zmieniać konwencję z self na s lub S. Chcieliby przekształcić

PYTHON

51

wszystko na klasę lub każdą operację dostępu zamieniać na metodę dostępową, podczas gdy w Pythonie takie rozwiązania nie są rozsądne. Stosowanie takich mechanizmów prowadzi do uzyskania bardziej rozwlekłego kodu, który jest trudniejszy do debugow ania i działa znacznie wolniej. Pewnie zna pan powiedzenie: „W FORTRAN-ie można pisać przy użyciu dowolnego języka” . Podobnie jest z Javą — niektórzy próbują pisać kod Javy nawet wtedy, gdy używają innych języków. Poświęcił pan bardzo dużo czasu na to, by dla każdego działania istniałjeden oczywisty sposób jego realizacji. Wydaje się, że wyraża pan opinię, iż postępowanie w ten sposób — po pythonowemu — pozwala czerpać prawdziwe korzyści z używania Pythona.

Guido: Nie jestem pewien, czy naprawdę poświęciłem tak wiele czasu na zapewnienie tylko jednej drogi rozwiązywania problemów. Deklaracja Zen of Python jest znacznie m łodsza od języka Python, a większość cech języka istniało na długo przedtem, zanim Tim Peters zapisał w spom niane reguły w formie „p oem atu” . Nie sądzę, aby pisząc te zasady, przypuszczał, że odniosą one tak duży sukces i w taki sposób się rozpowszechnią. To chwytliwa fraza.

Guido: Tim ma dar do używania wielkich słów. ,Jest tylko jeden sposób na wykonanie tej operacji” — to w większości przypadków białe kłamstwo. Jest wiele sposobów definiowania struktur danych. Można wykorzystać krotki i listy. W wielu przypadkach nie ma wielkiego znaczenia to, czy użyjemy krotki, listy, czy może słownika. Zwykle okazuje się, że jedno z rozwiązań jest obiektywnie lepsze, ponieważ działa dobrze w wielu sytuacjach. Istnieje kilka przypadków , w których listy działają lepiej niż krotki — na przykład gdy ciągle się rozszerzają. Zasada, o której mówimy, pochodzi z pierwotnej filozofii języka ABC, w którym dążono do minimalizacji liczby kom ponentów . Język ABC miał tę samą filozofię, co Algol 68. Obecnie jest to język prawie martwy, ale wywarł duży wpływ na rozwój innych języków. Szczególnie duży wpływ Algola mogłem zaobserwować w moim środowisku w latach osiemdziesiątych, ponieważ Adriaan van Wijngaarden był jednym z wielkich Algola 68. Kiedy poszedłem do college’u, on jeszcze wykładał. Przez jeden lub dwa semestry słuchałem jego anegdot na tem at historii Algola 68. Opowiadał je, kiedy m iał na to ochotę. Wcześniej był dyrektorem CWI (od hol. Centrum Wiskunde & Informatica). Kiedy ja do niego dołączyłem, dyrektorem był już ktoś inny. Znałem wiele osób blisko związanych z Algolem 68. Lambert M eertens, jeden z głów nych autorów języka ABC, był jednocześnie jednym z głównych autorów raportu na tem at języka Algol 68. Oznacza to, że praw dopodobnie m usiał sporo pisać na maszynie, ale czasami również trochę pomyśleć i przeprowadzić testy. W yraźnie pozostaw ał pod w pływem filozofii Algola 68 — idei udostępniania konstrukcji, które można połączyć na wiele różnych sposobów w celu stworzenia różnych struktur danych bądź struktury programu.

52

ROZDZIAŁ

DRUGI

Z całą pewnością pod jego wpływem pow stała zasada: „M am y listy lub tablice, które mogą zawierać dowolne inne elementy. Mogą one zawierać liczby lub ciągi znaków, ale także inne tablice i krotki składające się z innych elementów. Wszystkie te elem enty m ożna ze sobą połączyć” . Nagle przestaje być potrzebna tablica w ielowymiarowa, poniew aż tablica tablic rozwiązuje wszystkie problem y wielowymiarowości. Idea łączenia kilku kluczowych elem entów w taki sposób, by można było pokryć różne przypadki, była częścią filozofii języka ABC. Zapożyczyłem te elementy prawie w całości, nie myśląc o nich zbyt intensywnie. Chociaż może się wydawać, że Python pozwala na łączenie elem entów w bardzo elastyczny sposób — jeśli tylko nie próbujem y zagnieżdżać instrukcji wewnątrz wyrażeń — istnieje znacząca liczba specjalnych przypadków w składni. W niektórych sytuacjach przecinek rozdziela parametry, w innych elementy listy, a jeszcze w innych niejawną krotkę. Istnieje bardzo wiele przypadków składni, w których określone operatory nie są dozwolone, ponieważ spowodowałyby konflikt z otaczającymi je konstrukcjami składniowymi. Nigdy nie stanowi to zbyt poważnego problemu, ponieważ jeśli coś nie działa, zawsze można to ująć w parę nawiasów. Ze względu na powody wymienione powyżej składnia trochę się rozrosła (przynajmniej z perspektywy autorów parserów). Takie elementy jak rozumienie list oraz wyrażenia generatora nie są ujednolicone pod względem składniowym. Wierzę, że w Pythonie 3000 będą. W dalszym ciągu istnieją subtelne różnice semantyczne, ale przynajmniej składnia jest taka sama.

Wiele wersji Pythona Czy w Pythonie 3 0 0 0 znajdzie się prostszy parser?

Guido: Nie sądzę. Nie będzie bardziej złożony, ale także nie będzie znacząco prostszy. Zatrzymanie dalszego komplikowania kodu w moim przekonaniu jest zwycięstwem.

Guido: Zgadzam się. Dlaczego określił pan kompilator Pythona jako najprostszy i najmniej inteligentny, ja k i można sobie wyobrazić?

Guido: Pierwotnie m iało to bardzo praktyczne uzasadnienie — nie miałem wykształcenia w kierunku generow ania kodu. Byłem tylko ja i m usiałem mieć generator kodu bajtow ego pod ręką, zanim mogłem zrobić jakiekolwiek inne interesujące operacje z językiem. W dalszym ciągu twierdzę, że wykorzystywanie bardzo prostego parser a jest dobre. W końcu to tylko program przekształcający tekst w drzewo reprezentujące strukturę

PYTHON

53

programu. Jeśli składnia jest na tyle dwuznaczna, że jej analiza wymaga zaawansowanej technologii, to ludzie czytający kod praw dopodobnie także będą zagubieni. Poza tym złożona składnia znacznie utrudnia napisanie parsera. Python jest niezwykle prosty do parsowania, zwłaszcza na poziomie składni. Analiza leksykalna jest dość subtelna, poniew aż wymaga czytania poziom ów wcięć z wykorzystaniem niewielkiego stosu wbudowanego w analizator leksykalny. Analizator leksykalny Pythona jest kontrprzykładem teorii rozdzielania analizy leksykalnej od gramatycznej. Niemniej jednak to prawidłowe rozwiązanie. Zabawne jest to, że sam jestem zwolennikiem automatycznie generowanych parser ów, ale nie wierzę zbyt m ocno w generow aną autom atycznie analizę leksykalną. Python zawsze był w yposażony w generowany ręcznie skaner oraz automatyczny parser. Dla Pythona napisano wiele różnych parserów. W łasne parsery mają nawet porty Pythona na inne maszyny wirtualne, na przykład Jython, IronPython lub PyPy. Nie ma w tym niczego szczególnego, ponieważ parser nigdy nie jest bardzo złożonym projektem. Struktura języka jest bowiem taka, że można ją bardzo łatwo parsować za pom ocą prostego, rekurencyjnego parsera zstępującego (ang. recursive descent parser) analizującego jeden token do przodu. Działanie parsera spowalniają jedynie niejednoznaczności, które można rozwiązać tylko poprzez analizę kodu znajdującego się dalej — aż do końca programu. W językach naturalnych istnieje wiele przykładów sytuacji, w których parsowanie zdania jest niemożliwe do czasu przeczytania ostatniego słowa i wielu zagnieżdżeń wewnętrznych. Istnieją również zdania, które m ożna parsować tylko wtedy, kiedy znam y osobę, z którą rozmawiamy, ale to jest całkowicie odmienna sytuacja. Dla potrzeb parsowania języków programowania jestem zwolennikiem parserów analizujących jeden token do przodu. To sugeruje, że w Pythonie nigdy nie będzie mogło być makr, ponieważ w takim przypadku trzeba by było przeprowadzić kolejną fazę parsowania!

Guido: Są sposoby osadzania w ew nątrz parsera makr, które mogłyby działać. Nie jestem jednak przekonany, czy m akra rozwiązują jakikolwiek problem , który miałby jakieś szczególne znaczenie dla Pythona. Z drugiej strony, ponieważ język jest łatwy do parsowania, to gdyby udało się opracować pewien zbiór makr pasujących do składni języka, zaimplementowanie ewaluacji makr w postaci operacji na drzewie parsowania byłoby bardzo łatwe. Ta dziedzina jednak niespecjalnie mnie interesuje. Dlaczego zdecydował się pan na używanie ścisłego parsowania w kodzie źródłowym?

Guido: Zastosowanie akapitów do grupow ania nie było now atorską koncepcją Pythona. Odziedziczyłem ją z języka ABC, ale występowała ona także w starszym języku occam. Nie wiem, czy autorzy języka ABC zaczerpnęli pomysł z języka occam, wymyślili go niezależnie, czy też był jakiś wspólny przodek. Pomysł można przypisać Donowi Knuthowi, który zaproponował stosowanie akapitów już w 1974 roku.

54

ROZDZIAŁ

DRUGI

Oczywiście mógłbym nie wzorować się na języku ABC, tak jak to zrobiłem w innych obszarach (na przykład w języku ABC wykorzystywano wielkie litery do oznaczania słów kluczowych języka i nazw procedur — tego pomysłu nie skopiowałem). Własność ta spodobała mi się jednak podczas korzystania z języka ABC. Według mnie pozwalała ona na uniknięcie bezsensownej debaty prowadzonej wówczas pom iędzy użytkownikami języka C na tem at tego, gdzie należy umieszczać nawiasy klamrowe. Byłem również świadom tego, że w czytelnym kodzie akapity są i tak wykorzystywane do oznaczania grupowania. Poza tym spotykałem subtelne błędy w kodzie — wcięcia nie były zgodne z nawiasami klamrowymi (narzędziem do grupowania na poziomie składni). W takich przypadkach program ista i osoby analizujące kod zakładały, że wcięcia są zgodne z grupowaniem, co powodowało, że błędy umykały ich uwadze. Z długich sesji debugowania można wyciągnąć cenne lekcje. Ścisłe formatowanie powinno prowadzić do powstania bardziej przejrzystego kodu oraz zredukować różnice w układzie kodu tworzonego przez różnych programistów. Czy nie przypomina to jednak zmuszania ludzi do naśladowania maszyn, choć wydaje się, że powinno być odwrotnie?

Guido: Jestem przeciwnego zdania. Formatowanie bardziej pomaga ludziom niż maszynie — zresztą mówiłem już o tym przy okazji odpowiedzi na poprzednie pytanie. Korzyści z zastosowania tego podejścia są bardziej widoczne podczas poprawiania kodu pisanego przez innego programistę. Nowych użytkowników na początku często odrzuca ta własność. Nie słyszę jednak, by sprawiało to problem bardziej doświadczonym programistom. Być może ludzie uczący Pythona nabyli um iejętność przew idyw ania tego efektu i odpow iednio zareagowali. Chciałbym pana zapytać o różne implementacje Pythona. Istnieje kilka — cztery lub pięć — dużych implementacji, w tym Stackless i PyPy.

Guido: Z technicznego punktu widzenia Stackless nie jest osobną implementacją. Stackless wymienia się często jako osobną implementację Pythona. Jest to bowiem rozwidlenie Pythona zastępujące sporą część maszyny wirtualnej z wykorzystaniem innego podejścia. Różnice są głównie w dyspozytorze kodu bajtowego. Czy tak?

Guido: Większa część kodu dyspozytora bajtowego jest bardzo podobna. Myślę, że kod bajtowy jest identyczny i z całą pewnością wszystkie obiekty są takie same. Różnice występują podczas wywołań z jednej procedury Pythona do innej: w implementacji Stackless operacja ta jest wykonywana za pomocą manipulowania obiektami. Na stos odkładany jest stos stosów ramek z wykorzystaniem tego samego fragmentu kodu w C. W implementacji CPython w tym momencie wywoływana jest funkcja C, która ostatecznie wywołuje nowy egzemplarz maszyny wirtualnej. Nie jest to właściwie

PYTHON

55

cała maszyna w irtualna, tylko pętla interpretująca kod bajtowy. W implementacji Stackless na stosie C jest tylko jedna z tych pętli. W tradycyjnej implementacji CPython sama pętla może występować na stosie C wielokrotnie. To jedyna różnica. PyPy, IronPython, Jython są osobnymi implementacjami. Nie słyszałem o narzędziu, które potrafiłoby tłumaczyć kod Pythona na JavaScript, ale nie byłbym zdziwiony, gdyby w pewnym momencie ktoś uzyskał w tym obszarze znaczące sukcesy. Słyszałem 0 eksperymentalnych rozwiązaniach tłumaczących kod Pythona na OCaml i Lisp, 1 kto wie na co jeszcze. Kiedyś zetknąłem się z narzędziem tłumaczącym na język C. Mark H am m ond i Greg Stein pracowali nad czymś podobnym w końcu lat dziewięćdziesiątych, ale doszli do wniosku, że szybkość, którą są w stanie uzyskać, jest bardzo umiarkowana. W najlepszych okolicznościach kod działał dwa razy szybciej. Poza tym generowany kod miał bardzo dużą objętość — olbrzymie binaria stwarzały problem. Problemem jest czas uruchamiania. Czy tak?

Guido: Myślę, że zespół pracujący nad implementacją PyPy jest na właściwym tropie. Wygląda na to, że jest pan przychylnie nastawiony do tych implementacji.

Guido: Zawsze byłem przychylnie nastawiony do innych implementacji. Od dnia, w którym Jim Huguin przyszedł do m nie z niemalże kom pletną im plem entacją JPython, byłem nią zafascynowany. W pewnym sensie nowa implementacja może służyć za walidację projektu języka. Oznacza to również, że grupa użytkowników może używać swojego ulubionego języka na swojej ulubionej platformie. Stale mamy sporo do uzgodnienia, ale z całą pewnością pomogło mi wydzielenie tych własności, które były rzeczywistymi własnościami języka i o które się martwiłem, oraz własności konkretnej im plem entacji, przy której zgadzałem się na to, aby w innych im plem entacjach pewne operacje były wykonyw ane inaczej. W końcu doszliśmy do śliskiego zbocza mechanizmów odśmiecania (ang. garbage collectiori). To zawsze było śliskie zbocze.

Guido: Ale to także konieczność. Nie wiem, jak długo zdołaliśmy przetrwać z klasycznym zliczaniem referencji, bez możliwości przeryw ania cykli. Zawsze postrzegałem zliczanie referencji jako sposób realizacji odśmiecania. Uważałem to za całkiem niezłą m etodę. W ojna pomiędzy zwolennikam i zliczania referencji a mechanizmami odśmiecania zawsze wydawała mi się głupia.

56

ROZDZIAŁ

DRUGI

Wróćmy do wspomnianych implementacji. Myślę, że Python to interesująca płaszczyzna ze względu na dość dobrą specyfikację. Wyróżnia się zwłaszcza w porównaniu z innymi językam i, takim i ja k Tel, Ruby czy Perl 5. Czy wynikło to z pańskiego dążenia do standaryzacji języka i sposobu jego działania, czy z tego, że analizował pan różne implementacje, czy może jeszcze z czegoś innego?

Guido: Praw dopodobnie był to efekt uboczny działania społeczności skupionej wokół PEP oraz różnych implementacji. Kiedy pierwotnie napisałem pierwszy zbiór dokumentacji, z entuzjazmem zabrałem się do tworzenia opisu języka, który w moim zamyśle miał być wystarczająco dokładną specyfikacją. Chciałem ją napisać w taki sposób, aby ktoś z Marsa lub Jowisza mógł zaimplementować język z zachowaniem poprawnej semantyki. Nigdy nie udało mi się nawet zbliżyć do osiągnięcia tego celu. Najbliżej jego osiągnięcia był język Algol 68 ze swoją prawie m atem atyczną specyfikacją. W przypadku innych języków, na przykład C++ i JavaScript, stworzenie specyfikacji udało się dzięki sile woli komisji standaryzacyjnych. Dotyczy to zwłaszcza języka C++. Były to z całą pewnością wysiłki wzbudzające respekt. Trzeba jednocześnie zaznaczyć, że pisanie tak dokładnej specyfikacji wymaga tyle pracy, że moje nadzieje na stworzenie czegoś podobnego dla Pythona w rzeczywistości nigdy się nie ziściły. Tym, czym dysponujemy, jest dostateczne zrozumienie sposobu, w jaki pow inien działać język, a także dostateczna liczba wykonanych testów jednostkowych i ludzi pod ręką, którzy w skończonym czasie będą potrafili rozwiać wątpliwości osób implementujących inne wersje. Wiem na przykład, że członkowie zespołu pracującego nad implementacją IronPython bardzo sumiennie podeszli do próby uruchomienia całego zestawu testów Pythona. Przy każdym niepowodzeniu decydowali, czy zestaw testów rzeczywiście testował specyficzne działanie implementacji języka CPython, czy też oznaczało to więcej pracy w ich własnej implementacji. Ludzie pracujący nad implementacją PyPy zrobili to samo, ale poszli o jeden krok dalej. Mieli do dyspozycji kilku ludzi znacznie bystrzejszych ode mnie, którzy opracowali odpowiednie dane testowe. Prawdopodobnie były one inspirowane ich własnym spojrzeniem na sposób generowania kodu oraz analizowania kodu w środowisku JIT. Po wykonaniu kilku testów, usunięciu niejasności i udzieleniu odpowiedzi na pytania odkryli oni, że istnieje określona kombinacja zdarzeń, o których wcześniej nikt nie pomyślał. To okazało się bardzo pomocne. Proces opracowywania wielu implementacji języka bardzo przyczynił się do usunięcia niejasności w specyfikacji języka. Czy dopuszcza pan myśl, że w przyszłości CPython może nie być podstawową implementacją?

Guido: To trudno przewidzieć. Niektórzy przewidują, że pewnego dnia .NET opanuje świat, inni uważają, że przyszłość należy do JVM. Według mnie to wszystko są życzenia. Nie mam pojęcia, co się wydarzy. Może nastąpić przełom technologiczny. Choć trudno przewidzieć, czy komputery, które znamy, bardzo się zmienią, to jakiś nowy rodzaj platformy może stać się dominujący i zmienią się reguły.

PYTHON

57

Na przykład odejście od architektury von Neumanna?

Guido: Nawet o tym nie myślałem, ale oczywiście to także jest możliwe. Rozważałem raczej to, co się wydarzy, kiedy dominującym urządzeniem komputerowym staną się telefony komórkowe. Telefonom komórkowym brakuje zaledwie kilku lat, aby uzyskały moc obliczeniową standardow ych laptopów. M ożna zatem sądzić, że za kilka lat telefony komórkowe będą miały takie możliwości (pomijając możliwości klawiatury i ekranu), że laptopy przestaną być potrzebne. Może się zdarzyć, że we wszystkich telefonach kom órkow ych, niezależnie od tego, w jakim kierunku rozwinie się ta platform a, znajdzie się JVM lub jakieś inne standardowe środowisko, w którym CPython nie będzie zbyt dobrym rozwiązaniem, a jakaś inna implementacja Pythona będzie działała lepiej. Powstaje także pytanie, co zrobimy, kiedy będziemy mieli w układzie 64 rdzenie, niezależnie od tego, czy będzie to w laptopie, czy telefonie komórkowym. Nie mam pojęcia, czy to bardzo zmieni paradygm at program ow ania dla większości działań, które wykonujemy. Może pojawić się zastosowanie dla pew nych języków pozwalających na specyfikowanie niezwykle subtelnych procesów współbieżnych, ale w większości przypadków przeciętny programista nie potrafi pisać prawidłowego kodu zapewniającego bezpieczeństwo wątków. Założenie, że pojawienie się wielu rdzeni zmusi ich do nauczenia się tej umiejętności, jest raczej mało realne. Spodziewam się, że systemy wielordzeniow e z pew nością będą przydatne, ale będą one wykorzystywane dla współbieżności na większą skalę. To jest zresztą lepsze rozwiązanie, poniew aż przy olbrzymiej różnicy kosztów pom iędzy trafieniam i a chybieniami w pamięci podręcznej pamięć główna nie spełnia już funkcji pamięci współdzielonej. Chcemy, aby procesy były w maksymalnym stopniu wyizolowane. W ja ki sposób powinniśmy obsługiwać współbieżność? Na jakim poziomie powinniśmy obsługiwać ten problem lub go rozwiązywać?

Guido: W moim odczuciu pisanie kodu jednowątkowego jest wystarczająco trudne, a pisanie kodu wielowątkowego znacznie trudniejsze — jest tak trudne, że większość program istów, włącznie ze m ną, nie potrafi robić tego dobrze. Dlatego nie sądzę, że szczegółowe prymitywy synchronizacyjne oraz współdzielona pamięć to dobre rozwiązanie — zam iast nich chętniej widziałbym pow rót rozwiązań bazujących na przekazywaniu kom unikatów . Jestem przekonany, że modyfikacja wszystkich języków program ow ania polegająca na dodaniu konstrukcji synchronizacyjnych nie jest dobrym pomysłem. Nie wierzę również, by próba usunięcia biblioteki GIL z języka CPython się sprawdziła. Myślę, że właściwym elem entem łam igłówki jest obsługa zarządzania wieloma procesami (w odróżnieniu od wątków). Z tego powodu wersje Python 2.6 oraz 3.0 będą wyposażone w nowy moduł standardowej biblioteki — obsługujący przetwarzanie

58

ROZDZIAŁ

DRUGI

równoległe. Moduł ten będzie oferował API podobny do API modułu obsługi wątków, ale oferujący operacje zarządzania procesami. Dodatkową cechą jest obsługa procesów działających na różnych hostach!

Rozwiązania praktyczne i doświadczenie Czy istnieją jakieś narzędzia lub własności, których pana zdaniem brakuje podczas pisania programów?

Guido: Gdybym potrafił szkicować na komputerze tak sprawnie jak za pomocą ołówka i kawałka papieru, mógłbym wykonywać więcej szkiców w czasie, gdy intensywnie myślę nad projektem . O bawiam się, że będę zmuszony czekać, aż mysz zostanie powszechnie zastąpiona rysikiem (lub palcem), dzięki czemu będzie można rysować na ekranie. Chociaż um iem dość dobrze posługiwać się ołówkiem i kartką, czuję się bardzo upośledzony, kiedy używam dowolnego kom puterow ego narzędzia do rysowania. Być może zdolność do szkicowania odziedziczyłem po moim ojcu, który był architektem i bardzo często szkicował. Z tego pow odu bardzo dużo rysowałem jako nastolatek. Z drugiej strony, kiedy przeglądam obszerny kod, nie wiem nawet, czego może mi brakować. Programiści Javy mają środowiska IDE, dzięki którym mogą szybko odpowiedzieć na pytanie, skąd jest wywoływana ta m etoda lub gdzie przypisano wartość do tej zmiennej. W przypadku dużych programów w Pythonie taka własność także by się przydała, ale analiza statyczna potrzebna do realizacji tej funkcji jest trudniejsza ze względu na bardziej dynamiczną naturę Pythona. W ja k i sposób testuje pan i debuguje kod?

Guido: To zależy od tego, jakie rozwiązanie jest właściwe w określonym przypadku. Podczas pisania kodu wykonuję dużo testów, ale stosuję różne m etody testowania dla różnych projektów. Przy pisaniu prostego i w całości zalgorytmizowanego kodu zwykle sprawdzają się testy jednostkowe. Z kolei jeśli piszę kod, który jest w wysokim stopniu interaktyw ny lub kom unikuje się z klasycznymi interfejsami API, często wykonuję wiele testów ręcznie, posługuję się wówczas historią wydawanych poleceń w wierszu polecenia lub odświeżam stronę w przeglądarce. Przykładem (dość ekstremalnym) sytuacji, kiedy nie można dobrze napisać testu jednostkowego, jest tworzenie skryptu, którego jedyny cel to zamknięcie bieżącej maszyny. Z pewnością dałoby się stworzyć atrapę wykonującą zamknięcie systemu, ale ten element także należałoby przetestować. Bez tego trudno byłoby stwierdzić, czy skrypt rzeczywiście działa. Testowanie program u w różnych środowiskach również jest trudne do zautomatyzowania. System Buildbot świetnie sprawdza się przy dużych systemach, ale nakład pracy potrzebny do jego skonfigurowania jest znaczny, dlatego w przypadku

PYTHON

59

mniejszych systemów pozostają ręczne metody zapewniania jakości. Mam dość dobrą intuicję podczas wykonywania testów jakościowych, ale niestety nie jest to łatwe do objaśnienia. Kiedy należy uczyć debugowania? I w ja k i sposób?

Guido: Ciągle. Debugujemy całe życie. Niedawno debugowałem problem z kolejką mojego sześcioletniego syna, poniew aż w pew nym punkcie toru wykolejała się. Debugowanie zazwyczaj sprowadza się do zejścia w dół o jeden bądź dwa poziomy abstrakcji. Proces ten jest w spom agany zatrzym aniem się w celu uważniejszego przyjrzenia się problemowi, pomyślenia oraz (czasami) użycia właściwych narzędzi. Nie sądzę, aby istniał jeden praw idłow y sposób debugow ania, którego m ożna by nauczać w pewnym momencie, nawet dla tak specyficznego celu, jak debugowanie błędów w programie. Istnieje niewiarygodnie szerokie spektrum możliwych przyczyn błędów w program ach. Należą do nich zwykłe literówki, chwilowe zaćmienia w m yśleniu (ang. thinkos), ukryte ograniczenia abstrakcji oraz wyraźne błędy w abstrakcjach lub ich implementacjach. Właściwe podejście do debugowania jest różne dla różnych przypadków. Narzędzia są używane głównie wtedy, gdy wymagana analiza (uważne przyglądanie się) jest żm udna i m onotonna. Zauważyłem, że programiści Pythona często potrzebują mniej narzędzi, ponieważ przestrzeń wyszukiwania (debugowany program) jest znacznie mniejsza. W ja k i sposób wznawia pan proces programowania?

Guido: To jest bardzo interesujące pytanie. Nie przypom inam sobie, abym kiedykolwiek specjalnie zastanawiał się nad tym, w jaki sposób to robię, choć oczywiście robię to ciągle. Najczęściej wykorzystuję narzędzia kontroli wersji: kiedy wracam do projektu, wykonuję operację d iff pom iędzy m oją przestrzenią roboczą a repozytorium . Z tego mogę wyciągnąć wniosek, w jakim punkcie jestem. Jeśli muszę przerwać pisanie kodu, to pozostaw iam znaczniki XXX w miejscach niedokończonych. Informują mnie one o specyficznych zadaniach do wykonania. Czasami używam mechanizmu, który zapożyczyłem od Lamberta Meertensa jakieś 25 lat temu: pozostawiam określony znacznik w bieżącym pliku z kodem źródłowym, w miejscu wskazywanym przez kursor. Na cześć Lamberta Meertensa stosuję znacznik „HIRO” . Jest to pospolite słowo z języka holenderskiego, oznaczające „tu ta j” . Wybrałem je ze względu na małe prawdopodobieństwo jego wystąpienia w ostatecznym kodzie. W firmie Google używamy narzędzi zintegrowanych z systemem Perforce. Narzędzia te pomagają mi na jeszcze wcześniejszym etapie: kiedy przychodzę do pracy, mogę uruchom ić polecenie, które wyświetla wszystkie niezakończone projekty w moim obszarze roboczym. W ten sposób przypomina mi, nad jakimi projektami pracowałem poprzedniego dnia. Prowadzę również dziennik, w którym od czasu do czasu zapisuję ciągi znaków trudne do zapamiętania (na przykład polecenia powłoki lub adresy URL).

60

ROZDZIAŁ

DRUGI

Pomagają mi one w w ykonyw aniu specyficznych zadań związanych z bieżącym projektem. Przykładem takiego ciągu może być adres URL do strony ze statystykami serwera lub polecenie powłoki, które kompiluje komponenty, nad którymi pracuję. Czy ma pan jakieś sugestie dotyczące projektowania interfejsów lub API?

Guido: To kolejny obszar, w którym nie poświęciłem zbyt wiele uwagi na opracowanie najlepszego procesu, m im o że opracow ałem m nóstw o interfejsów (czy też API). Chciałbym móc umieścić w tym miejscu wypowiedź Josha Blocha na ten temat. Mówił on o projektowaniu API w Javie, ale większość z tego, co powiedział, ma zastosowanie do dowolnego języka. Istnieje wiele prostych porad, na przykład wybieranie czytelnych nazw (rzeczowników dla klas, czasowników dla metod), unikanie skrótów, stosowanie spójnych nazw. U dostępnianie jak najmniejszej liczby m etod, które wspólnie zapewniają m aksym alną elastyczność itp. Josh Bloch posiada wielką umiejętność utrzymywania krótkiej listy argumentów: dwa do trzech argumentów to maksymalna liczba, jakiej m ożna użyć bez obaw o to, że użytkow nicy będą mieli problem y z zapamiętaniem kolejności. Najgorsze, jeśli obok siebie jest kilka argumentów tego samego typu. Przypadkowa zamiana kolejności może pozostać niezauważona przez bardzo długi czas. Kilka rzeczy szczególnie mnie irytuje: po pierwsze, co jest charakterystyczne dla języków dynam icznych, typ zwracanej wartości m etody nie pow inien zależeć od wartości jednego z argum entów . Jeśli tak jest, to tru dno zrozumieć, co zwraca m etoda, o ile nie znam y relacji argum entu ze zwracanym wynikiem — może się zdarzyć, że argum ent określający typ jest przekazywany ze zmiennej, której zawartości nie da się łatw o odgadnąć czytając kod. Po drugie, nie lubię argumentów-flag — to znaczy takich, których celem jest zmiana działania metody w zasadniczy sposób. Przy tak projektowanych interfejsach API flaga zawsze jest stała w obserwowanej liście param etrów. W ywołanie byłoby bardziej czytelne, gdyby interfejs API zawierał oddzielne metody: po jednej dla każdej wartości flagi. Kolejna rzecz, której nie lubię, to interfejsy API, które nie dają jasności co do tego, czy zwracają now y obiekt, czy też modyfikują obiekt istniejący. Dlatego właśnie w Pythonie m etoda s o r t( ) nie zwraca wartości: to podkreśla fakt, że modyfikuje istniejącą listę. Alternatywą dla m etody s o r t( ) jest w budow ana funkcja sorted(), która zwraca nową, posortowaną listę. Czy programiści aplikacyjni powinni stosować regułę „mniej znaczy więcej"? W ja k i sposób powinni oni upraszczać interfejs użytkownika w celu skrócenia procesu nauki posługiwania się aplikacją?

Guido: Jeśli chodzi o graficzne interfejsy użytkownika, stanowisko „mniej znaczy więcej” zdobywa coraz więcej zwolenników. Fundacja Mozilla zatrudniła do projektowania interfejsów użytkownika Azę Raskina, syna nieżyjącego już Jefa

PYTHON

61

Raskina (współprojektanta jednego z pierwszych interfejsów użytkownika komputera Macintosh). Firefox 3 to jeden z przykładów interfejsów użytkownika, gwarantujących duże możliwości bez konieczności stosowania przycisków, konfiguracji, preferencji i tym podobnych rzeczy. Inteligentny pasek lokalizacji obserwuje informacje wpisywane przez użytkownika, porównuje z adresami stron przeglądanymi wcześniej i dostarcza przydatnych sugestii. Jeśli użytkow nik zignoruje sugestie, przeglądarka spróbuje interpretować wpisywane informacje jako URL, a jeśli i to się nie powiedzie, jako zapytanie Google. To bardzo inteligentny mechanizm! Zastępuje trzy bądź cztery funkcje, które wymagałyby zastosowania osobnych przycisków lub pozycji w menu. Jest to odzwierciedlenie tego, co Jef i Aza mówili od wielu lat: klawiatura to urządzenie wejściowe o olbrzymich możliwościach. Lepiej opracować now e sposoby jej wykorzystania, niż robić wszystko myszą — najwolniejszym spośród wszystkich urządzeń wejściowych. Piękno tego rozwiązania polega na tym, że nie wymaga ono stosowania specjalnego sprzętu. Nie jest to zatem rozwiązanie rodem z literatury science fiction w rodzaju hełmów do wirtualnej rzeczywistości, czujników ruchu źrenic, nie mówiąc już o wykrywaczach fal mózgowych. Jest oczywiście wiele do zrobienia — na przykład okno dialogowe Preferencje w Firefoksie sprawia zatrważające wrażenie na każdym, kto m iał do czynienia z narzędziami firmy Microsoft — tam były co najmniej dwa poziomy zakładek i wiele okien dialogowych ukrytych w dziwnych miejscach. W jaki sposób mam zapamiętać, że aby wyłączyć obsługę JavaScript, muszę przejść do zakładki Zawartość? Czy opcje dotyczące plików cookie są w zakładce Prywatność, czy Zabezpieczenia? Być może w Firefoksie 4 zamiast okna dialogowego Preferencje znajdzie się inteligentna funkcja pozwalająca na wpisywanie słów kluczowych — jeśli na przykład zacznę wpisywać „has”, to znajdę się w obszarze konfigurowania haseł. Jakie wnioski z lekcji na tem at powstania, dalszego rozwoju i przystosowania się pańskiego języka do warunków współczesnych mogą wyciągnąć programiści, którzy tworzą systemy komputerowe dziś oraz będą je tworzyć w najbliższej przyszłości?

Guido: Przychodzi mi do głowy jedna lub dwie myśli na ten temat. Nie jestem typem filozofa, dlatego pytanie tego rodzaju nie należy do moich ulubionych ani też nie m am na nie gotowej odpowiedzi. Jest jednak jeden m echanizm, którego potrzebę istnienia szybko zauważyłem i który dobrze zaim plem entow ałem w Pythonie (w poprzedniku Pythona — języku ABC go nie było). Otóż system powinien zapewniać możliwość rozszerzania przez użytkowników. Co więcej, duży system powinien być rozszerzalny na dwóch poziomach lub większej ich liczbie. Od chwili pierwszego wydania Pythona do użytku publicznego otrzymywałem prośby o zmodyfikowanie języka, tak by uwzględniał określone rodzaje przypadków użycia. Moją pierwszą odpowiedzią na takie prośby zawsze jest sugestia napisania fragmentu

62

ROZDZIAŁ

DRUGI

kodu w Pythonie, spełniającego potrzeby użytkownika, i umieszczenia go w osobnym module do własnego użytku. To jest pierwszy poziom rozszerzalności — jeśli jakiś zestaw funkcji jest wystarczająco przydatny, może się znaleźć w bibliotece standardowej. Drugi poziom rozszerzalności polega na napisaniu m odułu rozszerzenia w języku C (albo C++ bądź innych językach). W modułach rozszerzeń można wykonać określone operacje, których nie da się w ykonać w czystym Pythonie (choć z biegiem lat możliwości czystego Pythona się poprawiły). Zam iast zmieniać sam język, wolę udostępnić interfejs API na poziomie języka C. Dzięki temu w m odułach rozszerzeń można wykonywać działania na wewnętrznych strukturach danych Pythona. Zmiany w języku stwarzają wiele problem ów związanych ze zgodnością, zachowaniem odpowiedniej jakości, klarowności semantyki itp. Poza tym rozwidlenia języka mogą się zdarzyć, kiedy ludzie sobie pom agają poprzez zmianę im plem entacji języka w swojej własnej kopii interpretera, która później trafia do innych. Takie rozwidlenia sprawiają wiele problemów — na przykład z pielęgnacją prywatnych zmian podczas ew oluow ania rdzenia języka. Problem sprawia również scalanie niezależnie rozszerzanych wersji, które użytkownicy chcą ze sobą połączyć. Moduły rozszerzeń pozwalają na uniknięcie tych problem ów . W praktyce większość własności wymaganych w rozszerzeniach jest już dostępna w interfejsie API języka C. Dzięki temu zaimplementowanie określonego rozszerzenia rzadko wymaga wprowadzania zmian w API języka C. Inna myśl, jaka przychodzi mi do głowy, jest taka: za pierwszym razem nie da się zrobić wszystkiego dobrze. We wczesnej fazie rozwoju, kiedy jest mało użytkowników, m ożna bez obaw wprow adzać drastyczne zm iany natychm iast po zauw ażeniu problemu. Nie trzeba się m artwić wsteczną zgodnością. Świetną anegdotą, którą lubię cytować, a która została potw ierdzona jako autentyczna przez świadka, jest historia o tym, jak Stuart Feldman, autor narzędzia Make w Uniksie v7, został poproszony o modyfikację zależności konstrukcji składniowej pliku Makefile dotyczącej znaków tw ardych tabulacji. Jego odpowiedź była mniej więcej taka: przyznał, że problem występuje, ale dodał, że jest już za późno, by wprowadzić poprawkę, ponieważ narzędzia używało już kilkudziesięciu użytkowników. Im bardziej wzrasta liczebność bazy użytkowników, tym większą konserwatywność trzeba zachować. W pewnym m omencie wsteczna zgodność jest naw et absolutną koniecznością. Dochodzi się bowiem wówczas do punktu, w którym jest tak wiele niezgodności, że zachowanie zgodności wstecz staje się już niemożliwe. Dobrą strategią postępowania z takim problemem jest to, co właśnie robię z Pythonem 3.0: ogłaszam przerw anie wstecznej zgodności dla konkretnej wersji, wykorzystuję okazję do popraw ienia tylu błędów, ile się da, i daję społeczności dużo czasu na realizację transformacji. W przypadku Pythona planujemy równolegle obsługiwać wersje 2.6 i 3.0 przez długi czas — znacznie dłuższy niż standardowy czas wspierania starszych wersji. Oferujemy również kilka strategii transform acji: autom atyczne narzędzie konwersji kodu

PYTHON

63

źródłow ego (niestety dalekie od doskonałości) w połączeniu z opcjonalnym i ostrzeżeniami w wersji 2.6 na temat używania własności, które zmienią się w wersji 3.0 (szczególnie jeśli program do konwersji nie będzie w stanie prawidłowo rozpoznać sytuacji). Ponadto um ożliwimy przeniesienie pew nych własności wersji 3.0 do wersji 2.6. Jednocześnie wersja 3.0 nie jest pisana całkowicie od początku (inaczej niż Perl 6 lub Zope 3 w świecie Pythona), przez co zmniejsza się ryzyko przypadkowego usunięcia istotnego zestawu funkcji. W ostatnich czterech lub pięciu latach zauważyłem znaczny wzrost zainteresowania językami dynamicznymi w dużych firmach. Najpierw PHP, w pewnym kontekście Ruby, zdecydowanie Python, choć w innych kontekstach i szczególnie Google. Wydaje mi się to interesujące. Zastanawiam się, gdzie ci ludzie byli 2 0 lat temu, kiedy używano takich języków , ja k Tcl, Perl czy nieco później Pythona, do wykonywania wielu użytecznych funkcji. Czy zauważył pan potrzebę przekształcenia tych języków na bardziej przyjazne korporacjom, cokolwiek by to miało znaczyć?

Guido: Język zwykle staje się przyjazny korporacjom, kiedy naprawdę inteligentni ludzie przestają się nim interesować i muszą sobie z nim radzić ludzie o bardziej przeciętnych umiejętnościach. Nie wiem, czy Python jest trudniejszy do używania przez przeciętnych programistów. Wydaje się, że za pomocą Pythona można wyrządzić sporo szkód, poniew aż jest to w całości język interpretow any. Z drugiej strony, jeśli napisze się napraw dę rozbudow any kod i nie wykona odpowiedniej liczby testów, można nie mieć pojęcia, co kod rzeczywiście robi. Powiedział pan, że linia kodu w Pythonie, Ruby, Perlu czy PHP może wymagać 10 linii w Javie.

Guido: Często tak jest. Myślę, że poziom akceptacji w świecie korporacji, pomimo istnienia pewnych użytecznych pakietów funkcjonalnych, w dużym stopniu zależy od poglądów konserw atyw nych menedżerów. Proszę sobie wyobrazić ludzi odpowiedzialnych za zasoby inform atyczne dla 100 000 ludzi w firmie, w której produkty inform atyczne nie są głównymi produktam i — na przykład fabryce samochodów, firmie ubezpieczeniowej lub innej dziedzinie — ale takiej, gdzie wszystko, co się robi, wymaga użycia kom puterów . Ludzie kierujący taką infrastrukturą z konieczności muszą być bardzo konserwatywni. Będą oni wybierali produkty znanych marek, na przykład Sun lub Microsoft, poniew aż wiedzą, że choć te firmy ciągle popełniają błędy, to są zobligowane je popraw iać, naw et jeśli czasem zajmuje to pięć lat. Takiego poczucia spokoju nie dają przeciętnemu menedżerowi produkty open source. Nie wiem dokładnie, czy, jak i kiedy to się zmieni. Istnieje możliwość, że gdyby firmy Microsoft lub Sun nagle zaczęły wspierać Pythona na swoich maszynach wirtualnych, programiści w korporacjach odkryliby, że mogą uzyskać większą produktyw ność bez kłopotów związanych z używaniem bardziej zaaw ansowanych języków.

64

ROZDZIAŁ

DRUGI

R OZ DZ I AŁ TRZECI

APL

W końcu lat pięćdziesiątych Kenneth Iverson, wówczas student Uniwersytetu w Harvardzie, opracował rozszerzenie notacji m atem atycznej umożliwiające precyzyjne opisywanie algorytmów. Później zespół, w którego skład weszli między innymi Adin Falkoff i inni pracownicy firmy IBM, stopniowo przekształcił tę notację w kom pletny język programowania znany jako APL. W języku wykorzystano rozszerzony zestaw znaków wymagający specjalnej klawiatury. Kod w tym języku przypomina ciągi czasami nieznanych symboli, ale wewnętrzna spójność języka sprawia, że jest on łatwy do nauczenia się. Z kolei doskonałe własności przetwarzania tablic sprawiają, że stanowi on niezwykle potężne narzędzie. Jego następcy, języki J i K, są kontynuacją tradycji spójności i dużych możliwości działań algebraicznych języka APL.

65

Papier i ołówek Czytałem artykuł napisany przez pana wspólnie z Kenem Iversonem, zatytułowany „The Design o f APL". Napisano w nim, że przez pierwsze siedem lub osiem lat język był tworzony całkowicie bez udziału komputerów! Dzięki temu można zmieniać aspekty projektowe, nie przejmując się problemami zgodności. Jaki był wpływ pierwszych implementacji na ewolucję języka?

A din Falkoff: Zgadza się, w pierwszych latach ewolucji języka APL, kiedy jego jedyną nazwą była „notacja Iversona” , język był stosow any głównie do obliczeń matematycznych wykonywanych na papierze, do analizy systemów cyfrowych oraz w edukacji. W tam tym czasie program ow anie uważaliśmy za dział m atem atyki związany z odkryciem i projektow aniem algorytmów. Symboliczna forma notacji sprzyjała takiem u rozum ieniu. Atrakcyjność notacji jako języka program ow ania ogólnego przeznaczenia stała się oczywista po pewnym czasie, a w zm acniały ją wysiłki pewnych ludzi (w szczególności Herba H ellerm ana z firmy IBM), którzy eksperymentowali z maszynowymi implementacjami znaczących elementów notacji, włącznie z prym ityw nym i funkcjami oraz operacjami na tablicach. Niemniej jednak praw dą jest, że w tym okresie mieliśmy całkowitą swobodę projektow ania języka bez obaw o problemy zgodności. Najbardziej znacząca, wczesna ewolucja języka obejmowała dwa etapy. Pierwszym było napisanie i opublikowanie pozycji „The Formal Description of System 360” [IBM Systems Journal, 1964]. Aby możliwe było formalne opisanie pewnych zachowań tego nowo zaprojektowanego systemu komputerowego, konieczne okazały się pewne dodatki i modyfikacje w notacji. Zostały one opisane w książce Iversona (A Programming Language [Wiley]). Drugi etap to zaprojektow anie klaw iatury dla terminali bazujących na układzie Selectric. Zrobiliśmy to, przewidując zastosowanie języka w komputerach. W ymusiło to znaczące ograniczenia wynikające z liniowej natury pisania na klawiaturze oraz wymagań mechanicznych mechanizmu Selectric. Wiele szczegółów na temat wpływu tych dwóch czynników na ewolucję języka można znaleźć w cytowanym przez pana artykule „The Design of APL” [IBM Journal o f Research and Development, 1974]. Pierwszą kompleksową implementacją języka była oczywiście implementacja APL\360. W prow adzała m echanizm y pisania zdefiniowanych funkcji (tzn. programów) — coś, co rozum iało się samo przez się w przypadku używania ołówka i papieru — oraz zarządzania środowiskiem wykonywania programów. Pojęcia, które wtedy w prow adzono, włącznie z systemem przestrzeni roboczych i bibliotek, regułami zasięgów nazw oraz stosow aniem współdzielonych zm iennych do kom unikacji z innym i systemami, przetrw ały bez znaczących zmian. Programy napisane dla implementacji APL\360 działają bez modyfikacji w nowoczesnych systemach APL, które są mi znane.

66

ROZDZIAŁ

TRZECI

Trzeba uczciwie powiedzieć, że istnienie im plem entacji APL\360 m iało wpływ na późniejszą ewolucję języka. Wynikało to ze stosowania zasady, że nowe pomysły muszą zawsze uwzględniać poprzednie, oraz — co oczywiste — ze stałej krytycznej analizy sposobu działania języka dla nowych, różnorodnych aplikacji. W ja k i sposób wyobrażaliście sobie typowego programistę w momencie opracowywania składni?

Adin: Nie próbowaliśmy w taki sposób przyporządkowywać programistów do składni. Zamiast tego postrzegaliśmy język jako m edium komunikacji wygodne dla ludzi, które przy okazji powinno sprawdzać się w komunikacji z komputerami. Doszliśmy do wniosku, że dla ludzi najwygodniejszy będzie język symboliczny podobny do algebry. Jednocześnie czuliśmy, że docenią oni możliwości symbolicznej reprezentacji, ponieważ ułatw ia ona form alne m anipulow anie wyrażeniami, co prowadziło do bardziej efektywnej analizy i syntezy algorytmów. W szczególności nie zakładaliśm y konieczności posiadania obszernego doświadczenia i wiedzy matematycznej. Używaliśmy systemu APL do nauczania programowania na poziomie szkoły podstawowej i średniej i odnieśliśmy znaczące sukcesy. Po pewnym czasie odkryliśmy, że niektórzy z najbardziej utalentow anych i doświadczonych programistów przywiązywali się do języka APL, używali go i brali udział w jego rozwoju. Czy złożona składnia ogranicza zakres akceptacji języka APL?

Adin: Składnia języka APL oraz jej wpływ na akceptację języka są warte przedyskutowania, choć nie zgadzam się z twierdzeniem, że składnia ta jest złożona. Język APL bazuje na notacji matematycznej i wyrażeniach algebraicznych. Zostały one uporządkowane dzięki usunięciu nietypowych form i uogólnieniu zaakceptowanej notacji. Na przykład zdecydowano, że nazwy funkcji dwuargumentowych, takich jak dodaw anie bądź mnożenie, będą wpisywane pomiędzy dw om a argum entam i, natomiast w przypadku funkcji jednoargumentowych symbole funkcji będą pisane przed argumentem, bez takich wyjątków, jakie występują w tradycyjnych notacjach m atem atycznych. Tak więc w artość bezwzględna w języku APL była oznaczana jedną pionową kreską przed argumentem, a nie za pomocą kresek po obu stronach, natomiast symbol silni występował w języku APL przed argumentem, a nie za nim. Pod tym względem składnia języka APL była prostsza od składni jego historycznego źródła. Składnia języka APL była również prostsza od notacji algebraicznej oraz innych języków programowania pod innym ważnym względem: reguły pierwszeństwa do obliczania wartości wyrażeń w APL są takie, że dla wszystkich funkcji obowiązują te same reguły pierwszeństwa, a użytkownik nie musi pamiętać, czy potęgowanie jest wykonywane przed mnożeniem lub jakie jest miejsce w hierarchii dla funkcji zdefiniowanych.

APL

67

Obowiązuje prosta reguła, zgodnie z którą podwyrażenie znajdujące się na skrajnej prawej pozycji jest obliczane w pierwszej kolejności. Z tych powodów nie uważam, że składnia języka APL ograniczyła jego akceptację. Efekt ten wynikał raczej ze stosowania wielu symboli spoza alfabetu, które nie były łatwo dostępne na standardowych klawiaturach. Dlaczego zdecydowano się zastosować specjalny zestaw znaków? W ja k i sposób ten zestaw znaków ewoluował w czasie?

Adin: Zestaw znaków zdefiniow ano poprzez użycie konw encjonalnej notacji matematycznej uzupełnionej kilkoma literami greckimi oraz kilkoma symbolami wizualnymi — takimi jak kwadrat. Swoją rolę odegrały także liniowe ograniczenia pisania na maszynie, co doprowadziło do powstania pewnych znaków, które można stworzyć przez przekreślanie. Później, kiedy terminale i urządzenia wejściowe stawały się bardziej uniwersalne, te zespolone znaki stawały się osobnymi podstawowymi symbolami. W prowadzono też kilka nowych znaków w celu zobrazowania nowych własności, na przykład romb jako separator instrukcji. Czy świadomie zdecydowano o stosowaniu takiego zestawu symboli w celu wydajniejszego używania ograniczonych zasobów czasowych?

Adin: Na zestaw znaków z całą pewnością miało wpływ dążenie do optymalizacji używania ograniczonych zasobów dostępnych w tam tym czasie, ale spójną, symboliczną postać opracow ano i utrzym yw ano ze względu na przekonanie, że ułatwiała ona analizę i formalne manipulowanie wyrażeniami. Poza tym zwięzłość kodu w porównaniu z jego odpowiednikami napisanymi w innych językach ułatwiała zrozumienie logicznego przepływu program ów zapisanych w APL przez osoby czytające kod. Wydaje mi się, że nauczenie się języka APL wymaga wiele pracy. Szczególnie trudne je st poznanie zestawu znaków. Czy istniało coś w rodzaju naturalnej selekcji, co oznaczało, że programiści APL byli ekspertami języka? Czy byli oni bardziej wydajni? Czy pisali kod o wyższej jakości, z mniejszą liczbą błędów?

Adin: Nauczenie się języka APL w takim stopniu, aby móc pisać programy, na przykład tak jak w języku FORTRAN, nie było ani trudne, ani czasochłonne. Programowanie w języku APL było bardziej wydajne ze względu na prostotę reguł oraz dostępność prymitywnych funkcji m anipulowania danymi, takich jak sortowanie, lub funkcji matematycznych, jak odwracanie macierzy. Te czynniki przyczyniały się do spójności program ów w APL, dzięki czemu m ożna je było łatwiej analizować i debugować. Wydajność wynikała również z implementacji języka APL — wykorzystania przestrzeni roboczych wraz z ich wszystkimi użytecznymi własnościami oraz interaktywnych, terminalowych systemów interpretacyjnych.

68

ROZDZIAŁ

TRZECI

Zwięzła forma wyrażeń może okazać się niezwykle przydatna w urządzeniach wyposażonych w m ały ekran, takich ja k komputer PDA lub smartphone. Czy fakt, że język APL był po raz pierwszy zakodowany na dużych maszynach, takich ja k IBM S ystem /360, ogranicza możliwości jego rozszerzania w kierunku wsparcia dla nowoczesnych projektów wymagających połączeń sieciowych oraz danych multimedialnych ?

Adin: Im plem entacja języka APL na urządzeniach podręcznych dostarczyłaby co najmniej bardzo rozbudowanego kalkulatora. Nie widzę problem u z sieciami i multimediami. Takie systemy były pisane w języku APL od bardzo długiego czasu. Narzędzia zarządzania graficznymi interfejsami użytkownika są powszechnie dostępne w nowoczesnych systemach APL. We wczesnej fazie rozwoju systemów APL wprowadzono mechanizmy zarządzania systemami operacyjnymi hostów oraz ich sprzętem w postaci funkcji APL. Ze względu na ekonomiczność języka APL, używano go do zarządzania sieciami w komercyjnych systemach z podziałem czasu (ang. timesharing). Prawdą jest, że pierwsze komercyjne systemy APL były zakodow ane na dużych maszynach, ale pierwsze implementacje, w których demonstrowano możliwości języka, zrealizowano na stosunkowo m ałych maszynach, takich jak kom putery z rodziny IBM 1620, IBM 1130 oraz IBM 1500. Te małe maszyny miały znaczące zastosowanie w aplikacjach edukacyjnych. Była naw et im plem entacja na eksperym entalnym kom puterze biurkowym o nazwie LC (od ang. Low Cost — tani), wyposażonym w niewielką pamięć i m ało pojem ny dysk. Ewolucję im plem entacji IBM języka APL opisano dość szczegółowo w artykule „The IBM Family of APL Systems” [IBM Systems Journal, 1991].

Podstawowe zasady Czy pogoń za standaryzacją była świadomą decyzją?

Adin: Rzeczywiście rozpoczęliśmy standaryzację stosunkowo wcześnie. Napisałem na ten tem at artykuł i częściowo tworzyliśm y standard ISO. Zawsze dążyliśmy do standaryzacji i w dużym stopniu nam się to udało. Odradzaliśm y wszystkim m anipulow anie podstaw ow ym i strukturam i języka, dodaw anie elem entów komplikujących składnię lub naruszanie podstawowych zasad, które próbowaliśmy zachowywać. Co w pana opinii było głównym czynnikiem motywującym do standaryzacji: kompatybilność czy czystość pojęciowa?

Adin: Dążenie do standaryzacji jest sprawą ekonomii. Z całą pewnością chcieliśmy, aby język APL był konkurencyjny ekonomicznie. Ponieważ implementowało go i używało wiele osób, opracowanie standardu wydawało się dobrym pomysłem.

APL

69

Kilku różnych producentów posługiwało się różnymi kompilatorami APL. Co by się stało, bez silnej standaryzacji, gdyby istniało rozszerzenie działające w jednym systemie, ale niedziałające w innym?

Adin: Nad tymi zagadnieniami pilnie pracują komisje standaryzacyjne języka APL. Czyniono również wysiłki, by uzyskać kom prom is pom iędzy rozszerzalnością a czystością. Chciałby pan, aby za pomocą języka APL rozwiązywano problemy, o jakich pan nie pomyślał, ale nie chciałby pan, aby zatraciła się zasadnicza natura systemu. W jakiej kondycji, pana zdaniem, będzie język za czterdzieści lat? Czy zastosowane przez pana zasady projektowe nadal będą miały zastosowanie?

Adin: Myślę, że tak. Naprawdę nie widzę żadnych istotnych przeszkód. Czyjest tak dlatego, że poświęcił pan wiele czasu na uważny projekt, czy też ze względu na pańską obszerną wiedzę teoretyczną z algebry?

Adin: Uważam, że byliśmy kilkoma inteligentnymi ludźmi wierzącymi w prostotę i praktyczne zasady, dążącymi do stosowania ich w naszym projekcie. Nauka i próby zapamiętywania wszystkich reguł w innych językach były według mnie tak trudne, że starałem się zachować prostotę do tego stopnia, aby móc je stosować. Pewne elem enty sposobu naszego myślenia m ożna zaobserwować w artykułach, zwłaszcza tych, których współautorami byliśmy Iverson i ja. Później sam napisałem artykuł „A Note on Pattern Matching: W here do you find the m atch to an empty array?” 1 [APL Quote Quad, 1979], w którym przeprowadziłem interesującą analizę dotyczącą małych programów i zasad algebraicznych. Uzyskane wyniki okazały się spójne i bardzo przydatne. W artykule przeanalizowałem różne możliwości. Odkryłem, że ta, która jest najprostsza do wyrażenia, sprawdza się lepiej niż pozostałe. Uważam, że budowanie języka z niewielkiego zbioru zasad i odkrywanie nowych idei na podstawie tych zasad jest fascynujące. Wydaje mi się, że jest to dobry opis matem atyki. Jaka jest rola matematyki w informatyce i programowaniu?

Adin: W edług mnie informatyka jest dziedziną matematyki. Programowanie obliczeń matematycznych jest z całą pewnością częścią matematyki. Zwłaszcza analiza numeryczna, przy której stale trzeba zachowywać kompatybilność pomiędzy dyskretnymi operacjami cyfrowymi a ciągłością teoretycznej analizy. Przychodzi mi też do głowy kilka innych myśli: problemy matematyczne, które można rozwiązywać tylko poprzez intensywne obliczenia inspirujące potrzebę szybkości;

1 Nota na temat dopasowywania wzorców: gdzie można znaleźć dopasowanie do pustej tablicy? — przyp. tłum.

70

ROZDZIAŁ

TRZECI

logiczne myślenie wymagane w matematyce i przeniesione do wszystkich rodzajów programowania; pojęcie algorytmów, które są klasycznym narzędziem matematycznym, a także różne specjalizowane dziedziny m atem atyki, na przykład topologia, zapożyczone do analizy problemów obliczeniowych. Czytałem materiały, w których znalazły się sugestie pana i innych osób, że jednym z interesujących zastosowań języka APL będzie nauczanie programowania i matematyki na poziomie szkół podstawowej i średniej.

Adin: Stworzyliśmy kilka takich artykułów, zwłaszcza na początku, i mieliśmy z nimi trochę zabawy. W tamtych czasach mieliśmy do dyspozycji jedynie terminale przypominające maszyny do pisania i udostępniliśmy kilka takich terminali lokalnym pryw atnym szkołom. Jeden z tych term inali był wykorzystywany do nauczania uczniów sprawiających problemy. Przygotowaliśmy dla nich ćwiczenia do wykonania na terminalach. Zabawnie było, gdy odkryliśmy, że kilkoro uczniów, którzy według oczekiwań mieli sprawiać problem y w nauce, w łam ało się do szkoły po godzinach po to, by kontynuować pracę na terminalu. Używali terminala podłączonego do naszego systemu z podziałem czasu. A więc podobało im się tak bardzo, że musieli to robić nawet poza godzinami szkolnymi?

Adin: Tak. Wykorzystywał pan język APL do tego, by uczyć osoby nieprogramujące myślenia programistycznego. Co sprawia, że język APL jest atrakcyjny dla osób niebędących programistami?

Adin: Największą zaletą jest brak niepotrzebnego balastu. Zanim doda się dwie liczby, nie trzeba ich deklarować. Jeśli chcesz dodać 7 do 5, po prostu piszesz 7+5, zamiast mówić, że jest liczba znana jako 7 i jest liczba znana jako 5 — obie są liczbami zmiennoprzecinkowymi lub całkowitymi, a wynik jest liczbą, który chcę przechować tu albo tu. Ze względu na brak tych elementów język APL stwarzał mniejszą barierę przed wykonywaniem zamierzonych działań. Kiedy ktoś uczy się programowania, początkowe kroki zmierzające do tego celu są bardzo proste. Trzeba po prostu zapisać to, co chce się zrobić. Nie trzeba poświęcać czasu na to, by prosić kompilator o wykonanie pracy.

Adin: To prawda.

APL

71

Łatwo ruszyć z miejsca i łatwo o zabawę. Czy ta technika pozwoliła pewnym ludziom zostać programistami lub zwiększyć swój poziom wiedzy programistycznej?

Adin: Łatwa dostępność sprawia, że eksperym entowanie również staje się łatwe. Jeśli możesz eksperym entować i w ypróbowywać różne rzeczy, możesz się uczyć. Uważam, że te cechy sprzyjają rozwijaniu umiejętności programistycznych. Notacja wybrana dla języka APL różni się od tradycyjnej notacji algebraicznej.

Adin: Nie różni się aż tak bardzo... obow iązują tylko inne reguły kolejności wykonywania działań. Są bardzo proste: obliczenia wykonuje się od prawej do lewej. Czy według pana te reguły są łatwiejsze do przyswojenia?

Adin: Tak, ponieważ jest tylko jedna reguła. Nie trzeba mówić, że jeśli jest zdefiniowana funkcja, robi się to w ten sposób, a nie inny, a potęgow anie wykonuje się przed m nożeniem itd. W ystarczy jedynie powiedzieć: „Spójrz na wiersz z instrukcjam i i wykonaj je od prawej do lewej” . Czy odejście od znanej notacji i kolejności wykonywania działań w celu uzyskania większej prostoty było świadomą decyzją?

Adin: To prawda. Większa prostota i bardziej ogólne podejście. Myślę, że to głównie zasługa Iversona. Był bardzo dobry z algebry i zainteresowany nauczaniem. Jedną z rzeczy, którą bardzo lubił, była reprezentacja wielomianów, która w języku APL jest bardzo prosta. Kiedy po raz pierwszy zobaczyłem tę notację, mimo że była ona nieznana, wydawała mi się pojęciowo znacznie prostsza. Jak rozpoznaje pan prostotę w projekcie i implementacji? Czy jest to sprawa dobrego smaku, czy doświadczenia? A może dążąc do uzyskania optymalnej prostoty, stara się pan stosować rygorystyczny proces?

Adin: Uważam, że do pewnego stopnia musi to być subiektywne, ponieważ zależy trochę od doświadczenia i środowiska. Powiedziałbym, że im mniej reguł, tym implementacja i projekt są prostsze. Wyszedł pan od niewielkiego zbioru aksjomatów, na których podstawie zbudował pan dalsze reguły. Czy można uzyskać większą złożoność, jeśli rozumie się ten niewielki zbiór aksjomatów?

Adin: Spróbuję odpowiedzieć na to pytanie, posługując się przykładem kolejności wykonywania działań. Uważam, że prościej jest stosować reguły pierwszeństwa działań bazujące na prostej formule od prawej do lewej, niż stosować reguły z tabeli, które mówią, że ta funkcja będzie wykonywana jako pierwsza, a ta jako druga. To zestawienie jednej reguły z niemal nieograniczoną liczbą reguł.

72

ROZDZIAŁ

TRZECI

W każdej aplikacji trzeba skonfigurować własny zbiór zmiennych i funkcji, a dla konkretnego zastosowania łatwiejsze może okazać się napisanie kilku nowych reguł. Jeśli jednak bierzemy pod uwagę język ogólnego przeznaczenia, jakim jest APL, trzeba wyjść od możliwie najmniejszego zbioru reguł. Aby dać ludziom projektującym systemy za pomocą języka więcej możliwości rozwoju?

Adin: Ludzie, którzy tworzą aplikacje, w rzeczywistości tworzą języki. Ogólnie rzecz biorąc, programowanie dotyczy opracowywania języków odpowiednich dla określonej aplikacji. Trzeba wyrazić problem w języku specyficznym dla danej dziedziny?

Adin: Ale obiekty, zwłaszcza rzeczowniki i czasowniki — obiekty i funkcje — muszą być jakoś zdefiniowane — na przykład za pomocą języka ogólnego przeznaczenia, takiego jak APL. A zatem używa się języka APL do zdefiniow ania tych rzeczy, a następnie tworzy się operacje realizujące działania, które ma wykonywać wybrana aplikacja. Czy pańska rola polega na tworzeniu bloków budulcowych, które można wykorzystać do wyrażenia swoich dążeń?

Adin: Moja rola polega na dostarczaniu im podstaw ow ych bloków budulcowych — podstawowych narzędzi do tworzenia bloków budulcowych odpowiednich do tego, co chcą osiągnąć w dziedzinie, w której pracują. Wydaje się, że podobny cel stawiali sobie twórcy innych języków. Mam tu na myśli Chucka Moore'a, twórcę języka Forth, McCarthy'ego z jego Lispem oraz twórców Smalltalka z wczesnych lat siedemdziesiątych.

Adin: Dokładnie tak było. 0 ile wiem, McCarthy jest typem teoretyka. Jego dążeniem było opracowanie systemu do efektywnego wyrażania rachunku lambda. Nie sądzę, aby rachunek lambda był wygodny w większości zastosowań, takich jak stara, prosta algebra, z której wywodzi się język APL. Przypuśćmy, że chcę zaprojektować nowy język programowania. Jakich rad by mi pan udzielił?

Adin: Myślę, że najlepszą radą jest, by robić to, co się lubi. Coś, co przyciąga do pracy 1 pomaga w osiągnięciu celu. Przy opracowywaniu rozwiązań zawsze kierowaliśmy się osobistymi względami. Myślę, że podobnie postępują inni projektanci. Przynajmniej taki wniosek można wyciągnąć z ich wypowiedzi. Zaczyna się robić coś, co chce się robić, a potem okazuje się, że może to znaleźć ogólne zastosowanie.

APL

73

Czy podczas tworzenia języka APL mógł pan w pewnym momencie stwierdzić: „Idziemy w złym kierunku; musimy zmniejszyć złożoność" lub „Mam y do dyspozycji kilka różnych rozwiązań, możemy zunifikować je w coś znacznie prostszego"?

Adin: Mniej więcej tak było, ale zazwyczaj stawialiśmy sobie pytanie, czy to uogólnienie obejmuje to, co mamy do tej pory, lub jakie jest prawdopodobieństw o, że pozwoli nam ono na zrobienie znacznie więcej przy dalszych znacznie mniejszych komplikacjach. Poświęcaliśmy wiele uwagi w arunkom brzegowym — na przykład co się stanie z ograniczeniami, jeśli zmniejszymy jakąś wartość z 6 do 5, a następnie do 4 i w dół do zera. A zatem, m ówiąc w skrócie, stosujem y funkcję, na przykład sum owanie wektora, i sprawdzamy, co się stanie w przypadku sum owania wektora złożonego z n elementów, następnie z n m inus jeden elem ent itd., aż do m om entu, kiedy ostatecznie wektor nie będzie miał żadnego elementu. Ile wynosi suma? Powinna wynosić 0, ponieważ jest to element neutralny. Wynikiem mnożenia przez pusty wektor powinna być wartość 1, ponieważ to jest element neutralny dla tej funkcji. Wspominał pan o analizie kilku różnych rozwiązań, próbach uogólniania oraz zadawaniu sobie pytań, o to, co się stanie na przykład w przypadku zbliżania się do zera. Gdyby nie wiedział pan, że przy dokonywaniu redukcji trzeba uzyskać element neutralny dla przypadku, gdy n ma wartość 0, mógłby pan przyjrzeć się obydwu tym przypadkom i powiedzieć: „Oto poprawny wynik: ma wartość 0 w takim przypadku oraz 1 w innym przypadku, ponieważ taka jest wartość elementu neutralnego".

Adin: Zgadza się. To jest jeden z procesów, jakich używamy. To, co stanie się w szczególnych sytuacjach, ma bardzo duże znaczenie. W przypadku wydajnego stosowania języka APL stosujemy te kryteria do bardziej zaawansowanych funkcji tworzonych dla określonej aplikacji. Często prowadzi to do nieoczekiwanych, ale wartościowych uproszczeń. Czy techniki projektowe używane podczas tworzenia języka dyktują, jakich technik projektowych mogą używać programiści podczas programowania w określonym języku?

Adin: Tak, ponieważ — jak powiedziałem wcześniej — programowanie jest procesem projektowania języków. Myślę, że jest to rzecz o fundamentalnym znaczeniu, o której — o ile wiem — niezbyt często wspomina się w literaturze.

74

ROZDZIAŁ

TRZECI

Tak jest w przypadku języka Lisp, ale wydaje się, że projektanci wielu języków, które powstały później, zwłaszcza w Algolu i jego pochodnych wywodzących się z języka, nie myślą w ten sposób. Czy istnieje podział pomiędzy tym, co jest wbudowane w język, a tym, co nie jest, i czy wszystko pozostałe to druga klasa?

Adin: Co pan rozumie przez pojęcie „druga klasa”? W języku APL druga klasa podlega tym samym regułom, co pierwsza klasa i nie ma z tym większych problemów. To samo można by powiedzieć o niemal wszystkich językach, takich ja k Lisp, Scheme lub Smalltalk, ale w języku C występuje wyraźne rozgraniczenie pomiędzy operatorami a funkcjami oraz funkcjami definiowanymi przez użytkowników. Czy ostre rozgraniczenie pomiędzy tymi podmiotami jest błędem projektowym?

Adin: Nie wiem, czy nazwałbym to błędem. Myślę jednak, że prościej stosować takie same reguły do tego, co jest prymitywne, i do tego, co nie jest prymitywne. Jaki największy błąd popełnił pan przy projektowaniu lub programowaniu? Czego owo doświadczenie pana nauczyło?

Adin: Kiedy rozpoczęły się prace nad językiem APL, świadomie unikaliśm y podejm ow ania decyzji projektowych, które zaspokajały potrzeby środowiska komputerowego. Na przykład unikaliśmy używania deklaracji. Postrzegaliśmy je jako niepotrzebne obciążanie użytkownika, skoro maszyna może z łatwością określić rozmiar i typ obiektów danych na podstawie samego obiektu w czasie jego wprowadzania lub generowania. Wraz z upływem czasu, kiedy język APL zaczął być stosowany coraz częściej, unikanie czynników sprzętowych stawało się coraz trudniejsze. Praw dopodobnie największą moją osobistą pom yłką było niedocenianie postępu w sprzęcie, przez co stałem się zbyt konserw atyw ny w projektow aniu systemów. Na przykład podczas opracowyw ania wczesnych im plem entacji języka APL na kom putery PC radziłem, aby z powodów wydajnościowych pominąć najnowsze rozszerzenia języka i zastosować zamiast nich tablice ogólnego przeznaczenia i liczby zespolone. Na szczęście mnie przegłosowano. Niedługo później nastąpił znaczny postęp w dziedzinie rozm iarów pamięci w kom puterach PC oraz szybkości procesorów. Dzięki temu zastosowanie wspomnianych rozszerzeń stało się całkowicie wykonalne. Trudno myśleć o wielkich błędach popełnionych w programowaniu, ponieważ błędów m ożna się spodziewać podczas pisania program ów o rozsądnej złożoności. Sposób rozwijania się błędów, czas ich odkrycia oraz wysiłki, jakie należy podjąć, aby zlikwidować skutki ich wystąpienia, zależą od stosowanych narzędzi programistycznych. Modularyzacja i wykorzystywanie gotowych, idiomatycznych fragmentów kodu, co jest zgodne ze stylem programowania funkcyjnego przyjętym w języku APL, ograniczają generowanie i propagację błędów, zatem nie stają się one wielkimi pomyłkami. Jeśli chodzi o błędy w projektow aniu samego języka APL, w dużym stopniu udało się ich uniknąć. Pozwoliły na to nasze metody tworzenia oprogramowania: kierowanie

się kryterium zapewnienia zgodności pomiędzy projektantami a osobami pracującymi nad implementacją jako ostatecznym czynnikiem decyzyjnym; uwzględnianie uwag użytkowników zdobywających praktyczne doświadczenie w różnych aplikacjach, a także używanie języka przez nas samych, zanim projekt języka stał się zamrożony. Może się jednak zdarzyć, że to, co jedna osoba uznaje za zasadę, inna uważa za błąd, a różnic nie daje się zniwelować nawet przez długi czas. Przychodzą mi na myśl dwie rzeczy. Jedna z nich to zestaw znaków. Od samego początku były naciski na używanie słów zarezerwowanych zamiast abstrakcyjnych symboli wybranych do reprezentowania prymitywnych funkcji. Naszym zdaniem tworzyliśmy rozszerzenia matematyczne, a ewolucja notacji matematycznej zmierzała w kierunku używania symboli, które pozwalały na formalne m anipulow anie wyrażeniami. W późniejszym okresie Ken Iverson, który był żywo zainteresow any nauczaniem m atem atyki, podczas pracy nad językiem J zdecydował się na ograniczenie zestawu znaków do symboli ASCII. Dzięki temu systemy napisane w języku J stały się łatwo dostępne dla jego uczniów oraz innych osób i nie wymagały stosowania specjalizowanego sprzętu. Uważałem i nadal uważam, że należy zachować notację bazującą na symbolach. Według mnie jest ona bardziej zgodna z podejściem historycznym i — ostatecznie — bardziej czytelna. Czas pokaże, który kierunek był błędny i czy w ogóle miało to znaczenie. Przychodzi mi do głowy jeszcze druga myśl: do znaczących pomyłek w podejściu potencjalnie prow adziła interpretacja tablic ogólnego przeznaczenia, czyli tablic zawierających elementy skalarne, do których struktury istnieje dostęp w obrębie języka. Kiedy język APLA360 stał się produktem firmy IBM (była to jedna z pierwszych sytuacji, w roku 1966 lub 1967, kiedy firma IBM odłączyła swoje oprogramowanie od sprzętu), zaczęliśmy poszukiwać bardziej uniwersalnych rozszerzeń dla tablic. W związku z tym przeprowadzaliśmy badania dotyczące podstaw teoretycznych. Ostatecznie systemy APL wyposażono w przeciwstawne sposoby interpretowania elementów skalarnych wraz ze wszystkimi konsekwencjami syntaktycznymi. Zastanawiam się, jak rozwinie się ta sytuacja, kiedy ogólne zainteresowanie programowaniem równoległym stanie się ważniejsze pod względem komercyjnym.

Współbieżność Jakie są implikacje (dla projektowania aplikacji) z postrzegania danych jako kolekcji, a nie jako indywidualnych jednostek?

Adin: Jest to dość obszerny temat, o czym może świadczyć rozpowszechnienie się języków tablicowych oraz wprowadzenie prymitywów tablicowych w takich językach, jak FORTRAN. Myślę jednak, że istnieją dwa znaczące aspekty myślenia w kategoriach kolekcji.

76

ROZDZIAŁ

TRZECI

Pierwszy jest oczywiście uproszczeniem procesu myślenia — nie trzeba zajmować się szczegółami obsługi indywidualnych elementów. Na przykład stwierdzenie, ile liczb w określonej kolekcji jest równych zeru, i zapisanie prostego wyrażenia, które tworzy pożądany wynik, jest bliższe naturalnem u sposobowi myślenia od myślenia w kategoriach pętli w jakiejkolwiek postaci. Drugi aspekt to fakt, że możliwości stosowania współbieżności są bardziej ewidentne w programach działających bezpośrednio na kolekcjach. Dzięki temu można wydajniej wykorzystać nowoczesny sprzęt. Prowadzone są dyskusje dotyczące dodania mechanizmów wyższego rzędu do nowoczesnych języków programowania, takich ja k C+ + lub Java, przy czym trzeba poświęcić bardzo dużo czasu na wielokrotne zapisywanie tej samej pętli for. Na przykład mamy kolekcję elementów i chcemy wykonać jakieś działanie na każdej z nich. W języku APL ten problem rozwiązano 4 0 - 45 lat temu!

Adin: Cóż, nie wiem, ile to lat temu było, ale w rozwiązaniu tego problemu można wyróżnić dwa etapy. Pierwszy to użycie tablic jako prymitywów, natom iast drugi to wprowadzenie operatora each, którego działanie sprowadza się do zastosowania dowolnej funkcji do dowolnej kolekcji elementów. Zawsze jednak istniały pytania w rodzaju: „Czy chcemy, by istniały specyficzne prym ityw y do obsługi pętli?” . Zdecydowaliśmy, że nie chcemy, by tak było, ponieważ za bardzo komplikowało to składnię. Łatwiej było napisać kilka potrzebnych pętli w standardowy sposób. Składnia okazała się skomplikowana do zaimplementowania czy dla użytkowników?

Adin: I jedno, i drugie: ludzie muszą czytać kod, maszyny muszą czytać kod; składnia albo jest prosta, albo nie. Wprowadzenie nowych instrukcji z całą pewnością jest komplikacją. Pytanie brzmi teraz: „Czy to się opłaca?” i pod tym kątem trzeba podjąć decyzję projektową. Zawsze dochodziliśmy do przekonania, że nie chcemy nowych konstrukcji składniowych do obsługi pętli, poniew aż możemy zrealizować ją w sposób konw encjonalny za pom ocą istniejących konstrukcji. Powiedział pan, że język APL zawiera cechy, które ułatw iają programowanie współbieżne. Rozumiem, że chodzi o wykorzystanie tablic jako prymitywnych struktur danych języka. Wspomniał pan również o współdzielonych zmiennych. W ja k i sposób one działają?

Adin: Zmienna współdzielona w języku APL to zmienna, do której w danym momencie ma dostęp więcej niż jeden procesor. Wszystkie procesory współdzielące mogą być procesorami APL albo niektóre z nich m ogą być innego typu. Na przykład mam y zm ienną, nazwijmy ją X. Z pu nktu widzenia języka APL czytanie i zapisywanie zmiennej Xnie różni się od czytania i zapisywania zwykłej zmiennej. Może jednak istnieć inny procesor, na przykład procesor plików, który także ma dostęp do zmiennej X.

Jest ona zmienną współdzieloną. Niezależnie od tego, jaką wartość język APL przekaże zmiennej X, procesor plików wykorzysta ją zgodnie z własną interpretacją. Na podobnej zasadzie, kiedy procesor plików przekaże w artość do zmiennej X, która następnie zostanie odczytana przez APL, to procesor APL, interpretując tę wartość, również zastosuje do niej swoją własną logikę. Xjest zmienną współdzieloną. W systemach APL, na przykład APL2 firmy IBM, istnieje pewien protokół zarządzający dostępem do tej zmiennej. W związku z tym użytkownik nie ma kłopotów z sytuacjami wyścigu. Czy współbieżność, o której pan m ówił, je st czymś, co kompilator może określić automatycznie? Przypuśćmy, że chcę pomnożyć dwie tablice i dodać wartość do każdego elementu tablicy. Z łatwością można to wyrazić w języku APL. Czy jednak kompilator będzie potrafił przeprowadzić jaw ne operacje współbieżne, wykonując te działania?

Adin: Definicja języka APL jest taka, że nie ma znaczenia, w jakiej kolejności wykonuje się operacje na elem entach tablicy. Z tego pow odu kom pilator lub interpreter czy dowolna inna implementacja może wykonywać je równolegle lub w dowolnej kolejności. Oprócz prostoty na poziomie języka może to dać programistom opracowującym implementację niezwykłą elastyczność zmiany sposobu działania implementacji lub wykorzystywania zalet nowego sprzętu. Może to być również mechanizm wykorzystywania takich własności, ja k automatyczna współbieżność.

Adin: Zgadza się, ponieważ zgodnie z definicją języka, która — co oczywiste — jest definicją tego, co się dzieje w czasie wykorzystywania procesora, kolejność wykonyw ania działań nie ma znaczenia. Była to doskonale przemyślana decyzja. Czy w tamtych czasach była to unikatowa decyzja w historii języków programowania?

Adin: Nie znam historii języków program ow ania, ale ponieważ nasz język był w zasadzie jedynym poważnym językiem zorientowanym na przetwarzanie tablic, decyzja prawdopodobnie była unikatowa. Dyskusja o kolekcjach i dużych zbiorach danych jest ciekawa, ponieważ z pewnością te zagadnienia znajdują się w kręgu zainteresowań nowoczesnych programistów. Język APL poprzedził powstanie relacyjnych baz danych. Obecnie mamy wielkie zbiory danych w strukturach składających się z różnych typów danych w relacyjnych bazach danych oraz w dużych kolekcjach bez struktury, takich ja k strony WWW. Czy można je obsługiwać także w języku APL? Czy język APL oferuje modele, z których mogą się uczyć osoby korzystające z bardziej popularnych języków, takich ja k SQL, PHP, Ruby czy Java?

Adin: Elementami tablic w APL mogą być zarówno wartości skalarne, bez wewnętrznej struktury, jak i wartości nieskalarne o dowolnej złożoności. Struktura elementów

78

ROZDZIAŁ

TRZECI

nieskalarnych jest rekurencyjna — składają się one z innych tablic. Z tego powodu w języku APL można wygodnie reprezentować kolekcje bez struktury, takie jak strony WWW, którymi można manipulować za pomocą prymitywnych funkcji APL. Jeśli chodzi o bardzo duże tablice, język APL posiada mechanizmy interpretowania zewnętrznych plików jako obiektów APL. Po powiązaniu nazwy w przestrzeni roboczej z plikiem zewnętrznym można stosować operacje na plikach z wykorzystaniem wyrażeń APL. Z pun ktu w idzenia użytkow nika wygląda to tak, jakby plik znajdow ał się w obrębie przestrzeni roboczej, mimo że jego rozmiar może wielokrotnie przewyższać rozmiar przestrzeni roboczej. Bardzo trudno dokładnie powiedzieć, czego mogą się nauczyć z języka APL programiści innych języków. A wchodzenie w szczegóły języków, które pan wymienił, byłoby z mojej strony aroganckie, ponieważ nie jestem ekspertem w żadnym z nich. Z tego jednak, co czytam o nich w literaturze, widzę, że w dużej mierze zasady, którymi kierowaliśmy się podczas projektow ania języka APL — opisane między innym i w artykule z 1973 roku „The Design of APL” — były brane pod uwagę później, podczas projektowania innych języków. Spośród najważniejszych zasad — prostoty i przydatności w zastosowaniu praktycznym — ta ostatnia wydaje się częściej stosowana. Prostota jest celem trudniejszym do osiągnięcia, ponieważ nie istnieją praktyczne ograniczenia na złożoność. W języku APL dążymy do prostoty poprzez uw ażne definiow anie zasięgu dozw olonych prym ityw nych operacji, utrzym yw anie abstrakcyjnej natury obiektów APL oraz pow strzym ywanie się od pokusy uwzględniania przypadków specjalnych reprezentow anych przez działania innych systemów. Ilustracją tego może być fakt, że w języku APL nie istnieje pojęcie pliku. Mamy tablice, które mogą być traktowane jako pliki, ale nie istnieją jako takie prymitywne funkcje specjalnie zaprojektowane w celu wykonywania operacji na plikach. Jednak praktyczna potrzeba wydajności w zarządzaniu plikami we wczesnej fazie projektowania sprzyjała rozwojowi paradygmatu zmiennych współdzielonych, które same w sobie są pojęciem ogólnym, stosowanym w wielu aplikacjach — tam, gdzie program y w języku APL wymagają wywoływania własności innego, pomocniczego procesora (przetwarzającego język APL bądź nie). Później opracowano ogólne pojęcie przestrzeni nazw. Pozwoliło to programom w APL na bezpośrednie manipulowanie obiektami poza przestrzenią roboczą. Można było zatem uzyskać dostęp do pól i metod Javy, bardzo dużych kolekcji danych, programów skom pilow anych w innych językach itp. Interfejs użytkow nika zarów no dla mechanizmów zmiennych współdzielonych, jak i przestrzeni nazw rygorystycznie zachowuje składnię i semantykę języka APL, co zapewnia zachow anie prostoty. Z tego względu, bez wchodzenia w szczegóły, można powiedzieć, że nowsze języki mogą skorzystać na utrzymaniu ścisłego przywiązania do swoich prymitywnych pojęć.

APL

79

W tym celu pojęcia te należy zdefiniować w sposób jak najbardziej ogólny, w kontekście aplikacji, w których mają być one wykorzystywane. Jeśli chodzi o szczegółową charakterystykę języka APL jako m odelu, to m ożna powiedzieć, że język APL udowodnił, że deklaracje nie są konieczne. Ich stosowanie jednak może w niektórych sytuacjach mieć w pływ na w ydajność w ykonyw ania programów. Pokazał również, że liczba różnych typów danych może być dość mała. Podążanie w tym kierunku, zamiast przyjmowania za pewnik, że użytkownik musi pomagać kom puterow i poprzez dostarczanie tego rodzaju informacji związanych z implementacją, może okazać się dla nowszych języków bardzo korzystne. Podobnie w języku APL nie istnieje pojęcie wskaźnika i nikt nigdy za nim nie tęsknił. Trzeba oczywiście pamiętać, aby prymitywne operacje w języku, tam, gdzie to możliwe, były definiowane na kolekcjach danych o abstrakcyjnej strukturze wewnętrznej, na przykład standardowych tablicach, drzewach i innych. Słusznie pan zauważył, że język APL poprzedził powstanie relacyjnych baz danych. Zarów no dr Edgar F. (Ted) Codd, jak i grupa tworząca język APL w latach sześćdziesiątych pracowali w T.J. W atson Research Center należącym do IBM. W tedy właśnie Codd tworzył pojęcie relacyjnej bazy danych. Uważam, że mieliśmy bardzo duży wpływ na tę pracę. W szczególności przypominam sobie gorącą dyskusję pomiędzy nami pewnego popołudnia, kiedy pokazywaliśmy, że do reprezentowania relacji pomiędzy jednostkam i danych m ożna używać prostych macierzy zamiast skom plikow anych systemów bazujących na skalarnych wskaźnikach.

Klasyka Wiem, że wiele rozwiązań projektowych w Perlu wywodzi się z języka APL. Niektórzy twierdzą, że nieczytelność Perlą w pewnym stopniu wywodzi się z języka APL. Nie wiem, czy to jest komplement, czy nie?

Adin: Proszę pozwolić mi, że dam przykład takiego rodzaju kom plem entu. W projektow aniu i używaniu języków programowania, w szczególności przez takie firmy jak IBM, kiedy w grę wchodzą cele biznesowe, jest bardzo dużo polityki. W pewnych okresach starano się inicjować konkurencyjne eksperymenty, aby zobaczyć, czy język APL sprawdzi się lepiej niż na przykład język PL1 lub FORTRAN. Wyniki były zawsze przesadzone, ponieważ ocen dokonywali ludzie z drugiej strony. Był jednak pewien komentarz, który zawsze będę pamiętał: ktoś powiedział, że język APL nie może być bardzo dobry, ponieważ dwóm najinteligentniejszym ludziom, których on zna, Iversonowi i Falkoffowi, nie uda się spowodować, aby ludzie w to uwierzyli.

80

ROZDZIAŁ

TRZECI

Jakie wnioski z lekcji na tem at powstania, dalszego rozwoju i przystosowania się pańskiego języka do warunków współczesnych mogą wyciągnąć programiści, którzy tworzą systemy komputerowe dziś oraz będą je tworzyć w najbliższej przyszłości?

Adin: Decyzje dotyczące projektow ania systemów nie są czysto techniczne lub naukowe. Olbrzymi wpływ mają względy ekonomiczne i polityczne, zwłaszcza kiedy istnieją potencjalne zależności od szczegółów technicznych, tak jak w przypadku projektowania języków i systemów. Kiedy w połowie lat sześćdziesiątych język APL zaczął odgrywać rolę ważnego narzędzia wykorzystywanego w firmie IBM i zaczęto zastanawiać się nad uczynieniem z niego produktu, musieliśmy rywalizować z IBM-owskim monarchą językowym. Oświadczył on, że w przyszłości firma będzie wspierała tylko język PL/1 — poza oczywiście FORTRAN-em i COBOL-em, które już miały ugruntowaną pozycję w branży i nie można było ich całkowicie odrzucić. Jak pokazała historia, było to stanow isko nie do przyjęcia przez firmę, skazane na porażkę. W tamtym czasie nie było to jednak takie oczywiste, jeśli wziąć pod uwagę dom inację firmy IBM w branży kom puterowej oraz dominację pewnych frakcji w strukturach kierownictwa firmy. Musieliśmy walczyć z taką polityką, aby zyskać odpowiednie wsparcie dla języka APL i przetrwać. Bitwy były toczone na kilku frontach: jako członkowie IBM Research Division staraliśmy się wykorzystać jak najwięcej okazji do wygłaszania profesjonalnych odczytów, prowadzenia seminariów i form alnych pokazów po to, by ugruntować unikatow e cechy języka APL w technicznej świadomości tam tych czasów. Zwerbowaliśmy wpływowych ludzi (wszędzie, gdzie było to możliwe), aby zrównoważyć wpływy przeciwników języka w strukturach władz firmy. Rozpowszechnialiśmy i wspieraliśmy wewnętrzne zastosowania systemu APL\360 w ośrodkach zajmujących się wytwarzaniem oprogram ow ania i produkcją. Inspirowaliśmy zainteresowanie ważnych klientów językiem APL, aby wymusić dostępność języka APL poza firmą, przynajmniej w zakresie eksperymentalnym. Szukaliśmy sprzymierzeńców w dziale m arketingu. W końcu odnieśliśmy sukces — język APL/360 znalazł się wśród pierwszych IBM-owskich produktów program ow ych dostępnych na rynku. Stało się to po odłączeniu sprzętu od oprogramowania w końcu lat sześćdziesiątych. Ze względu na zainteresowanie, jakie wzbudziły techniczne dysputy i pokazy w NASA Goddard Space Center, nastąpił znaczący przełom. W 1966 roku instytucja ta poprosiła o umożliwienie dostępu do naszego wewnętrznego systemu APL w celu wykonania eksperymentów z jego wykorzystaniem. To był bardzo ważny klient, a ludzie z działu marketingu nawoływali nas do spełnienia ich prośby. Odmówiliśmy jednak, upierając się, że moglibyśmy się zgodzić, gdyby pozw olono nam na przeprow adzenie tygodniow ego kursu instruktażowego w Goddard Space Center.

APL

81

Uzyskaliśmy taką zgodę, ale powstały trudności z realizacją planu: w tamtym okresie systemy z podziałem czasu, takie jak APL\360, wymagały terminali podłączonych do centralnego systemu za pom ocą m odem ów akustycznych pracujących ze specjalizowanymi telefonicznymi przystawkami do przetwarzania danych. Takich urządzeń telefonicznych używano również po drugiej stronie. Były podłączone do centralnego kom putera, ale było ich mało. Po uzyskaniu wszystkich pozwoleń na rozpoczęcie projektu dowiedzieliśmy się, że żadna z firm telekomunikacyjnych w stanie Nowy Jork ani Waszyngton nie była w stanie dostarczyć sprzętu potrzebnego do realizacji projektu w Space Center. Chociaż praktyką firmy telekomunikacyjnej z Waszyngtonu była praca tylko ze swoim w łasnym sprzętem, uzyskaliśmy zgodę na zainstalow anie dowolnej przystawki przetwarzania danych, jaką uda nam się zdobyć. Jednak pom im o usilnych starań menedżerów komunikacyjnych z IBM firma telekom unikacyjna stanu Nowy Jork nie była w stanie zdobyć potrzebnego sprzętu. Zasugerowano jednak, że przymkną oko, jeśli użyjemy ich sprzętu, który już był w naszym posiadaniu, w sposób, który oficjalnie był zakazany. W związku z tym odłączyliśmy połowę łączy od naszego centralnego kom putera, a uzyskane w ten sposób przystawki przetwarzania danych wysłaliśmy naszą ciężarówką do Space Center. Tam zostały one po cichu zainstalow ane przez lokalną firmę telekom unikacyjną. Dzięki tem u mogliśmy przystąpić do naszego kursu. Było to pierwsze użycie systemu APL/360 przez instytucję zewnętrzną. Dzięki temu język APL wyszedł poza firmę pom im o stosow ania polityki wsparcia wyłącznie dla języka PL/1. Czego pan najbardziej żałuje, gdy myśli o języku APL?

Adin: Podczas pracy nad projektem języka APL staraliśmy się ze wszystkich sił (i ciężko pracowaliśmy nad tym na arenie politycznej), by język ten został zaakceptowany i powszechnie używany. W tym kontekście nie znajduję niczego, czego m ógłbym żałować. Jedyną rzeczą, której ewentualnie mógłbym żałować z perspektywy czasu, jest to, że nie zaczęliśmy wcześniej i nie podjęliśmy większych wysiłków w kierunku opracow ania wydajnego kom pilatora. Nie mogliśmy jednak wiedzieć, jakie będą koszty tego przedsięwzięcia, ze względu na ograniczone zasoby. Co więcej, istnieją powody, aby sądzić, że obecne zainteresowanie programowaniem współbieżnym oraz zaadaptow anie operacji na tablicach w stylu APL do tradycyjnych języków kompilowanych, takich jak FORTRAN, w odpowiednim czasie przyniosą efekty. W ja k i sposób definiuje pan sukces w swojej pracy?

Adin: Są dowody na to, że język APL jest bardzo przydatnym narzędziem używanym w wielu przedsięwzięciach realizowanych przez firmę IBM. Pozwolił on na zastosowanie znacznie bardziej uproszczonego podejścia do używania komputerów. Dzięki temu badacze i projektanci produktów mogli wydajniej zabrać się do problem ów ,

82

ROZDZIAŁ

TRZECI

nad którym i pracowali — począwszy od fizyki teoretycznej, a skończywszy na tworzeniu monitorów z płaskim ekranem. Język był również używany do tworzenia prototypów ważnych systemów biznesowych obsługujących linie produkcyjne i hurtow nie. Dzięki niem u m ożna było szybko rozpocząć korzystanie z systemu i przetestować rozw iązania przed zagłębieniem się w implementacje z w ykorzystaniem innych systemów program ow ania. Udało się nam wprowadzić język APL do całej linii produktów IBM oraz dostarczyć wskazówek dla innych firm komputerowych, aby mogły opracować własne systemy APL zgodne z międzynarodowym standardem. Język APL znalazł również duże zastosowanie w instytucjach akademickich, jako narzędzie i wzorzec. W ten sposób w ypełnił jeden z głów nych celów tworzenia języka — zastosowanie w edukacji. Język APL był prekursorem języków programowania i systemów traktujących tablice jako prymitywne obiekty danych i wykorzystujące zmienne współdzielone do obsługi współbieżności. Z tego względu bez w ątpienia będzie m iał silny w pływ na inne dziedziny związane z programowaniem współbieżnym. Mam wielką satysfakcję z tego, że w ciągu ostatnich kilku miesięcy trzy oddzielne konsorcja z branży komputerowej rozpoczęły prace w tej dziedzinie.

APL

83

RO ZDZIAŁ

TRZECI

ROZDZIAŁ

CZWARTY

Forth

Forth jest językiem programowania wykorzystującym stos i bazującym na sklejaniu (ang. concatenative). Został opracowany przez Chucka Moore'a w latach sześćdziesiątych. Główne cechy języka Forth to wykorzystanie stosu do przechowywania danych i słów kluczowych operujących na stosie — do zdejm owania argum entów (operacja (operacja

p u s h ).

pop)

oraz umieszczanie wyników

Sam język jest na tyle mały, że działa na dowolnym sprzęcie

— od systemów wbudowanych po superkomputery. Jego możliwości budowania wyrażeń są wystarczające do tego, aby budować przydatne programy złożone z kilkuset słów. Następcy języka Forth to colorForth (jego twórcą również jest Chuck Moore) oraz język programowania Factor.

85

Język Forth a projektowanie języków Jak zdefiniowałby pan język Forth?

Chuck Moore: Forth jest językiem programowania o minimalnej składni. Zawiera jawny stos parametrów, który pozwala na wydajne wywoływanie procedur. Prowadzi to do tworzenia wyrażeń przyrostkowych (postfiksowych — co oznacza, że operatory występują za ich argumentami) oraz zachęca do stosowania wysoko sfaktoryzowanego stylu programowania z wieloma krótkimi procedurami współdzielącymi parametry na stosie. Czytałem, że nazwa Forth wywodzi się od oprogramowania czwartej generacji. Czy mógłby pan powiedzieć coś więcej na ten temat?

Chuck: Nazwa Forth wywodzi się od słowa „fourth” (z ang. czwarty), co oznacza język kom puterowy czwartej generacji. O ile sobie przypominam, przeskoczyłem jedną generację. FORTRAN i COBOL były językami pierwszej generacji, Algol i Lisp — drugiej. Wszystkie te języki podkreślały znaczenie składni. Im bardziej rozbudowana składnia, tym więcej możliwości sprawdzania błędów. Pomimo to większość błędów dotyczy składni. Ja postanowiłem zminimalizować składnię na rzecz semantyki. Słowa kluczowe języka Forth rzeczywiście mają bardzo bogate znaczenie. Uważa pan język Forth za zestaw narzędzi językowych. Mogę zrozumieć ten pogląd, jeśli wezmę pod uwagę jego stosunkowo prostą składnię w porównaniu z innymi językami oraz zdolność do budowania słowników na podstawie słów podstawowych. Czy coś pominąłem?

Chuck: Nie, to fakt, że język jest bardzo sfaktoryzowany. Program w języku Forth składa się z wielu niewielkich słów, podczas gdy program w języku C składa się z mniejszej liczby większych słów. Przez niewielkie słowo rozumiem takie słowo, którego definicja zwykle nie przekracza jednego wiersza. Język m ożna budować poprzez definiowanie nowego słowa w kontekście słów poprzednich. Hierarchię tę można budować tak długo, aż uzyska się na przykład tysiąc słów. W ystępują dwa zasadnicze problemy: 1) stwierdzenie, które słowa są przydatne, oraz 2) zapamiętanie wszystkich słów. Aplikacja, nad którą obecnie pracuję, zawiera tysiąc słów. Posiadam narzędzia do wyszukiwania słów, ale słowa m ożna wyszukiwać tylko wtedy, kiedy w iadom o, że słowo istnieje oraz jaką m a pisownię. Prowadzi to do innego stylu programowania, a przyzwyczajenie się do wykonywania pewnych działań w ten sposób zajmuje programiście sporo czasu. Widziałem wiele programów w języku Forth, które przypominały programy w C, ale zostały przepisane na język Forth — takie wykorzystanie języka nie było moją intencją. Moim zamiarem było umożliwienie startu od początku. Inną interesującą cechą zestawu narzędzi Forth

86

ROZDZIAŁ

CZWARTY

jest to, że słowa zdefiniowane przez użytkownika są dokładnie tak samo wydajne i m ają taką samą ważność jak słowa predefiniow ane w jądrze. Nic nie stoi na przeszkodzie, aby definiować własne słowa. Czy struktura widoczna na zewnątrz — zbiór wielu małych słów — wywodzi się z implementacji języka Forth?

Chuck: To wynik naszych bardzo w ydajnych sekwencji w yw ołań procedur. Nie ma przekazywania param etrów , ponieważ język bazuje na wykorzystaniu stosu. Działanie polega na w yw ołaniu procedury i zwrocie sterowania. Stos jest w yeksponowany. Kod maszynowy jest kompilowany. Przełączenie do procedury i z procedury to dosłownie jedna instrukcja wywołania i jedna instrukcja zwrotu sterowania. D odatkow o zawsze m ożna sięgnąć w dół do odpow iednika języka asemblera. Można zdefiniować słowo, które wykona właściwe instrukcje maszynowe zam iast w yw ołań procedur. W związku z tym m ożna uzyskać w ydajność rów ną wydajności innych języków, a być może jeszcze lepszą. Nie ma kosztów związanych z wywoływaniem procedur w języku C?

Chuck: Zgadza się. To daje programiście niezwykłą swobodę. Mądra faktoryzacja problem u nie tylko pozwala na rozwiązanie go w sposób wydajny, ale także na przedstawienie w bardzo czytelny sposób. Z drugiej strony, jeśli ktoś zrobi to źle, powstanie kod, którego nikt nie zdoła przeczytać — kod niemożliwy do zrozum ienia przez menedżera (jeśli menedżer w ogóle jest w stanie coś zrozumieć). W ten sposób można stworzyć prawdziwy chaos. A zatem jest to miecz obosieczny. Można zrobić coś bardzo dobrze lub bardzo źle. Co by pan powiedział (lub ja k i kod by pan pokazał) programiście używającemu innego języka programowania, aby zainteresował się językiem Forth?

Chuck: Bardzo trudno jest zainteresować doświadczonego programistę językiem Forth. A to dlatego, że zainwestował on w naukę narzędzi wspomagających programowanie w jego języku, systemie operacyjnym i zbudował sobie bibliotekę odpowiednią dla swoich zastosowań. Poinform ow anie go, że język Forth byłby mniejszy, szybszy i łatwiejszy do nauki nie jest wystarczającym argumentem do tego, by chciał kodować wszystko od początku. Początkujący programista lub inżynier, który chce napisać kod, nie musi pokonywać tej przeszkody i jest o wiele bardziej chętny do nauki nowych narzędzi. Podobnie może być z doświadczonym programistą, który rozpoczyna nowy projekt z nowymi ograniczeniami — na przykład, jak w moim przypadku, układami wielordzeniowymi.

FORTH

87

Wspomniał pan, że spotkał wiele programów w języku Forth, które przypominały programy w języku C. \N ja k i sposób można zaprojektować lepszy program w języku Forth?

Chuck: Metodą dół-góra. Po pierwsze, mamy pewne sygnały wejścia-wyjścia, które trzeba wygenerować, zatem je generujemy. Następnie piszemy kod, który zarządza generowaniem tych sygnałów. Potem przechodzimy w górę, aż do słowa znajdującego się na najwyższym poziomie. Nadajemy m u nazwę start. Wpisujemy start, a reszta dzieje się sama. Nie bardzo wierzę w analityków systemowych, którzy pracują w trybie góra-dół. Ich praca polega na zidentyfikowaniu problemu i podziale go na czynniki w taki sposób, że jest on bardzo trudny do zaimplementowania. Zgodnie z zasadami projektowania dziedzinowego logikę biznesową należy opisywać językiem klienta. Czy istnieje powiązanie pomiędzy tworzeniem słów oraz używaniem pojęć z danej dziedziny?

Chuck: Program ista pow inien znać dziedzinę, zanim przystąpi do pisania. Proponow ałbym rozm awianie z klientem. Słuchałbym słów, których on używa, i starałbym się używać tych słów w taki sposób, aby klient rozumiał, co program robi. Za pom ocą języka Forth m ożna uzyskać tego rodzaju czytelność, ponieważ stosuje on notację postfiksową. Gdybym tworzył aplikację finansową, prawdopodobnie używałbym słowa „procent” . M ożna by powiedzieć na przykład „2,03 procent” . Argumentem słowa „procent” jest 2,03. Wszystko zatem działa i brzmi w sposób bardzo naturalny. W ja k i sposób projekt, który rozpoczął się w erze kart perforowanych, może być w dalszym ciągu użyteczny w czasach współczesnych komputerów i internetu? Język Forth został zaprojektowany dla komputera IBM 1130 w 1968 roku. To, że w 2 0 0 7 roku został wybrany jak o język do przetwarzania równoległego, brzmi zabawnie.

Chuck: Między 1968 a 2007 rokiem język podlegał przeobrażeniom. Forth jest jednak najprostszym możliwym językiem kom puterow ym . Nie nakłada na program istę żadnych ograniczeń. Pozwala m u definiować słowa, które zwięźle opisują aspekty problemu w hierarchiczny sposób. Czy pana celem jes t tworzenie programów czytelnych na takim samym poziomie ja k opis w języku naturalnym?

Chuck: Na najwyższym poziomie tak, ale język naturalny nie jest dobry do opisu funkcji programu. Nie do tego został stworzony. Język naturalny posiada jednak taką samą cechę, jaką ma język Forth — pozwala definiować nowe słowa.

88

ROZDZIAŁ

CZWARTY

W większości przypadków nowe słowa definiuje się poprzez objaśnianie ich znaczenia za pom ocą słów zdefiniow anych wcześniej. W języku naturalnym może to być kłopotliwe. W słownikach często się zdarza, że definicje są cykliczne i nie można na ich podstawie wywnioskować żadnej treści. Czy możliwość koncentrowania się na słowach zamiast na nawiasach klamrowych i kwadratowych występujących w języku C sprawia, że stosowanie dobrego smaku do programów w Forth jest łatwiejsze?

Chuck: Mam taką nadzieję. Programista języka Forth przejmuje się również wyglądem kodu, a nie tylko funkcją, którą on realizuje. Osiągnięcie sekwencji słów, które do siebie pasują, to przyjemne uczucie. Dlatego właśnie opracow ałem język colorForth. Denerwowały mnie konstrukcje syntaktyczne, które ciągle występowały w języku Forth. Na przykład komentarze można wydzielić za pomocą lewego i prawego nawiasu. Spojrzałem na wszystkie te znaki interpunkcyjne i przyszło mi na myśl, że może istnieć lepszy sposób. Lepszy sposób jest dość kosztowny w tym sensie, że z każdym słowem w kodzie źródłowym można powiązać znacznik. Kiedy udało mi się przełknąć te koszty, było mi bardzo przyjemnie, że wszystkie te zabawne małe znaczki zniknęły i zostały zastąpione kolorowymi słowami. Dla mnie był to znacznie bardziej elegancki sposób prezentowania opisu funkcji. Otrzymywałem wiele krytycznych uwag od osób mających trudności z rozróżnianiem kolorów. Byli bardzo zdenerwowani tym, że próbowałem pozbawić ich możliwości wykonywania zawodu programistów. Ktoś jednak wpadł na pomysł, aby rozróżniać nie na podstawie kolorów, a na podstawie zestawów znaków, co również okazało się dobrym sposobem. Kluczem jest czterobitowy znacznik w każdym słowie, co umożliwia rozróżnianie do 16 działań. Kompilator może natychmiast ocenić zamiary programisty, zamiast oceniać je z kontekstu. Języki drugiej i trzeciej generacji charakteryzowały się minimalizmem, na przykład dla metacyklicznych implementacji tzw. wyciągania (ang. bootstrappingj. Forth jest doskonałym przykładem minimalizmu w zakresie pojęć językowych oraz wymaganego wsparcia sprzętowego. Czy było to typowe dla tamtych czasów, czy powstało wraz z upływem czasu?

Chuck: Nie, był to świadomie w ybrany cel projektow y — dążenie do uzyskania jak najmniejszego jądra. Chciałem, by było jak najmniej predefiniow anych słów kluczowych oraz by program ista m iał możliwość dodaw ania słów, jeśli pojawi się taka potrzeba. Podstawowym powodem takiego projektu była przenośność. W tam tych czasach istniały dziesiątki typów minikomputerów, a później pojawiły się dziesiątki typów m ikrokom puterów . M oim celem było umożliwienie działania języka Forth na wszystkich tych maszynach.

FORTH

89

Chciałem, aby przenośność była maksymalnie ułatwiona. Założyłem, że istnieje jądro złożone ze 100 słów, które wystarczą do wygenerowania — ja nazywam to systemem operacyjnym, ale to nie do końca to — kolejnych kilkaset słów. Następnie można przystąpić do wykonywania aplikacji. M oim zadaniem było dostarczenie pierwszych dwóch etapów i umożliwienie program istom aplikacyjnym w ykonania trzeciego. Zazwyczaj byłem również programistą aplikacyjnym. Definiowałem słowa, które w mojej opinii były niezbędne. Pierwszych kilkaset słów powinno być zaimplementowanych w języku maszynowym — na przykład asemblerze lub innym języku dającym bezpośredni dostęp do określonej platformy. Kolejnych dwieście lub trzysta słów było napisanych na wyższym poziomie, w celu zm inim alizow ania zależności maszynowych od niższego, wcześniej zdefiniowanego poziomu. W efekcie aplikacja jest niem al całkowicie niezależna od sprzętu. Dzięki takiej architekturze przenoszenie program ów z jednego m inikom putera do innego nie było trudne. Czy udało się przenosić programy poza ten drugi etap?

Chuck: Oczywiście. Miałem na przykład edytor tekstowy, którego używałem do edycji kodu źródłowego. Zazwyczaj udawało mi się go przenosić bez zmian. Słyszałem plotki, że za każdym razem, kiedy wpadła panu do rąk nowa maszyna, natychmiast przystępował pan do przenoszenia na nią języka Forth.

Chuck: Tak. Był to dla mnie najłatwiejszy sposób na zrozumienie sposobu działania maszyny oraz jej specjalnych cech. Mogłem się też dowiedzieć, czy zaimplementowanie standardowego pakietu słów języka Forth jest łatwe, czy nie. W ja k i sposób odkrył pan kod zszywany pośrednio (ang. indirect threadedj?

Chuck: Kod zszywany pośrednio to subtelne pojęcie. Każdemu słowu języka Forth odpow iada pozycja w słowniku. W kodzie zszywanym bezpośrednio (ang. direct threaded) każda pozycja wskazuje na kod, który m a się w ykonać w przypadku napotkania słowa. Kod zszywany pośrednio wskazuje na lokalizacje zawierające adresy kodu, który ma się wykonać. W ten sposób można uzyskać dostęp do innych informacji niż tylko adresy — na przykład do wartości zmiennych. Taki sposób kodow ania zapew niał najbardziej kom paktow ą reprezentację słów. Okazało się, że może ona być odpow iednikiem zarów no kodu zszywanego bezpośrednio, jak i kodu zszywanego proceduram i (ang. subroutine-threaded). Oczywiście te pojęcia i ta term inologia nie były znane w roku 1970. W ydawało mi się jednak, że jest to najbardziej naturalny sposób zaimplementowania różnych typów słów.

90

ROZDZIAŁ

CZWARTY

W ja k i sposób język Forth wpłynie na przyszłe systemy komputerowe?

Chuck: To już się stało. Od 25 lat pracuję na mikroprocesorach zoptymalizowanych do używania języka Forth. Ostatnio wykorzystuję wielordzeniowy układ, którego rdzenie są komputerami działającymi w Forth. Co udostępnia język Forth? Ponieważ jest prostym językiem, pozwala na realizację prostego komputera: 256 słów pamięci lokalnej, 2 stosy typu push-down, 32 instrukcje, operacje asynchroniczne, łatw a kom unikacja z sąsiadami. Małe wymiary i niski pobór mocy. Forth zachęca do tworzenia wysoce sfaktoryzowanych programów. Takie programy nadają się do przetw arzania równoległego, zgodnie z wym aganiam i układów wielordzeniowych. Wiele prostych programów zachęca do tego, aby projekt każdego z nich był przemyślany. Poza tym ich napisanie wymaga najwyżej 1% kodu, który trzeba by było napisać w innym przypadku. Zawsze, kiedy słyszę ludzi, którzy chwalą się milionami wierszy kodu, wiem, że źle zrozumieli swój problem. Współcześnie nie ma takich problemów, które wymagałyby wielu milionów wierszy kodu. Są za to nieostrożni programiści, źli menedżerowie lub wymagania zgodności niemożliwe do spełnienia. W ykorzystanie języka Forth do zaprogram owania wielu niewielkich kom puterów to doskonała strategia. Inne języki po prostu nie mają takiej m odularności ani elastyczności. Ponieważ kom putery stają się coraz mniejsze, a sieci złożone z nich współpracują ze sobą, to język Forth będzie środowiskiem przyszłości. Brzmi to podobnie do jednej z głównych idei Uniksa: wiele programów komunikujących się ze sobą, z których każdy wykonuje jedną rzecz. Czy także dziś jest to najlepsza architektura? Zamiast wielu programów na jednym komputerze wiele programów działających przez sieć?

Chuck: Pojęcie kodu wielowątkowego zaimplementowane w systemie Unix oraz innych systemach operacyjnych zainicjowało zjawisko przetwarzania równoległego. Istnieją jednak istotne różnice. Duże komputery umożliwiają ponoszenie znaczących kosztów potrzebnych do realizacji wielowątkowości. W końcu olbrzymie systemy operacyjne już istnieją. Jednak dla potrzeb przetwarzania równoległego niemal zawsze im więcej komputerów, tym lepiej. Przy stałych zasobach więcej kom puterów oznacza mniejsze kom putery. A małe komputery nie są w stanie sprostać kosztom obliczeniowym typowym dla komputerów dużych. Małe komputery łączy się w sieci wewnątrz układu, pomiędzy układami oraz za pomocą łączy bezprzewodowych. Małe komputery mają małą pamięć. Nie ma na nich miejsca na system operacyjny. Kom putery muszą być autonom iczne i mieć w budow aną

FORTH

91

zdolność do komunikowania się. A zatem komunikacja musi być prosta — rozbudowane protokoły komunikacyjne się nie sprawdzają. Oprogramowanie musi być kompaktowe i wydajne. To idealne zastosowania dla języka Forth. Systemy, które wymagają milionów wierszy kodu, staną się nieodpowiednie. Są one konsekwencją dużych, scentralizowanych komputerów. Przetwarzanie rozproszone wymaga innego podejścia. Język zaprojektowany do obsługi obszernego kodu o skomplikowanej składni zachęca programistów do pisania dużych programów. Programiści czerpią z tego satysfakcję i są za to nagradzani. Nie ma motywacji, aby dążyć do zwięzłości. Chociaż kod wygenerowany przez język składniowy może być niewielki, zazwyczaj taki nie jest. Im plem entacja uogólnień w ym aganych przez składnię prowadzi do nieporęcznego i niewydajnego kodu obiektowego. Taki kod nie nadaje się do zastosowania w małych komputerach. W dobrze zaprojektowanym języku istnieje korelacja jeden do jednego pom iędzy kodem źródłow ym a kodem obiektowym. Dla programisty jest oczywiste to, jaki kod zostanie wygenerowany z jego źródeł. Dzięki tem u uzyskuje on w łasną satysfakcję, staje się wydajny, a potrzeba dokum entacji maleje do minimum. Język Forth zaprojektowano między innymi z myślą o zwięzłości zarówno na poziomie kodu źródłowego, ja k i binarnego wyniku. Z tego powodu jest on popularny wśród programistów systemów wbudowanych. Jednak programiści z wielu innych dziedzin mają powody, dla których wybierają inne języki. Czy są takie aspekty projektu języka, które tylko zwiększają objętość kodu źródłowego lub wynikowego?

Chuck: Forth rzeczywiście jest kom paktow y. Jedną z przyczyn, dla których tak się dzieje, jest jego prosta składnia. Inne języki sprawiają wrażenie, jakby celowo dodaw ano do nich konstrukcje składniowe. Powoduje to redundancję i stwarza powody do sprawdzania składni, a tym samym wykrywania błędów. Język Forth stwarza niewiele okazji do wykrywania błędów z pow odu braku redundancji. Przyczynia się to do bardziej kompaktowego kodu źródłowego. Z moich doświadczeń z innymi językami wynika, że większość błędów dotyczy składni. Projektanci niejako tworzą okazje do popełniania przez programistów błędów, które mogą być następnie wykryte przez kompilator. Nie wydaje mi się, aby było to wydajne. W ten sposób pisanie poprawnego kodu staje się trudniejsze. Przykładem może być sprawdzanie typów. Przypisywanie typów do różnych liczb umożliwia wykrywanie błędów. Niezamierzoną konsekwencją takiego działania jest dokładanie pracy programistom. Muszą oni bowiem konwertować typy lub unikać kontroli typów w czasie realizowania zaplanowanych działań.

92

ROZDZIAŁ

CZWARTY

Inną konsekwencją składni jest konieczność przystosowania jej do wszystkich aplikacji. Przez to staje się ona bardziej obszerna. Forth jest językiem rozszerzalnym. Programista może tworzyć struktury równie wydajne jak te, których dostarcza kompilator. Z tego względu nie trzeba przewidywać i dostarczać wszystkich możliwości. Charakterystyczną cechą języka Forth są operatory przyrostkowe. To upraszcza kompilator i pozwala na translację kodu źródłowego na obiektowy w trybie jeden do jednego. Programista lepiej rozumie swój kod, a wynikowy skompilowany kod jest bardziej zwięzły. Zwolennicy wielu nowoczesnych języków programowania (zwłaszcza Pythona i Ruby) podają czytelność jako jedną z ważniejszych zalet. Czy w porównaniu z nimi Forth je st ła tw y do nauki i utrzymania? Czego może nauczyć język Forth inne języki, jeśli chodzi o czytelność?

Chuck: Zwolennicy każdego języka programowania podkreślają jego czytelność. Języki programowania nie są jednak czytelne. Być może wydają się czytelne dla osoby znającej język, ale nowicjusz zawsze jest skonsternowany. Problemem jest tajemnicza, przypadkowa i zagmatwana składnia. Te wszystkie nawiasy, operatory itp. Trzeba się nauczyć, do czego one służą, a na koniec i tak dochodzi się do wniosku, że nie istnieje dobre uzasadnienie ich obecności. Pomimo wszystko trzeba postępować zgodnie z regułami. A poza tym nie można mówić tym językiem. Trzeba by czytać znaki przestankowe tak jak Victor Borgia. Forth łagodzi ten problem dzięki ograniczeniu składni do m inim um . Tajemnicze symbole @i ! występujące w języku wymawia się „pobierz” (ang. fetch) i „zapisz” (ang. store). Są symbolami tylko ze względu na ich częste występowanie. Program ista może posługiwać się naturalnym i słowami. Są one porozdzielane za pom ocą znaków interpunkcyjnych. Przy odpow iednim doborze słów m ożna konstruować sensowne zdania. W języku Forth pisano nawet poematy. Inną zaletą jest notacja przyrostkowa (postfiksowa). We frazie typu „6 cali” można zastosować operator „cali” do parametru 6 w naturalny sposób. To bardzo czytelne. Z kolei zadanie programisty sprowadza się do opracowania słownictwa opisującego problem . Słownictwo to może się rozrastać do sporych rozmiarów. Aby program był zrozumiały dla użytkownika, musi on znać to słownictwo. Programista musi też zdefiniować przydatne słowa. Tak czy owak, czytanie programu wymaga wysiłku. Niezależnie od języka. W ja k i sposób definiuje pan sukces w swojej pracy?

Chuck: Sukces to eleganckie rozwiązanie.

FORTH

93

W języku Forth nie pisze się programów. Forth jest programem. Dodaje się słowa w celu stworzenia słownictwa opisującego problem. Jeśli zdefiniuje się właściwe słowa, rozwiązanie problem u jest oczywiste. Przy użyciu słów m ożna interaktyw nie rozwiązywać dowolne aspekty problemu. Na przykład można zdefiniować słowa opisujące obwód. Chcę dodać obwód do układu, wyświetlić jego układ, zweryfikować reguły projektu, uruchomić symulację. Słowa wykonujące te działania tworzą aplikację. Jeśli uda się je prawidłowo dobrać i stworzą kompaktowy, wydajny zestaw narzędzi, to będzie znaczyło, że odniosłem sukces. Gdzie nauczył się pan pisania kompilatorów? Czy w tamtych czasach było to zajęcie, które musieli wykonywać wszyscy?

Chuck: W latach sześćdziesiątych byłem studentem Uniwersytetu Stanford. Razem ze m ną studiowała grupa ludzi, którzy zajmowali się pisaniem kompilatora Algola — wersji na kom puter Burroughs 5500. To były najwyżej trzy, cztery osoby. Zrobiło na mnie wrażenie to, że trzech lub czterech ludzi potrafiło usiąść i napisać kompilator. Powiedziałem sobie: „Jeśli oni potrafią to robić, to dlaczego nie ja” . Usiadłem i to zrobiłem. Nie było trudne. W tamtym czasie pisanie kompilatorów było zajęciem mistycznym. \N dalszym ciągu nim jest.

Chuck: Tak, ale już w mniejszym stopniu. Od czasu do czasu pojawiają się nowe języki. Niezależnie od tego, czy są interpretowane, czy kompilowane, zawsze znajdą się programiści-fanatycy chętni, by je tworzyć. System operacyjny to kolejne ciekawe pojęcie. Systemy operacyjne są niezwykle złożone i całkowicie zbędne. Bill Gates dokonał cudu, sprzedawszy światu pojęcie systemu operacyjnego. To praw dopodobnie największe oszustwo, jakie świat kiedykolwiek widział. System operacyjny nie robi absolutnie nic dla użytkownika. Jest procedura zwana sterownikiem dysku, procedura obsługi komunikacji. W nowoczesnym świecie system operacyjny nie robi niczego więcej. System W indows m arnuje m nóstw o czasu na nakładki, zarządzanie dyskiem i wielu czynności, które są całkowicie nieważne. Dysponujemy dyskami o pojemności kilkuset gigabajtów, pamięcią RAM o pojemności wielu megabajtów. Świat zmienił się do tego stopnia, że system operacyjny stał się niepotrzebny. A co z obsługą urządzeń?

Chuck: Dla każdego urządzenia istnieje procedura obsługi. To biblioteka, a nie system operacyjny. Wystarczy wywołać lub załadować to, co jest potrzebne.

94

ROZDZIAŁ

CZWARTY

W ja k i sposób wznawia pan proces programowania po krótkiej przerwie?

Chuck: Dla m nie krótka przerwa w kodow aniu nie jest żadnym problem em . Jestem bardzo skoncentrow any na problem ie i śnię o nim przez całą noc. Myślę, że to charakteryzuje programowanie w Forth: pełne zaangażowanie przez krótki okres (kilka dni) w celu rozwiązania problemu. Pomocne jest to, że aplikacje języka Forth są w naturalny sposób podzielone na podprojekty. Większość kodu języka Forth jest prosta i łatwa do ponownego przeczytania. Kiedy robię naprawdę trudne rzeczy, piszę obszerne kom entarze. Dobre kom entarze pom agają na now o wejść w problem, ale zawsze konieczne jest czytanie i rozumienie kodu. Jaki największy błąd popełnił pan przy projektowaniu lub programowaniu? Czego owo doświadczenie pana nauczyło?

Chuck: Jakieś 20 lat temu chciałem stworzyć narzędzie do projektowania układów VLSI. Nie miałem języka Forth dla mojego nowego PC, zatem pomyślałem, że zastosuję inne podejście: język maszynowy. Nie język asemblera, ale wpisywanie szesnastkowych instrukcji. Stworzyłem kod tak, jak zrobiłbym to w języku Forth — składał się z wielu prostych słów, które kom unikow ały się ze sobą w sposób hierarchiczny. Program działał. Używałem go przez 10 lat. Był jednak tru dny do utrzym ania i dokum entow ania. Ostatecznie napisałem go od nowa w języku Forth, przez co stał się mniejszy i prostszy. Okazało się, że Forth był wydajniejszy od języka maszynowego. Częściowo wynikało to z jego interaktywności, a częściowo ze składni. Interesującą cechą kodu w Forth jest to, że liczby można dokumentować przez wyrażenia używane do ich obliczania.

Sprzęt W ja k i sposób ludzie powinni postrzegać sprzęt, na którym programują: jako zasób czy jako ograniczenie? Jeśli ktoś rozpoznaje go jako zasób, może zoptymalizować kod, by wykorzystać wszystkie własności sprzętu. Jeśli ktoś postrzega go jako ograniczenie, będzie starał się pisać kod z myślą o tym, że kod będzie działał lepiej na nowszej i bardziej rozbudowanej wersji sprzętu. Nie stanowi to problemu, ponieważ rozwój sprzętu następuje bardzo szybko.

Chuck: To bardzo dobre spostrzeżenie — oprogramowanie musi być ukierunkowane na sprzęt. Autorzy oprogramowania na komputery PC przewidują powstanie szybszych komputerów, dzięki czemu mogą sobie pozwolić na to, aby programy działały niezbyt wydajnie. N atom iast gdy pisze się program y dla systemów wbudow anych, należy zakładać, że system będzie stabilny przez cały czas życia projektu. Niezbyt wiele program ów

FORTH

95

migruje z jednego projektu do innego. A zatem w tym przypadku sprzęt stanowi ograniczenie. Za to w przypadku kom puterów PC sprzęt jest zasobem, który będzie się rozwijał. Sytuacja ta może się zmienić po przejściu na przetwarzanie równoległe. Aplikacje, które nie będą mogły wykorzystać wielu kom puterów , staną się ograniczone, ponieważ pojedyncze kom putery przestaną być coraz szybsze. Przepisywanie istniejącego oprogramowania w celu optymalizacji przetwarzania równoległego jest niepraktyczne. N atom iast nadzieja, że inteligentne kom pilatory rozwiążą problem , to pobożne życzenie. Co jest sednem problemu współbieżności?

Chuck: Sednem problemu współbieżności jest szybkość. Komputer musi wykonać w aplikacji wiele działań. M ożna je zrealizować na pojedynczym procesorze obsługującym wielozadaniowość. Innym rozwiązaniem jest równoległe wykonanie tych działań na wielu procesorach. Drugi sposób jest znacznie szybszy, a współczesne oprogram ow anie wymaga tej szybkości. Czy rozwiązanie leży w sprzęcie, oprogramowaniu, czy w kombinacji jednego z drugim?

Chuck: Połączenie procesorów ze sobą nie jest trudne. A zatem sprzęt istnieje. Jeśli napisze się oprogram ow anie, które wykorzysta tę cechę, problem będzie rozwiązany. Jeśli jednak możliwe jest przeprogramowanie oprogramowania, można je wykonać tak wydajnie, że systemy wieloprocesorowe przestaną być potrzebne. Problem polega na używ aniu systemów w ieloprocesorowych bez konieczności modyfikowania istniejącego oprogramowania. Na tym polega zastosowanie podejścia z inteligentnymi kompilatorami, które nigdy nie zostało osiągnięte. Jestem zdum iony, że nie przepisano (nie m ożna było przepisać) oprogram ow ania napisanego w latach siedemdziesiątych. Powodem tej sytuacji jest to, że wówczas oprogramowanie było ekscytujące. Wszystko było robione po raz pierwszy. Programiści pracowali po 18 godzin dziennie tylko dlatego, że mieli z tego przyjemność. Dziś program owanie jest zajęciem od 9.00 do 17.00. Częścią pracy zespołowej zgodnej z harmonogramem. Średnia przyjemność. Dodaje się zatem kolejną warstwę oprogramowania po to, by uniknąć przepisywania starych programów. To jest przynajmniej bardziej zabawne od przekodowywania głupiego edytora tekstu.

96

ROZDZIAŁ

CZWARTY

Mamy dostęp do olbrzymiej mocy obliczeniowej współczesnych komputerów. Ile jednak faktycznego przetwarzania (tzn. obliczeń) wykonują te systemy? A ile czasu poświęcają na przenoszenie i formatowanie danych?

Chuck: Ma pan rację. Większość aktywności komputerów pochłania przenoszenie danych, a nie obliczenia. Nie jest to wyłącznie przenoszenie danych, ale także kompresja, szyfrowanie, kodowanie. Przy szybkim przesyłaniu danych musi to być w ykonane na poziomie układów, zatem można się zastanawiać, do czego w ogóle są potrzebne komputery. Czy możemy się z tego czegoś nauczyć? Czy sprzęt należałoby budować w inny sposób? Don Knuth sformułował problem: sprawdźcie, co się dzieje w komputerze w ciągu jednej sekundy. Powiedział, że to, co byśmy odkryli, mogłoby zmienić wiele rzeczy.

Chuck: Moje układy kom puterow e uwzględniają ten problem , ponieważ są wyposażone w prosty i wolny mnożnik. Nie jest wykorzystywany zbyt często. Przesyłanie danych pom iędzy rdzeniami i dostęp do pamięci to ważne funkcje. Z jednej strony ma pan język, który pozwala tworzyć własne słowniki i nie wymusza myślenia o sprzęcie. Z drugiej strony system ma bardzo małe jądro, które jest mocno powiązane z tym sprzętem. Interesujące jest to, w ja k i sposób język Forth łączy te dwa elementy, które rozdziela przepaść. Czy to prawda, że na niektórych maszynach poza jądrem języka Forth nie ma systemu operacyjnego?

Chuck: Nie ma. Forth jest rzeczywiście autonomiczny. Wszystko, co jest potrzebne, istnieje w jądrze. Ale to oddziela sprzęt od osób piszących programy w języku Forth.

Chuck: Zgadza się. Maszyna Lisp robiła coś podobnego, ale nigdy nie zdobyła popularności. Językowi Forth udało się to bez trudu.

Chuck: Lisp nie obejmował systemu wejścia-wyjścia. W istocie język C nie zajmuje się systemem wejścia-wyjścia i dlatego wymaga systemu operacyjnego. Język Forth od samego początku obejmował system wejścia-wyjścia. Nie wierzę w najbardziej popularny wspólny mianownik. Myślę, że jeśli ktoś przechodzi na nową maszynę, to jedynym powodem jest to, że w jakiś sposób różni się ona od poprzedniej i chcemy wykorzystać te różnice. Aby tego dokonać, trzeba zejść do poziomu wejścia-wyjścia.

FORTH

97

Kernighan i Ritchie twierdzą, że w celu ułatwienia przenośności języka C chcieli wykorzystać podejście najmniejszego wspólnego czynnika. Dla pana przenośność jest łatwiejsza, jeśli nie zastosuje się tego podejścia.

Chuck: Stosowałem standardowe sposoby realizacji przenośności. Wykorzystywałem słowo — fetchp — które pobiera 8 bitów z portu. To słowo należy definiować inaczej na różnych komputerach, ale jest to ta sama funkcja realizowana na stosie. A zatem w pewnym sensie język Forth je st odzwierciedleniem języka C wraz ze standardową biblioteką wejścia-wyjścia.

Chuck: Tak, chociaż kiedyś pracowałem ze standardow ą biblioteką FORTRAN-a i to było okropne. Po prostu biblioteka zawierała niewłaściwe słowa. Stosowanie ich było niezwykle kosztowne. Zdefiniowanie kilkudziesięciu instrukcji realizujących operacje wejścia-wyjścia jest tak łatwe, że nie ma sensu ponosić kosztów predefiniow anego protokołu. Czy często stosował pan obejścia tych mechanizmów?

Chuck: W FORTRAN-ie tak. Kiedy posługujesz się na przykład W indowsem, nic nie możesz zrobić. Nie masz dostępu do systemu wejścia-wyjścia. Celowo trzym ałem się z dala od W indowsa. Jednak naw et bez W indow sa Pentium był najtrudniejszą maszyną do zaimplementowania Fortha. Zawiera zbyt obszerny zestaw instrukcji. Ma też zbyt wiele mechanizmów sprzętowych, takich jak bufory TLB, oraz różne rodzaje pamięci podręcznej, których nie sposób zignorować. Trzeba utorow ać sobie drogę. Kod inicjalizacyjny potrzebny do uruchom ienia języka Forth był najtrudniejszy do napisania i najbardziej rozbudow any. Mimo że m usiał być uruchom iony tylko raz, to większość czasu poświęciłem na opracow anie praw idłow ego sposobu w ykonania tej czynności. Udało się samodzielne uruchomienie języka Forth w systemie Pentium, zatem wysiłek się opłacił. Proces trwał około 10 lat. Po części ze względu na gonitwę za zmianami sprzętowymi w procesorach Intel. Wspomniał pan, że język Forth obsługuje operacje asynchroniczne. Co pan rozumie przez operację asynchroniczną?

Chuck: Cóż, jest kilka znaczeń. Język Forth zawsze posiadał możliwości przetwarzania w ieloprogram owego oraz wielowątkowego, które nazw ałbym m echanizm am i kooperacyjnymi. Było dostępne słowo pause. Jeśli jakieś zadanie dotarło do miejsca, w którym nie mogło wykonać żadnej czynności bezpośrednio, w ykonyw ało polecenie pause. Program szeregujący, realizujący algorytm cykliczny, przydzielał wtedy komputer do następnego zadania w pętli.

98

ROZDZIAŁ

CZWARTY

Gdyby nie użyto słowa pause, m ogłoby dojść do całkowitego zm onopolizowania kom putera. Tak jednak nigdy się nie stało, ponieważ kom puter był dedykowany. Działała na nim pojedyncza aplikacja, a wszystkie zadania były „zaprzyjaźnione” . Zgaduję, że tak było w daw nych czasach, kiedy wszystkie zadania były „zaprzyjaźnione” . To jest jeden z typów asynchroniczności zadań — wykonywanie własnych operacji bez konieczności synchronizacji. Jedną z własności języka Forth jest to, że słowo pause można zaszyć w słowach niższego poziomu. Każda próba odczytu lub zapisu na dysku powodowała uruchomienie słowa pause, ponieważ mechanizm dysku wiedział, że musi poczekać na wykonanie operacji. W nowych układach wielordzeniowych, nad którymi pracuję, przyjmujemy taką samą filozofię. Każdy komputer działa niezależnie. Jeśli jest zadanie na jednym komputerze i inne zadanie na kom puterze sąsiednim, to obydwa działają równolegle, ale kom unikują się ze sobą. Jest to odpow iednik tego, co te zadania robiłyby w komputerze z obsługą wielowątkowości. Język Forth bardzo dobrze nadaje się do w ykonyw ania niezależnych zadań. W przypadku kom putera wielordzeniowego nie do końca mogę użyć tych samych program ów . Mogę jednak w pewien sposób rozłożyć program y na czynniki, tak by działały równolegle. Czy w przypadku wielu wątków działających w trybie kooperacyjnym każdy z wątków ma swój własny stos i należy się przełączać pomiędzy nimi?

Chuck: W niektórych komputerach przełączenie zadania sprowadzało się do zapisania słowa na wierzchołku stosu, a następnie przełączenia wskaźnika stosu. W innych przypadkach trzeba było skopiować stos i załadować nowy, ale wtedy uznałbym to za powód do utrzymywania bardzo płytkiego stosu. Czy celowo ogranicza pan głębokość stosu?

Chuck: Tak. Początkowo stosowałem stosy o dowolnych rozmiarach. Głębokość stosu w pierwszym zaprojektowanym przeze mnie układzie wynosiła 256, ponieważ uważałem, że to mało. Projektowałem już układy, w których stos miał głębokość 4. Obecnie uważam, że dobra głębokość stosu mieści się pomiędzy 8 a 10. Zatem z czasem stałem się większym minimalistą. Spodziewałbym się czegoś odwrotnego.

Chuck: W pracy z aplikacją do projektowania układów VLSI zdarzyła mi się sytuacja, w której m usiałem rekurencyjnie sprawdzać ścieżki w układzie. W tym przypadku musiałem stworzyć stos o głębokości około 4000. Realizacja tej operacji może wymagać zastosowania innego rodzaju stosu — implementowanego programowo. Jednak nic nie stoi na przeszkodzie, aby w systemach Pentium był to stos sprzętowy.

FORTH

99

Projektowanie aplikacji Powiedział pan, że Forth je st idealnym językiem dla wielu małych komputerów działających w sieci — tzw. inteligentnego pyłu (ang. smart dust). Do jakiego typu aplikacji pana zdaniem najbardziej nadają się te małe komputery?

Chuck: Na pewno komunikacja, na pewno czujniki. Naprawdę jednak to dopiero zaczynam się uczyć, w jaki sposób m ożna osiągnąć większe cele za pom ocą niezależnych komputerów współpracujących ze sobą. Komputery wielordzeniowe, którymi dysponujemy, są brutalnie małe. Mają 64 słowa pamięci. Właściwie mają 128 słowa pamięci: 64 słowa pamięci RAM i 64 pamięci ROM. Każde słowo może pomieścić do czterech instrukcji. Pojedynczy kom puter może realizować 512 instrukcji, kropka. W związku z tym zadania muszą być proste. Proszę się zastanowić, w jaki sposób rozłożyć na czynniki takie zadanie jak stos TCP/IP i poprzydzielać je na kilka takich kom puterów , tak aby żaden z kom puterów nie potrzebował więcej niż 512 instrukcji? To doskonały problem projektowy. W tej chwili właśnie rozwiązuję problem podobnego rodzaju. Myślę, że rozczłonkowanie m ałych zadań na wiele kom puterów sprawdza się w odniesieniu do prawie wszystkich aplikacji. O wiele łatwiej jest zrealizować aplikację, jeśli zostanie rozłożona na niezależne części, niż próbować wykonywać je seryjnie na pojedynczym procesorze. Uważam, że podział na wiele kom puterów jest dobry w przypadku aplikacji generowania wideo. Z pewnością jest to dobry sposób dla zadań kompresji i dekompresji obrazów. Dopiero jednak się uczę sposobów realizacji takich aplikacji. W firmie są także inni ludzie, którzy też się tego uczą i sprawia im to przyjemność. Czy są jakieś dziedziny, w których wspomniany sposób jest nieodpowiedni?

Chuck: Tradycyjne, istniejące oprogramowanie. Boję się o te programy. Jeśli problem jest rozwiązywany od nowa, to myślę, że podział na wiele zadań staje się o wiele bardziej naturalny. Jest on bardziej zbliżony do ludzkiego sposobu myślenia. Sądzę, że mózg składa się z wielu niezależnych agentów. Dla mnie agent jest odpowiednikiem niewielkiego rdzenia. Świadomość powstaje w wyniku komunikacji pomiędzy nimi, a nie w wyniku działania dowolnego z nich. Tradycyjne oprogramowanie jest niedocenianym, ale poważnym problemem. Sytuacja będzie się tylko pogarszać — nie tylko w bankowości, ale także w lotnictwie oraz innych branżach technicznych. Problemem są miliony wierszy kodu. Można by było je zakodować na now o przy użyciu najwyżej kilku tysięcy wierszy kodu w Forth. Stosowanie maszynowej translacji nie ma jednak sensu. Wykorzystywanie tej techniki spowodowałoby, że kod stałby się jeszcze większy. Nie ma jednak sposobu walidacji takiego kodu. Koszty i ryzyko byłyby olbrzymie. Stare oprogramowanie może stać się zmorą naszej cywilizacji.

100

ROZDZIAŁ

CZWARTY

Wygląda na to, że jest pan pewien, że za następnych 10 lub 2 0 lat będzie powstawało coraz więcej oprogramowania składającego się z wielu luźno połączonych ze sobą części.

Chuck: O tak. Jestem pewien, że tak właśnie będzie. Komunikacja bezprzewodowa jest bardzo wygodna. Mówi się o m ikroagentach instalow anych w ciele, które napraw iają pewne rzeczy lub m onitorują jakieś param etry. Agenty te m ogą kom unikow ać się tylko bezprzewodowo lub być może za pom ocą sygnałów akustycznych. Nie mogą zrobić zbyt wiele. To tylko kilka molekuł. Zatem świat musi pójść właśnie w takim kierunku. Tak właśnie jest skonstruowane społeczeństwo ludzi. Mamy sześć i pół miliarda niezależnych agentów, którzy współpracują ze sobą. Zły dobór słów może doprowadzić do powstania aplikacji źle zaprojektowanych i trudnych do utrzymania. Czy tworzenie większych aplikacji z kilkunastu lub kilkuset małych słów prowadzi do powstawania żargonu? W ja ki sposób można tego uniknąć?

Chuck: Właściwie nie można. Sam czasami źle dobieram słowa. Jeśli ktoś tak robi, sam siebie wprowadza w błąd. Pamiętam, że w jednej aplikacji miałem takie słowo — nie pam iętam , co to dokładnie było — ale zdefiniowałem je, a później zmodyfikowałem . Ostatecznie miało ono przeciwne znaczenie do nazwy. To tak, jakby słowo prawo powodowało wyrównywanie do lewej. Było to bardzo mylące. Walczyłem z tym przez jakiś czas i ostatecznie zmieniłem nazwę słowa, ponieważ zrozumienie programu z tym słowem było prawie niemożliwe — wprowadzało ono zbyt wiele zamieszania. Lubię używać słów z języka naturalnego, a nie skrótów. Lubię, jeśli da się je wypowiedzieć. Z drugiej strony lubię, jeśli są krótkie. Po jakimś czasie wyczerpują się krótkie opisowe słowa z języka naturalnego i trzeba zrobić coś innego. Nie znoszę prefiksów — topornej próby stworzenia przestrzeni nazw, aby można było wielokrotnie używać takich samych słów. Dla mnie to zwykłe wykręty. Istnieje łatwy sposób rozróżniania słów, ale trzeba wykazać trochę inteligencji. Bardzo często w aplikacjach języka Forth występują osobne słowniki, w których mogą występować te same słowa. W jednym kontekście słowo znaczy jedno, a w drugim coś innego. W przypadku mojego projektu VLSI ten idealizm zawiódł. Potrzebowałem co najmniej tysiąca słów i nie są to słowa z języka naturalnego. To nazwy sygnałów lub innych elementów. Szybko musiałem odwołać się do definicji, dziwnie brzmiących słów, prefiksów i czego tam jeszcze. Kod nie jest tak czytelny, jak bym sobie tego życzył. Jednak z drugiej strony jest w nim wiele takich słów, jak nand, nor i xor, opisujących różne bramki używane w systemie. Tam, gdzie to możliwe, używam opisowych słów. Dziś spotykam innych programistów piszących w języku Forth. Nie chcę być jedynym program istą w języku Forth. Niektórzy z nich wymyślają dobre nazwy, inni robią to bardzo źle. Niektórzy tw orzą bardzo czytelną składnię, a inni nie uważają,

FORTH

101

aby to było ważne. Niektórzy wymyślają bardzo krótkie definicje słów, a inni używają słów o długości strony. Nie ma tu jednej prawidłowej reguły. Są jedynie konwencje stylistyczne. Kluczową różnicą pomiędzy językiem Forth a językami C, Prolog, Algol czy FORTRAN — czyli językami konw encjonalnym i — jest to, że w tych drugich próbow ano przewidywać wszystkie możliwe struktury i konstrukcje składniowe i wbudowywano je w język. Prowadziło to do pow staw ania bardzo niezgrabnych języków. Myślę, że język C jest niezgrabny — te wszystkie nawiasy, klamry, dw ukropki i średniki — cały ten balast. Język Forth eliminuje konieczność stosowania tego wszystkiego. Nie musiałem rozwiązywać wszystkich problemów. Musiałem jedynie dostarczyć narzędzia, które ktoś inny mógłby wykorzystać do rozwiązywania swoich problemów. Miałem umożliwić zrobienie czegokolwiek, a nie wszystkiego. Czy do mikroprocesorów należałoby dołączać kod źródłowy, tak aby można je było poprawić nawet kilka dziesięcioleci później?

Chuck: Ma pan rację. Dołączenie kodu źródłowego do mikrokomputerów byłoby dobrą dokumentacją. Język Forth jest zwięzły, zatem ułatwia spełnienie tego postulatu. Następnym krokiem byłoby jednak dołączenie kompilatora i edytora, tak by można było przeanalizować i zmodyfikować kod mikrokomputera bez potrzeby korzystania z innego kom putera (systemu operacyjnego), które na przykład zostały zagubione. Język colorForth jest moją próbą realizacji tych narzędzi. Wszystko, czego potrzeba, to kilka kilobajtów kodu źródłowego i/lub obiektowego. Z łatwością m ożna by to zapisać w pamięci flash i wykorzystywać w przyszłości. Jakie jest powiązanie pomiędzy projektem języka a projektem oprogramowania napisanego w tym języku?

Chuck: Język determinuje jego zastosowania. Zasada ta jest prawdziwa dla języków, jakimi posługują się ludzie. Zobaczmy, jakie są różnice pomiędzy językami romańskimi (francuski, włoski), zachodnimi (angielski, niemiecki, rosyjski) a wschodnimi (arabski, chiński). Języki mają wpływ na kulturę oraz sposób widzenia świata. Mają wpływ na to, co się mówi i jak się mówi. W śród tych języków angielski jest szczególnie zwięzły i coraz bardziej popularny. Podobnie jest dla języków w relacji komputer - człowiek. Pierwsze języki (COBOL, FORTRAN) były zbyt rozwlekłe. Późniejsze języki (Algol, C) miały rozbudowaną składnię. Zastosowanie tych języków musiało prowadzić do powstania obszernych, niezgrabnych opisów algorytmów. Za ich pom ocą m ożna było wyrazić wszystko, ale było to robione źle. Język Forth rozwiązuje te problemy. Jest w zasadzie pozbaw iony składni. Sprzyja kompaktowym, wydajnym opisom. Minimalizuje potrzebę stosowania komentarzy, które bywają niedokładne i odwracają uwagę od samego kodu.

1 02

ROZDZIAŁ

CZWARTY

Język Forth jest również wyposażony w prosty i wydajny mechanizm wywoływania procedur. W języku C wywoływanie procedur wymaga kosztownej konfiguracji i odtwarzania. To zniechęca do stosowania tego mechanizmu. Jednocześnie zachęca do stosowania obszernych zbiorów parametrów, które amortyzują koszty wywołań, ale prowadzą do rozbudowanych i złożonych procedur. Wydajność Fortha pozwala rozbić aplikacje na wiele małych procedur. Zazwyczaj występują one właśnie w tej postaci. Mój osobisty styl to stosow anie definicji mieszczących się w jednym wierszu — setek małych procedur. W takim przypadku ważne stają się nazwy przypisywane do kodu — zarówno jako instrument tworzenia m nem oników, jak i sposób na uzyskanie czytelności. Czytelny kod wymaga mniej dokumentacji. Brak składni w języku Forth zachęca do braku dyscypliny. W edług mojej opinii to pozwala na wykorzystanie indywidualnej kreatywności oraz przyczynia się do pow staw ania bardzo przyjemnego kodu. Inni postrzegają tę cechę jako wadę. Obawiają się utraty kontroli i braku standaryzacji. Sądzę, że w większym stopniu jest to wada zarządzania niż błąd języka. Powiedział pan: „Większość błędów dotyczy składni". W ja k i sposób unika pan innych typów błędów w programach Fortha — na przykład błędów logicznych, błędów pielęgnacji oraz wyboru złego stylu?

Chuck: Cóż, błędy w języku Forth przede wszystkim mają związek z zarządzaniem stosem. Zazwyczaj coś nieum yślnie pozostawiam y na stosie, a później wartość ta w prow adza nas w błąd. Ze słowami są związane kom entarze dotyczące stosu. Są one bardzo ważne. Mówią nam , co jest na stosie przy wejściu oraz co jest w m om encie wyjścia. To jednak tylko komentarze. Nie można im wierzyć. Niektórzy urucham iają te instrukcje i używają do weryfikacji oraz sprawdzania zachowania stosu. Ogólnie rzecz biorąc, rozwiązaniem jest faktoryzacja. Jeśli m am y słowo, którego definicja zajmuje jeden wiersz, możemy je przeczytać, myśląc o sposobie, w jaki zachowuje się stos, i wywnioskować, czy działa ono poprawnie. Możemy przetestować słowo i przekonać się, czy działa w taki sposób, jaki zamierzyliśmy, ale nawet w takim przypadku jesteśmy narażeni na błędy stosu. Słowa dup i drop są stosowane bardzo często, dlatego trzeba posługiwać się nimi ostrożnie. Bardzo ważna jest możliwość urucham iania słów poza kontekstem poprzez podanie param etrów wejściowych i analizę param etrów wyjściowych. Jeśli projektujem y w trybie dół-góra, wiemy, że wszystkie słowa zdefiniow ane wcześniej działają praw idłow o, ponieważ je przetestowaliśmy. W języku Forth jest zaledwie kilka instrukcji warunkow ych. Istnieje konstrukcja if-else-then oraz begin-while. Wyznaję filozofię, którą staram się propagować, a zgodnie z którą należy minimalizować liczbę instrukcji warunkowych w programach. Zamiast

FORTH

103

tworzyć słowo, które coś sprawdza i albo robi jedno, albo drugie, definiujemy dwa słowa: jedno, które robi jedną czynność, i drugie realizujące inną czynność. Następnie wystarczy wybrać właściwe słowo. W języku C taki sposób się nie sprawdza, poniew aż w yw ołania są kosztowne. W związku z tym stosuje się param etry, które pozwalają, aby ta sama procedura działała inaczej w zależności od sposobu wywołania. To właśnie jest przyczyną wszystkich błędów i komplikacji w tradycyjnych programach. Poprzez próby obchodzenia niedoskonałości implementacji?

Chuck: Tak. Pętli nie da się uniknąć. Pętle mogą być bardzo przyjemne. Jednak pętle w języku Forth, a przynajmniej w języku colorForth, są bardzo proste — mają jedno wejście i jedno wyjście. Jakiej rady udzieliłby pan nowicjuszowi, aby programowanie stało się przyjemniejsze i bardziej efektywne?

Chuck: Pewnie nie zdziwi pana, że poradziłbym m u nauczyć się pisać kod w języku Forth. Nawet jeśli ktoś nie zamierza zawodowo pisać kodu w języku Forth, zapoznanie się z nim pozwala przyswoić pewne umiejętności, pozwalające na spojrzenie na inne języki z innej perspektywy. Nie napisałem prawie żadnego program u w języku C, ale gdybym miał taki napisać, zrobiłbym to w stylu języka Forth — używając wielu prostych procedur. Nawet gdyby trzeba było ponieść jakieś koszty, myślę, że warto byłoby je ponieść dla poprawienia możliwości utrzymania kodu. Inna rzecz to zachowanie prostoty kodu. Podczas projektowania samolotu lub pisania aplikacji, nawet jeśli jest nią edytor tekstu, istnieje trend dodawania nowych własności tak długo, aż w końcu koszty stają się zbyt duże. Byłoby lepiej, gdyby istniało kilka edytorów tekstu — każdy koncentrowałby się na innej grupie użytkowników. Używanie W orda do napisania e-maila jest głupie. Aż 99% dostępnych funkcji jest zbędnych. Powinno się używać edytora e-maili. Kiedyś takie były, ale obowiązujący trend wydaje się daleki od takiego oprogramowania. Nie rozumiem, dlaczego tak się dzieje. Trzeba dążyć do zachowania prostoty. Jeśli tworzysz jakąś aplikację i jesteś członkiem zespołu projektowego, staraj się przekonać innych, żeby dążyli do maksymalnej prostoty. Nie należy zbyt wiele przewidywać. Nie należy rozwiązywać problemów, które być może pojawią się w przyszłości. Lepiej skoncentrować się na problemie, który mamy do rozwiązania. Przewidywanie jest bardzo niewydajne. Może się zdarzyć, że będziemy przewidywali, że wydarzy się 10 rzeczy, a faktycznie wydarzy się tylko jedna. Oznacza to marnotrawstwo wielu wysiłków. W ja k i sposób rozpoznaje pan prostotę?

Chuck: Istnieje nauka zajmująca się złożonością. Jedna z jej dziedzin zajmuje się sposobami mierzenia złożoności. Definicja, którą lubię — nie wiem, czy jest jakaś

104

ROZDZIAŁ

CZWARTY

inna — mówi, że prostszy jest taki opis, który jest krótszy. Jeśli mamy dwa pojęcia, prostsze jest to, które ma krótszy opis. Jeśli potrafimy znaleźć krótszą definicję czegoś, potrafimy znaleźć prostszą definicję. Czasami taka definicja ma subtelną wadę, ponieważ każdy opis zależy od kontekstu. Jeśli ktoś potrafi napisać bardzo krótką procedurę w języku C, może powiedzieć, że jest ona bardzo prosta, ale zależy od dostępności kompilatora języka C, systemu operacyjnego oraz kom putera, na którym te wszystkie elementy się uruchom ią. A zatem w rzeczywistości nie mamy prostej rzeczy. Jeśli weźmiemy pod uwagę szerszy kontekst, okaże się, że jest to dość skomplikowane. Myślę, że ze złożonością jest tak jak z pięknem. Nie można go zdefiniować, ale kiedy się je zobaczy, natychmiast się je rozpoznaje. Jaki wpływ na programowanie ma praca zespołowa?

Chuck: Praca zespołowa — bardzo przereklamowane pojęcie. Pierwszym zadaniem zespołu jest podział problemu na stosunkowo niezależne części. A potem przydział każdej z części innej osobie. Kierownik zespołu jest odpow iedzialny za to, aby poszczególne części do siebie pasowały. Czasami dwie osoby potrafią wspólnie pracować. Rozmawianie o problemie pozwala go objaśnić. Jednak zbyt wiele komunikacji jest szkodliwe. Myślenie grupowe nie sprzyja kreatywności. Kiedy kilka osób pracuje razem, najczęściej jedna wykonuje pracę. Czy to dotyczy projektów wszystkich typów. Napisanie czegoś w rodzaju OpenOffice.org wydaje się dość złożone, nieprawdaż?

Chuck: Coś takiego jak OpenOffice.org można by rozdzielić na mniejsze projekty, które byłyby program ow ane przez oddzielne osoby. Ich kom unikacja pow inna sprowadzać się do zapewnienia zgodności. W ja k i sposób rozpoznaje pan dobrego programistę?

Chuck: Dobry programista szybko pisze kod dobrej jakości. Dobry kod jest poprawny, zwięzły i czytelny. Szybko znaczy kilka godzin do kilku dni. Zły programista dużo mówi o problemie, marnuje czas na planowanie, zamiast pisać, a podstawą jego kariery staje się pisanie i debugowanie kodu. Jaka jest pańska opinia o kompilatorach? Czy uważa pan, że maskują one prawdziwe umiejętności programistów?

Chuck: Kompilatory to według mojej opinii najgorszy kod, jaki kiedykolwiek napisano. Są zwykle pisane przez ludzi, którzy wcześniej nigdy nie pisali kompilatora i nigdy nie będą robili tego ponownie.

FORTH

1 05

Im bardziej rozbudowany język, tym bardziej złożony, podatny na błędy i trudny w użytkowaniu kompilator. Jednak prosty kompilator dla prostego języka to kluczowe narzędzie — choćby z powodu dokumentacji. Ważniejszy od kom pilatora jest edytor. Duży w ybór edytorów pozwala każdemu programiście na wybór własnego. Jest to bardzo szkodliwe dla pracy grupowej. Wielość edytorów sprzyja rozwojowi chałupniczej branży zajmującej się translacją jednego formatu na drugi. Innym grzechem głów nym autorów kom pilatorów jest skłonność do używania wszystkich znaków specjalnych, jakie można znaleźć na klawiaturze. Z tego powodu klawiatury nigdy nie staną się mniejsze i prostsze. Z kolei kod źródłowy staje się nie do przebycia. Umiejętności programisty są jednak niezależne od tych narzędzi. Potrafi on szybko opanować ich osobliwości i tworzyć dobry kod. W ja k i sposób należy dokumentować oprogramowanie?

Chuck: Bardzo wysoko cenię komentarze. Znacznie bardziej niż inni. Oto kilka powodów: •

Jeśli kom entarze są zwięzłe, często są niejasne. W tedy trzeba się domyślać, co oznaczają.



Jeśli są obszerne, dominują nad kodem, w którym są osadzone i który próbują objaśniać. Trudno znaleźć i ocenić komentowany kod.



Komentarze często są źle napisane. Programiści nie mają talentu literackiego, zwłaszcza jeśli język, w którym piszą komentarze, nie jest ich językiem ojczystym. Stosowanie żargonu i błędy gramatyczne pow odują, że kom entarze często są nieczytelne.



Co ważniejsze, kom entarze często są niedokładne. Kod może się zmienić bez aktualizacji komentarzy. Chociaż kod czasami jest uważnie przeglądany, komentarze przegląda się rzadko. Komentarze niedokładne stwarzają więcej kłopotów niż całkowity brak komentarzy. Czytelnik musi ocenić, czy komentarz lub kod jest prawidłowy.

Komentarze często w prow adzają czytelnika w błąd. Powinny one objaśniać przeznaczenie kodu, a nie sam kod. Parafrazowanie kodu do niczego się nie przydaje. Jeśli zaś jest niedokładne, staje się bardzo mylące. Komentarze powinny wyjaśniać, dlaczego kod istnieje, jakie funkcje próbuje realizować oraz wszystkie sztuczki zastosowane dla osiągnięcia celu. W języku colorForth komentarze są oznaczone jako zacieniowane bloki. Powoduje to usunięcie ich z właściwego kodu, przez co kod ten staje się bardziej czytelny. Pomimo to są one natychm iast dostępne do czytania lub aktualizacji. Sposób ten ogranicza również rozmiar komentarzy do rozmiaru kodu.

1 06

ROZDZIAŁ

CZWARTY

Komentarze nie zastępują właściwej dokum entacji. Trzeba stworzyć dokum ent, który opisowo objaśni w ybrany m oduł kodu. Powinien on rozwijać kom entarze i koncentrować się na kompletnym opisie. Oczywiście rzadko się to robi, zwykle nie m ożna sobie pozwolić na tworzenie tak szczegółowej dokum entacji ze względów ekonomicznych. Taki dokum ent m ożna również łatwo stracić, ponieważ jest on oddzielony od kodu. Cytat ze strony http://www.colorforth.com/HOPL.html: „Problem opatentowania języka Forth był bardzo szeroko dyskutowany. Ponieważ jednak patenty programów są kontrowersyjne i mogą wymagać interwencji Sądu Najwyższego, instytucja NRAO zrezygnowała z ubiegania się o nie. W związku z tym praw a autorskie powróciły do mnie. Nie uważam, że pomysły powinny podlegać patentom. Doszedłem więc do wniosku, że jedyną szansą dla języka Forth jest to, aby stał się własnością publiczną. Tam rozkwitł". Patenty programów dziś także wywołują kontrowersje. Czy pańska opinia na temat patentów jest w dalszym ciągu taka sama?

Chuck: Nigdy nie byłem zwolennikiem patentów na oprogram ow anie. Zbytnio przypom ina to patentow anie myśli. A patentow anie języków (protokołów) jest szczególnie kłopotliwe. Język może odnieść sukces tylko wtedy, gdy jest używany. Wszystko, co zniechęca do używania języka, jest głupie. Czy sądzi pan, że patentowanie technologii uniemożliwia lub ogranicza je j rozpowszechnianie?

Chuck: Sprzedawanie oprogram ow ania, ze względu na łatwość kopiow ania, jest trudne. Firmy podejmują wiele wysiłków w celu ochrony swoich produktów. Często prowadzi to do sytuacji, w której produkty stają się nie do użytku. Moja odpowiedź na ten problem jest taka, aby sprzedawać sprzęt i rozdawać oprogramowanie. Sprzęt jest trudniejszy do skopiowania, a jego cena wzrasta w miarę powstawania nowego oprogramowania. Patenty to jeden ze sposobów rozwiązywania tych problemów. Patenty udowodniły, że są wielkim dobrodziejstwem wynalazczości. Potrzebny jest jednak pewien umiar, aby nie pow staw ały bezmyślne patenty oraz by była zachow ana spójność z wcześniejszymi dziełami (patentam i). Zagw arantow anie i wymuszenie ich jest związane z olbrzymimi kosztami. Ostatnie propozycje reform w prawie patentowym zagrażają indywidualnej wynalazczości na rzecz dużych firm. To byłoby tragiczne.

FORTH

107

108

RO ZDZIAŁ

CZWARTY

ROZDZIAŁ

PIĄTY

BASIC

W 1963 roku Thomas Kurtz i John Kemeny opracowali BASIC — język programowania ogólnego przeznaczenia. Zaprojektowano go z myślą o nauczaniu programowania początkujących studentów, a także o pisaniu użytecznych programów przez doświadczonych programistów. Pierwotne cele języka obejm owały abstrakcję od szczegółów sprzętowych. Język bardzo się rozpowszechnił w latach siedemdziesiątych — wraz z rozwojem m ikrokom puterów. Wiele kom puterów osobistych było wyposażonych we własne odmiany BASIC-a. Choć dzięki powstaniu im plementacji Visual Basic Microsoftu i True BASIC Kurtza język odszedł od numerowania wierszy i instrukcji GOTO, wiele pokoleń programistów uczyło się sztuki programowania, używając języka zachęcającego do eksperymentowania i nagradzającego ciekawość.

1 09

Cele języka BASIC Jaki jest najlepszy sposób, by nauczyć się programowania?

Tom Kurtz: Początkujący programiści nie pow inni ślęczeć nad podręcznikam i. Większość podręczników jest o wiele za obszerna, aby utrzymać uwagę nowicjusza. W ym agane są proste zasady kodow ania, łatw o dostępne i łatw e w posługiw aniu się implementacje oraz wiele przykładów. Niektórzy nauczyciele preferują metodę nauczania, zgodnie z którą programiści muszą zdobyć wiele doświadczenia, zanim zaczną je stosować. Pan zdecydował się na stworzenie języka, którego może używać programista na dowolnym poziomie zaawansowania. Języka, w którym można wzbogacać swoją wiedzę przez doświadczenie.

Tom: Zgadza się. Kiedy ktoś nauczy się programować, poznawanie nowych języków programowania nie jest trudne. Najtrudniej nauczyć się pierwszego języka. Jeśli nowy język nie jest zbytnio rozbudowany, to nauczenie się go wymaga tylko niewielkiego kroku. Można bowiem bazować na językach znanych wcześniej. Można tu przywołać analogię do języków naturalnych (których przyswojenie jest znacznie trudniejsze): kiedy ktoś nauczy się pierwszego języka romańskiego, nauczenie się drugiego języka z tej samej grupy staje się dużo łatwiejsze. Po pierwsze, gramatyka jest podobna, wiele słów m a takie samo brzmienie, a składnia jest stosunkow o prosta (na przykład czasownik występuje w środku — tak jak w języku angielskim — lub na końcu zdania). Im prostszy jest pierwszy język, tym łatwiej przeciętnemu uczniowi się go nauczyć. Czy to ewolucyjne podejście stało się dla pana inspiracją do podjęcia decyzji o stworzeniu języka BASIC?

Tom: Kiedy zdecydowaliśmy się na stworzenie BASIC-a (John Kemeny i ja, około 1962 roku), m iałem zamiar stworzyć uproszczoną wersję FORTRAN-a lub Algola. To się nie udało. Większość języków program ow ania zawiera niejasne reguły gramatyczne, które są barierą dla początkującego studenta. Postanowiliśmy usunąć takie konstrukcje z BASIC-a. Oto kilka założeń uwzględnionych w projekcie BASIC-a: •

Jeden wiersz, jedna instrukcja. Nie mogliśmy użyć kropki do zakończenia instrukcji, tak jak w języku JOSS. Z kolei konwencja Algola — stosowanie średnika — wydawała nam się bez sensu, podobnie zresztą jak znak kontynuacji (C) z FORTRAN-a.



Numery wierszy są celami instrukcji GOTO. Musieliśmy zastosować num ery wierszy, poniew aż było to na długo przed powstaniem technologii WYSIWYG. Wprowadzanie nowego pojęcia — „etykiety instrukcji” — nie wydawało nam się dobrym pomysłem (później, kiedy tworzenie

110

ROZDZIAŁ

PIĄTY

i edycja programów stały się prostsze, umożliwiliśmy użytkownikom rezygnację z numerowania wierszy, pod warunkiem że użytkownik nie używa instrukcji GOTO; od tamtego czasu BASIC stał się w pełni strukturalny). •

Wszystkie operacje arytmetyczne są zmiennoprzecinkowe. Jednym z trudniejszych zadań dla początkującego jest nauczenie się powodów, dla których trzeba rozróżniać typ całkowity od zmiennoprzecinkowego. W tamtych czasach prawie wszystkie języki program owania skłaniały się ku architekturze większości sprzętu komputerowego, w którym liczby zmiennoprzecinkowe były używane do obliczeń inżynieryjnych, a liczby całkowite do prostych obliczeń (z powodów wydajnościowych). Dzięki decyzji o tym, że wszystkie operacje arytmetyczne są zmiennoprzecinkowe, zabezpieczyliśmy użytkownika przed koniecznością definiowania typów liczb. Byliśmy zmuszeni wykonywać pewne skomplikowane działania wewnętrzne, kiedy była wymagana liczba całkowita (tak jak w indeksie tablicy), a użytkownik podał liczbę różną od całkowitej (na przykład 3,1). W takich sytuacjach stosowaliśmy zaokrąglenia. Podobne problemy mieliśmy z rozróżnianiem pomiędzy ułam kami binarnymi a dziesiętnymi. Na przykład w instrukcji:

FOR I = 1 TO 2 STEP 0.1 część dziesiętna — 0,1 — jest nieskończonym okresowym ułamkiem binarnym. Aby określić warunek zakończenia pętli, musieliśmy wykorzystać współczynnik rozmycia — ang. fuzz factor (niektórych z problem ów przekształceń pomiędzy formatem binarnym a dziesiętnym nie uwzględniono w oryginalnym BASIC-u; zostały one jednak uwzględnione w znacznie nowszym języku True BASIC). •

Liczba jest liczbą. Nie ma potrzeby form atow ania podczas w prow adzania liczb w kodzie lub w instrukcjach przetw arzania danych. Instrukcja PRINT wyświetla wyniki w domyślnym formacie. Instrukcja FORMAT lub jej odpowiedniki w innych językach są dość trudne do nauczenia się. Początkujący użytkownik mógłby się zastanawiać, dlaczego musi się jej uczyć. Przecież chciał jedynie uzyskać prostą odpowiedź.



Sensowne wartości domyślne.

Jeśli istnieją jakieś komplikacje dla bardziej zaawansowanych użytkowników, nie powinny one być widoczne dla początkujących. Trzeba przyznać, że oryginalny BASIC nie zawierał zbyt wielu zaawansowanych własności, ale pojęcie było i jest istotne. Prawidłowość naszego podejścia była poparta tym, że nauczenie nowicjusza pisania prostych program ów w języku BASIC zajm owało godzinę. Nasze szkolenie rozpoczynaliśmy od czterech jednogodzinnych lekcji. Następnie zmniejszyliśmy ich liczbę do trzech, potem do dwóch, aż wreszcie do kilku taśm wideo.

BASIC

ttt

Kiedyś doszedłem do wniosku, że wstępny kurs informatyki można przeprowadzić z w ykorzystaniem wersji BASIC-a (nie tej oryginalnej, ale wersji wyposażonej w konstrukcje programowania strukturalnego). Za pomocą BASIC-a niemożliwe było jedynie wprowadzenie pojęcia wskaźników i alokowanej pamięci! Inny problem: w czasach początków komputerów uruchomienie programu wymagało w ykonania kilku kroków. Kompilacja. Linkowanie i ładow anie. W ykonywanie. Zdecydowaliśmy, że w BASIC-u każde uruchomienie będzie uwzględniało wszystkie te kroki, tak aby użytkownik nawet nie był ich świadomy. W tam tym okresie historii kom puterów większość języków wymagała wieloprzebiegowego kompilatora, który zużywał zbyt wiele cennego czasu komputera. W związku z tym kompilowaliśmy program raz i uruchamialiśmy wiele razy. Jednak małe programy pisane przez studentów były kompilowane i uruchamiane tylko raz. W ym agało to od nas opracow ania jednoprzebiegowego kom pilatora i przejścia bezpośrednio do fazy wykonania, jeśli faza kompilacji zakończyła się bez błędów. Ponadto po wykryciu pięciu błędów zatrzymywaliśmy raportowanie. Przypominam sobie kilkustronicowe wydruki błędów z FORTRAN-a zawierające szczegółowy opis wszystkich błędów składniowych w programie, a których przyczyną było pominięcie jednej kluczowej kropki na początku. Widziałem podręcznik BASIC-a z 1964 roku. Podtytuł brzmiał: „Elementarny język algebraiczny opracowany dla systemu z podziałem czasu w Dartmouth college". Co to jest język algebraiczny?

Tom: Cóż, obaj byliśmy m atematykami, zatem jest naturalne, że pewne elementy języka wyglądają na matematyczne. Na przykład podnoszenie liczb do potęgi i tego rodzaju operacje. Także funkcje, które dodaliśmy, były matematyczne — na przykład sinus i cosinus. Myśleliśmy bowiem o studentach, którzy mieli wykonywać rachunki przy użyciu program ów BASIC-a. Z tego pow odu język był ukierunkow any na obliczenia numeryczne. Pod tym względem różnił się od innych języków powstałych w tamtym czasie, na przykład COBOL-a, który opracowano z myślą o innych cechach. Podczas projektowania BASIC-a przyglądaliśmy się temu, jak wyglądał FORTRAN tam tych czasów. Dostęp do niego na dow olnym z dużych kom puterów IBM był możliwy za pośrednictwem 80-kolumnowych kart perforowanych. W naszym kampusie dostęp do komputerów odbywał się za pośrednictwem maszyn dalekopisowych, których używaliśmy jako urządzeń wejściowych. Zdecydowaliśmy się na takie rozwiązanie, ponieważ były one kom patybilne z liniami telefonicznymi. Za ich pośrednictwem mogliśmy podłączyć term inale w różnych miejscach kam pusu do centralnego komputera. Wszystko zatem zrealizowaliśmy za pośrednictwem oprzyrządowania zaprojektow anego wcześniej do celów kom unikacji — na przykład łączności dalekopisowej, przechowywania czy przekazywania komunikatów itp. A zatem udało nam się obejść bez kart perforowanych.

1 12

ROZDZIAŁ

PIĄTY

Po drugie, chcieliśmy pozbyć się wszystkich wymagań, jakie stawiały użytkownikom karty perforowane. Owe wymogi polegały na przykład na tym, że pewne elementy musiały znajdować się w określonych kolumnach na karcie. Chcieliśmy opracować swobodną formę — użytkownik wpisywał coś na klawiaturze dalekopisu będącej notabene standardową klawiaturą QWERTY, ale wyłącznie z wielkimi literami. W taki sposób pow staw ała form uła języka. Tekst program u m a być łatwy do wpisywania. Pierwotnie BASIC był niezależny od spacji. Wprowadzenie spacji bądź ich brak w tekście program u nie m iały żadnego znaczenia. Język był pierwotnie skonstruowany w taki sposób, że niezależnie od tego, czy wprowadzany tekst programu zawierał spacje, czy nie, kom puter interpretow ał go prawidłowo. Powodem takiej konstrukcji było to, że niektóre osoby, zwłaszcza pracownicy dydaktyczni, nie potrafili dobrze pisać na maszynie. Brak wrażliwości na spacje przetrwał do pierwszych wersji BASIC-a na kom putery osobiste. Doprowadziło to do dość zabawnych sytuacji związanych z interpretacją wpisywanego tekstu. W systemie Dartmouth nie było problemu niejednoznaczności. Dopiero w późniejszych latach, gdy język się rozwinął, spacje zaczęły być potrzebne. Nazwa zmiennej musiała być zakończona spacją bądź symbolem. Według jednej z krytycznych opinii na temat BASIC-a jest to język zaprojektowany do nauki programowania. Próba pisania w nim dużych programów powoduje, że język staje się chaotyczny. Co pan o tym myśli?

Tom: Jest to zdanie wypowiedziane przez kogoś, kto nie śledził rozwoju BASIC-a na przestrzeni lat. Nie jest to język w początkowej fazie rozwoju. W języku True BASIC osobiście napisałem programy składające się nawet z 10 000 i 20 000 wierszy, które działały całkiem dobrze. Mógłbym napisać programy złożone z 30 000 lub nawet 40 000 wierszy i również nie byłoby żadnego problemu. Implementacja języka i jego projekt to dwie różne rzeczy. Projekt języka opisuje, co użytkow nik musi wpisać, aby w ykonać swoją pracę. Po um ożliw ieniu korzystania z bibliotek m ożna robić wszystko, co się chce. Następnie pozostaje kwestią implementacji języka to, czy będzie on obsługiwał programy o dowolnych rozmiarach. Język True BASIC obsługuje takie programy. Pod tym względem różni się on od innych wersji BASIC-a. Na przykład Microsoft BASIC i stw orzony na jego podstaw ie Visual BASIC m ają pewne ograniczenia. Inne wersje BASIC-a, które pojawiały się na przestrzeni lat, miały inne ograniczenia. Były to jednak ograniczenia w implementacji, a nie w projekcie języka.

BASIC

113

Jakie cechy języka True BASIC umożliwiły pisanie dużych programów?

Tom: Właściwie tylko jedna — enkapsulacja, czyli moduły. Struktury enkapsulacji nazwaliśmy modułami. Własność ta została ustandaryzowana przez komitet standaryzacyjny BASIC-a, zanim jeszcze pojawiła się w implementacjach. Miało to miejsce w początkowych latach języka True BASIC. M oduły w prow adzono do standardu języka około 1990 lub 1991 roku. Nowoczesne komputery mają mnóstwo pamięci i bardzo szybkie procesory. W związku z tym nie było problemu z implementacją tego rodzaju mechanizmu. Mimo że trzeba się było cofnąć do dwu przebiegowego kompilatora?

Tom: Linker także jest napisany w języku True BASIC. To właściwie bardzo uproszczona wersja języka True BASIC. Kod jest kompilowany do postaci kodu B, podobnego do kodu P z Pascala. Właściwe linkowanie polega na uruchomieniu instrukcji kodu B. Dostępny jest bardzo szybki interpreter, który uruchamia instrukcje kodu B. Język True BASIC, podobnie jak oryginalny BASIC, jest kompilowany. Oryginalny kod BASIC-a był kompilowany na bezpośredni kod maszynowy w jednym przebiegu. Kod True BASIC kompiluje się do postaci kodu B. Jest on bardzo prosty, zatem wystarczy szybka pętla w języku C. Pierwotnie napisano ją dla platform DOS-owych. M echanizm ten działa bardzo szybko. Nie tak szybko jak w językach zoptym alizowanych pod kątem szybkości, ale dosyć szybko. Jak powiedziałem, w kodzie B są instrukcje dwuadresowe. Zatem jest on bardzo szybki. W początkowym okresie BASIC-a interpretacja nie spow alniała kodu, ponieważ operacje zmiennoprzecinkowe były wykonywane programowo. Upieraliśmy się, aby w językach True BASIC i Dartm outh BASIC zawsze posługiwano się liczbami dwupozycyjnymi. Dzięki tem u 99% użytkow ników nie m usiało przejmować się dokładnością. Teraz oczywiście posługujem y się standardem IEEE, który jest udostępniany automatycznie przez wszystkie układy. Czy uważa pan, że jedyną różnicą pomiędzy językiem zaprojektowanym pod kątem nauczania a językiem przeznaczonym do tworzenia profesjonalnego oprogramowania jest to, że tego pierwszego można łatwiej się nauczyć?

Tom: Nie. Wszystko zależy od rozwoju wypadków. Język C pojawił się w odpowiednim czasie i dał dostęp do sprzętu. Języki obiektowe będące dziś w użyciu — to, czego uczą, oraz to, co robią profesjonaliści — są pochodnymi tego środowiska. Dlatego te języki są bardzo trudne do przyswojenia. Oznacza to, że osoby, które używają tych pochodnych języków, są profesjonalnie wyszkolone i należą do zespołów programistycznych, mogą tworzyć znacznie bardziej

114

ROZDZIAŁ

PIĄTY

zaawansowane aplikacje — na przykład te używane do realizacji filmów, dźwięku i tego rodzaju operacji. Takie funkcje znacznie łatwiej zrealizować za pomocą języków obiektowych, na przykład Objective-C. Jeśli jednak nie jest to naszym celem i chcemy jedynie napisać dużą aplikację, to możemy skorzystać z języka True BASIC wywodzącego się z języka D artm outh BASIC. Co jest ostatecznym celem ułatwień w posługiwaniu się językam i programowania? Czy kiedykolwiek uda się stworzyć język tak prosty, że każdy użytkownik komputera będzie w stanie napisać własny program?

Tom: Nie sądzę. Wiele działań, które wykonywaliśmy w D artm outh za pom ocą BASIC-a, teraz m ożna wykonać przy użyciu innych aplikacji, na przykład arkuszy kalkulacyjnych. Za pom ocą arkuszy kalkulacyjnych m ożna wykonywać dość skomplikowane operacje. Co więcej, niektóre aplikacje matematyczne, o których myśleliśmy, m ożna teraz zrealizować z w ykorzystaniem bibliotek program ów tw orzonych przez społeczności profesjonalistów. Szczegóły języka programowania właściwie nie mają znaczenia, ponieważ nowego języka m ożna nauczyć się w jeden dzień. Jeśli jest dostępna dobra dokumentacja, nauczenie się nowego języka jest łatwe. Po prostu nie widzę potrzeby tworzenia nowego języka, który m iałby być językiem doskonałym . Bez koncentracji na specyficznej dziedzinie nie można stworzyć dobrego języka — to pomysł z góry skazany na porażkę. To tak, jakby zadać pytanie o to, jaki jest najlepszy język mówiony lub pisany na świecie. Czy jest nim język polski? A może angielski? Lub jeszcze inny? Czy można taki zdefiniować? Nie można, ponieważ wszystkie pisane i mówione języki uwzględniają warunki życia w miejscach, w których są używane. W związku z tym nie ma czegoś takiego jak doskonały język. Doskonały język programowania także nie istnieje. Czy zawsze miał pan taki zamiar, żeby ludzie pisali wiele bardzo małych programów i nazywali siebie programistami?

Tom: Było to naszym celem. Dziwne jest jednak to, że kiedy język się rozwinął, to bez dodawania zbytniej złożoności stało się możliwe pisanie program ów składających się z 10 000 wierszy. To dlatego, że staraliśmy się, by wszystko było bardzo proste. Cała idea, a jednocześnie urok systemów z podziałem czasu, polega na tym, że cykl przetw arzania jest tak krótki, iż nie trzeba się m artwić optym alizacją program u. Trzeba jedynie zoptymalizować czas osobisty. Kiedyś, zanim opracowaliśmy język BASIC, przez kilka lat pisałem program na kom puter w MIT. W ykorzystywałem przy tym system SAP (od ang. Symbolic Assembler Program) dla kom putera IBM 704. Starałem się napisać ten program oraz robić wszystko to, co miało sens. W związku z tym zoptym alizowałem obliczenia w taki sposób, aby nie wykonywały się bloki, które nie były konieczne. Napisałem

BASIC

1 15

program do końca. Niestety, ten przeklęty program nie działał, a przekonanie się, że nie działa, zajęło mi miesiąc, ponieważ m iałem dostęp do kom putera co dwa tygodnie. Cykl przetwarzania trwał dwa tygodnie. Nie wiem, ile zużyłem m inut lub godzin czasu kom putera w tym okresie. Za rok, kiedy pojawił się FORTRAN, napisałem w nim program. Nie potrzebowałem chyba nawet pięciu m inut czasu komputera, aby wszystko zaczęło działać. Cały ten biznes z optymalizacją i kodowaniem jest całkowicie błędny. Nie trzeba tego robić. Optymalizację przeprowadza się tylko wtedy, kiedy jest to konieczne, i robi się to później. Języki wyższego poziomu optymalizują czas komputera automatycznie, ponieważ popełnia się mniej błędów. Rzadko spotykam się z takim zdaniem.

Tom: Pod tym względem informatycy są w pewnym sensie głupcami. Programiści komputerowi koncentrują się na niewielkich, fascynujących detalach programowania i nie patrzą na system z inżynierskiego punktu widzenia, próbując optymalizować cały system. Próbujemy optymalizować bity i bajty. Tak czy owak, to tylko moja dygresja. Nie jestem pewien, czy potrafiłbym to uzasadnić. Czy ewolucja w sprzęcie miała wpływ na ewolucję języka?

Tom: Nie, ponieważ naszym założeniem było, aby język stanowił ochronę przed koniecznością znajomości sprzętu. Kiedy stworzyliśmy BASIC, chcieliśmy, by był niezależny od sprzętu. W języku bądź jego właściwościach nie dodano później niczego, co byłoby związane ze sprzętem. Nie jest to prawda w przypadku niektórych wczesnych odmian BASIC-a na komputery osobiste. Wersje te były bardzo luźno związane z tym, co zrobiliśmy w Dartmouth. Na przykład w jednej z wersji BASIC-a na kom putery osobiste wprowadziliśmy możliwość ustawiania lub inspekcji zawartości podanej lokalizacji w pamięci. W naszym BASIC-u w D artm outh nie było takiej funkcji. Zatem tam te wersje BASIC-a na kom putery osobiste bardzo zależały od możliwości sprzętu. Projekt języków na komputery osobiste odzwierciedlał sprzęt, który był dostępny. Gdyby rozmawiał pan z ludźmi, którzy stworzyli Microsoft BASIC, odpowiedzieliby, że sprzęt miał wpływ na własności języka. Jednak w przypadku BASIC-a z Dartmouth tak nie było.

1 16

ROZDZIAŁ

PIĄTY

Zdecydował się pan na to, aby wszystkie działania arytmetyczne były zmiennoprzecinkowe po to, by było łatw iej użytkownikom. Jaka jest pana opinia na temat tego, w ja k i sposób nowoczesne programy komputerowe obsługują liczby? Czy powinniśmy przejść na dokładną formę reprezentacji z wykorzystaniem liczb o dowolnej precyzji, gdzie liczby uważa się za rodzaj tablicy cyfr?

Tom: Istnieje wiele sposobów reprezentowania liczb. Prawdą jest, że większość języków w tamtych czasach — tak jak i współczesne języki — odzwierciedlała dostępność typów reprezentacji liczb dostępnych na dzisiejszym sprzęcie. Na przykład w przypadku program ow ania w języku C istnieją typy liczbowe odpow iadające liczbowej reprezentacji dostępnej na sprzęcie — liczby zm iennoprzecinkowe pojedynczej precyzji, liczby zmiennoprzecinkowe podwójnej precyzji, liczby całkowite pojedynczej precyzji, liczby całkowite podwójnej precyzji itp. Wszystkie one są częścią języka C, ponieważ zaprojektowano je w celu działania na sprzęcie. Dlatego właśnie muszą dawać dostęp do reprezentacji liczb w określonym komputerze. W jaki sposób można reprezentować liczby w komputerach? Dzięki temu, że komputer zawiera stałą liczbę cyfr binarnych lub bitów binarnych — które są wykorzystywane w większości kom puterów — oraz że może przedstawić za ich pom ocą skończoną liczbę cyfr dziesiętnych, wiemy o ograniczeniach dotyczących typu i liczb, jakie można reprezentować. Wiadomo, że prowadzi to do określonych typów błędów zaokrąglania. Niektóre języki dają dostęp do nieograniczonej dokładności, na przykład 300 cyfr dziesiętnych, ale w łasność ta jest realizowana przez oprogram ow anie, poprzez reprezentację bardzo dużych liczb jako potencjalnie nieskończonych tablic cyfr. Wszystko to jest jednak wykonywane programowo. W konsekwencji jest bardzo wolne. Nasze podejście w języku BASIC sprowadzało się do prostego stwierdzenia, że liczba jest liczbą. „3” jest liczbą, ale „1 ,5 ” także jest liczbą. Nie obarczaliśmy naszych studentów koniecznością pamiętania o takim rozgraniczeniu. Niezależnie od tego, co wprowadzili jako liczbę, staraliśmy się robić wszystko, aby przedstawić tę liczbę w postaci zmiennoprzecinkowej dostępnej na określonej maszynie. W arto powiedzieć, że kiedy po raz pierwszy zastanawialiśmy się nad tym, jakiego komputera użyć (w 1964 roku ostatecznie użyliśmy komputera GE), upieraliśmy się, żeby kom puter ten obsługiwał sprzętowo liczby zmiennoprzecinkowe. Chcieliśmy bowiem uniknąć konieczności programowej realizacji arytmetyki. Oczywiście wiąże się to z pewnymi niedokładnościami, ale takie jest życie. Czy instrukcje GOTO i GOSUB to wybór spowodowany sprzętem, ja k i był dostępny w tamtych czasach? Czy w nowoczesnych językach programowania te instrukcje również powinny być dostępne?

Tom: Nie uważam, żeby sprzęt miał tu coś do rzeczy. To bez znaczenia.

BASIC

117

W niektórych językach strukturalnych były potrzebne takie instrukcje, ale to było dawno, 20 lub 30 lat temu, więc nie sądzę, aby stanowiło to istotny problem. Było to wtedy ważne, ponieważ odzwierciedlało sposób, w jaki w tamtych czasach pisano programy komputerowe w języku maszynowym oraz języku asemblera. Kiedy tworzyliśmy BASIC, idea program ow ania strukturalnego jeszcze nie była znana. W związku z tym wzorowaliśmy BASIC na języku FORTRAN, a w nim była instrukcja GOTO. Jakie kryteria brał pan pod uwagę, zastanawiając się nad wprowadzeniem nowych własności do języka w czasie jego rozwoju?

Tom: Starałem się uwzględniać wszystko, co było potrzebne w danym czasie — żadnej teorii. Na przykład jedną z własności, jaką dodaliśmy już po tym, jak BASIC ujrzał światło dzienne w 1964 roku, była możliwość przetwarzania informacji nienumerycznych — ciągów znaków. W prowadziliśm y możliwość przetw arzania ciągów znaków, aby ludzie piszący programy, na przykład gry, mogli posługiwać się słowami „tak” lub „nie” zamiast „1 ” lub „0” . W oryginalnym BASIC-u „1” oznaczało „tak ” , a „0” oznaczało „nie” . Wkrótce jednak wprowadziliśmy możliwość obsługi ciągów znaków. Zrobiliśmy to tylko dlatego, że było potrzebne.

Projektowanie kompilatorów Kiedy pisał pan pierwszą wersję BASIC-a, udało się panu stworzyć jednoprzebiegowy kompilator, podczas gdy wszyscy inni tworzyli kompilatory wieloprzebiegowe. Jak do tego doszło?

Tom: To bardzo proste, jeśli projekt języka jest stosunkowo prosty. Wiele języków stosuje proste rozwiązanie tego problemu. Wszystko było znane. Jedyne, co trzeba było przenieść do tzw. przebiegu jeden i pół, to wypełnienie danych do realizacji przekazywania sterowania w przód (ang. forward transfer). Była to jedyna rzecz, która uniemożliwiała stworzenie całkowicie jednoprzebiegowego kompilatora. W pierwszych stu wierszach programu jest instrukcja GOTO, która skacze gdzieś do obszaru pierwszych tysiąca wierszy. Czy w takiej sytuacji występuje faza łączenia?

Tom: To właśnie zrobiliśmy. Był to odpowiednik listy łączenia (ang. linking list). W języku asemblera kom putera, na którym pracowaliśmy, nie skorzystaliśmy ze struktury listy jednokierunkowej, ale to było właśnie to. Mogła to być niewielka tablica z adresami, które były wypełniane później.

1 18

ROZDZIAŁ

PIĄTY

Czy w tym przypadku możliwe było jednoczesne parsowanie i generowanie kodu?

Tom: Tak. Inna rzecz, że język celowo zaprojektowano prosto, tak aby było możliwe jednoprzebiegowe parsowanie. Mówiąc inaczej, nazwy zm iennych są bardzo ograniczone. Litera lub litera i cyfra. Nazwy tablic także były proste — jednoi dwuwymiarowe tablice zawsze miały nazwy złożone z pojedynczej litery, za którą występował lewy nawias. Parsowanie było trywialne. Nie było tablicy przeszukiwania. Co więcej, przyjęliśmy prostą strategię: pojedyncza litera lub litera z cyfrą daje 26 razy 11 nazw zmiennych. Przydzieliliśmy dla nich miejsce w pamięci — stałe miejsce w pamięci na wartości tych zmiennych, jeśli miały one wartości. Nie używaliśmy nawet tablicy symboli. Czy potrzebne wam były deklaracje zmiennych?

Tom: Nie, absolutnie nie. Tablice zawsze były oznaczone literą, za którą występował lewy nawias — zatem w istocie to była deklaracja. Spróbuję się zastanowić, czy to dobrze pamiętam. Jeśli używało się tablicy, na przykład a(3), była to automatycznie tablica... myślę, że od 0 do 10. Inaczej mówiąc, były stosowane automatyczne domyślne deklaracje. Tablice zaczynały się od zera, ponieważ byliśmy matematykami. Jeśli chce się przedstawić współczynniki wielomianu, to pierwszy m a indeks 0. Czy było to proste do zaimplementowania?

Tom: Było trywialne do zaimplementowania. W implementacji kompilatora jest wiele elementów, które wcale nie są trudne. Zaimplementowanie przeszukiwania tablicy symboli stosowanej w bardziej zaawansowanych wersjach BASIC-a, które powstały później, także nie było trudne. Czy optymalizacja jest tak kłopotliwa?

Tom: Nie przejmowaliśmy się optymalizacją, ponieważ 99% wszystkich programów pisanych w tedy przez studentów i pracow ników dydaktycznych to były bardzo niewielkie, trywialne programy. Optymalizacja nie miała sensu. Powiedział pan, że polimorfizm implikuje interpretację w fazie wykonywania.

Tom: Myślę, że tak jest, ale nikt nie atakow ał mnie za to twierdzenie, ponieważ nigdzie o nim nie dyskutowałem. Polimorfizm oznacza, że pisze się program, który zachowuje się inaczej w zależności od danych, na których działa. Jeśli dane te nie zostaną uwzględnione w kodzie źródłowym, w fazie wykonania ta część programu nie wie, co ma robić, dopóki nie zacznie się wykonywać. To jest właśnie interpretacja w fazie wykonywania. Czy źle to rozumiem?

BASIC

1 19

Weźmy pod uwagę Smalltalk, gdzie podobno są dostępne źródła. Czy bardzo późne wiązanie można zaliczyć jako wiązanie w fazie wykonywania?

Tom: To bardzo podchwytliwe pytanie. Istnieje wczesne wiązanie, późne wiązanie lub wiązanie w fazie wykonywania. To naprawdę trudne. Wyobrażam sobie, że można znaleźć sposoby obejścia tych mechanizmów. Załóżmy na przykład, że piszemy procedurę sortującą. W przypadku sortowania liczb porów nywanie, które liczby są mniejsze, jest oczywiste. W przypadku sortow ania ciągów znaków jest to już mniej oczywiste, ponieważ nie wiadomo, czy zastosować sortowanie ASCII, porządkowanie słownikowe, czy też jakiś inny rodzaj sortowania. Podczas pisania procedury sortującej w iadom o, czego się chce, dlatego właśnie w ten sposób realizuje się porównania. Jeśli piszemy procedurę sortującą ogólnego przeznaczenia, musim y wywołać procedurę lub w ykonać podobną czynność po to, by przekonać się, czy A jest mniejsze od B, cokolwiek to znaczy. Jeśli próbujemy sortować klucze rekordów lub wykonywać podobne operacje, musimy wiedzieć, jaki jest porządek naszego sortowania. Sortowane mogą być różne rzeczy. Czasami są to ciągi znaków, ale możliwości są różne. Pisząc algorytm sortow ania, tego nie wiemy, co oznacza, że trzeba to odłożyć na później. Oczywiście jeśli operacje są wykonywane w fazie wykonywania, jest to interpretacja fazy wykonywania. Można to zrobić wydajnie — stworzyć niewielki program, procedurę, w której będą zapisane reguły sortowania elementów. Ale ten sposób nie jest automatyczny. Polimorfizm nie jest za darmo. Trzeba napisać warianty polimorficzne.

Tom: Ktoś to musi zrobić. Inną rzeczą, o której m ówią zwolennicy program ow ania obiektowego, jest dziedziczenie. Jest ono ważne tylko wtedy, gdy w języku obowiązuje ścisła kontrola typów. Przeczytałem w prow adzenia do wielu książek na tem at program ow ania obiektowego. Podają tam przykład kogoś, kto napisał jakiś kod. Ktoś inny mógłby wykorzystać go do jakiegoś innego celu, ale zachodzi to niezwykle rzadko. Problemem, jaki zawsze widziałem w tego rodzaju rozwiązaniach, jest to, że jeśli napisze się kod na tyle uniwersalny, że ktoś mógłby go wykorzystać, trzeba stworzyć bardzo obszerną dokumentację i udostępnić ją razem z kodem. Jest bardzo wiele rzeczy do opisania. To prawie kompletna aplikacja razem z dokumentacją. Dla takich program ów, które ja piszę, to przesada. Nie wiem, jaka jest praktyka w branży. To zupełnie inna sprawa. Czy nazwałby pan ideę taniego i łatwego wielokrotnego wykorzystywania kodu przedwczesnym uogólnieniem?

Tom: To idea, która może mieć znaczenie w zawodzie program isty, ale nie ma znaczenia dla szerszej grupy am atorów , którzy m ogą mieć potrzebę pisania

120

ROZDZIAŁ

PIĄTY

programów. W rzeczywistości większość ludzi nie pisze dziś programów. Wiele działań, dla których dawniej trzeba było pisać programy, dziś jest wykonywanych przez gotowe aplikacje dostępne na rynku. Można wprowadzić dane do arkusza kalkulacyjnego lub uzyskać wyniki w inny sposób. Bardzo rzadko dziś się zdarza, aby specjaliści z innych dziedzin niż informatyka pisali programy. Jeśli chodzi o nauczanie programowania, zwłaszcza w szkołach średnich, w których są rozszerzone programy nauczania informatyki, martwi mnie to, że materiał jest zbyt złożony. Nie wiem, jakich języków dzisiaj uczą, nie przyglądałem się temu. Kiedyś tw orzyłem strukturę pierwszego kursu program ow ania w college’u z w ykorzystaniem BASIC-a. Na podstawowym kursie informatyki mogłem nauczyć praktycznie wszystkiego, co chciałem, poza posługiw aniem się wskaźnikami i przydzielaniem pamięci. Taka pow inna być złożoność kursu podstawowego. W przypadku użycia Pascala do nauczania programowania trzeba wejść w zagadnienia wskaźników i alokacji pamięci w czasie, kiedy wiele osób jeszcze nie wie, czym jest program komputerowy. Nie krytykuję jednak konkretnych osób. Nigdy nie forsowałem swojego punktu widzenia. Jestem sam przeciwko wielu. Niektórzy uważają, że nie trzeba zbytnio przejmować się alokowaną pamięcią i wskaźnikami, o ile nie pisze się maszyn wirtualnych. Muszą się nimi przejmować osoby piszące kompilatory, ale tym zajmują się profesjonaliści.

Tom: Niech robi to kompilator. Użytkownik języka nie musi tego robić. W języku True BASIC osiągnęliśmy przenośność. Kilkoro młodych ludzi, naprawdę błyskotliwych, opracowało projekt. W tym czasie ja zajmowałem się programowaniem aplikacyjnym. Zespół opracow ał język pośredni, podobny do kodu P z Pascala. Nie był on jednak dwuadresowy, ale trzyadresowy. Okazało się bowiem, że praktycznie wszystkie instrukcje w języku BASIC składają się z trzech adresów. Na przykład LET A = 3 — to trzy elementy: opkod oraz dwa adresy. Następnie stworzono kompilator przy użyciu samego BASIC-a. Był on bardzo prosty — tak aby m ożna było tylko skompilować kompilator. Sam kompilator jest napisany w języku True BASIC i działa na dowolnej maszynie, na której jest zainstalow any silnik języka True BASIC. Ten silnik my nazywamy interpreterem. Język jest interpretowany na poziomie wykonywania, a nie na poziomie skanowania. A zatem wykonywanie programu składa się z trzech faz. Pierwsza to faza kompilacji, druga — linkowanie (ładowanie) i trzecia — wykonywanie. Użytkownik jednak tego nie wie. Użytkownik pisze tylko run lub klika Start i wszystko dzieje się samo. Skompilowany kod jest również niezależny od sprzętu. Można go przenosić na dowolne platformy. Jest to naprawdę zaawansowane środowisko języka. Próbowaliśmy różnych platform — na razie czterech lub pięciu — ale oczywiście projekty dla większości platform żyły

BASIC

121

przez jakiś czas i umarły. Teraz pozostały już tylko dwie, a właściwie trzy główne platformy: Unix, Microsoft i Apple — ta ostatnia jest dla nas interesująca, ponieważ D artm outh był zawsze szkołą Apple. Przenoszenie środowiska na te platformy okazało się bardzo trudne. Obsługa okien, gadżetów, przycisków i wszystkich tego typu rzeczy jest inna na każdej platformie. W każdym przypadku trzeba było się z nią szczegółowo zapoznać. Czasami operacje te są w ykonyw ane na bardzo niskim poziomie, zatem trzeba wszystko tworzyć samodzielnie. Stary, oryginalny Mac miał narzędzie Mac Toolbox. Na razie użyliśmy oprogramowania warstwowego — XVT z firmy Boulder w Colorado, które pozwalało tworzyć programy dla systemu Windows oraz na klasyczny Mac OS. Dzięki wykorzystaniu tego narzędzia udało nam się uzyskać znaczący postęp. Zanim firma przestała funkcjonować, wydano wersję dla systemu Windows, która korzystała bezpośrednio ze środowiska aplikacji systemu Windows. Problem polega na tym, że realizacja tych wszystkich przedsięwzięć, kiedy ma się tylko jednego programistę, zajmuje czas. Pojawiają się nowe wersje systemu operacyjnego, odkrywa się nowe błędy, które trzeba usuwać. Dla takiego małego zespołu, jakim byliśmy, stało się to prawie niemożliwe do udźwignięcia. Najpierw mieliśmy trzech programistów, później dwóch i na końcu jednego. Dla jednego programisty to o wiele za dużo pracy. Kod, który teraz jest w większości napisany w C, zawiera wiele dyrektyw #i fdef.

Język i praktyki programistyczne Jakie je st powiązanie pomiędzy projektem języka a projektem oprogramowania napisanego w tym języku?

Tom: Bardzo ścisłe. Większość języków zaprojektowano z myślą o bardzo specyficznym typie oprogram ow ania. D obrym przykładem jest APT — język do zarządzania automatycznie programowanymi narzędziami. W początkowych latach BASIC-a dodał pan instrukcję REM do oznaczania komentarzy. Czy pańskie zdanie na temat komentarzy i dokumentacji oprogramowania zmieniło się z biegiem lat?

Tom: Nie. Komentarze to rodzaj mechanizmu samoobronnego. Kiedy piszę programy w języku True BASIC, dodaję komentarze po to, aby przypominały mi, co miałem na myśli, kiedy pisałem kod. Uważam, że komentarze odgrywają istotną rolę. Jest ona różna, w zależności od rodzaju oprogramowania, które piszemy, tego, czy pracujemy w zespole, czy nie, oraz tego, czy nasz kod czytają inni. Komentarze są ważne, ale tylko tam, gdzie są niezbędne.

1 22

ROZDZIAŁ

PIĄTY

Czy ma pan jakieś wskazówki dla osób programujących w zespołach?

Tom: Nie, ponieważ nigdy tego nie robiliśmy. Wszystkie programy, które napisaliśmy w naszym środowisku, powstawały w pojedynkę. W czasie pracy nad True BASIC kod pisały dwie lub trzy osoby, ale pracowały one nad całkowicie osobnymi projektami. Nie mam żadnego doświadczenia w pracy w zespołach. Korzystał pan z systemu z podziałem czasu, zatem zasugerował pan, aby użytkownicy zaplanowali swoje sesje przed dalekopisem, zanim przed nim usiądą. Motto brzmiało: wpisywanie nie zastępuje myślenia. Czy ta zasada obowiązuje także dziś?

Tom: Uważam, że myślenie odgrywa istotną rolę. Kiedy jakaś duża firma zamierza opracować nowy produkt programowy, jej pracownicy muszą wcześniej dokładnie go przemyśleć. Myślę więc, że to robią. Osobiście nie staram się myśleć zbyt dużo zawczasu, a po prostu zaczynam pisać program. Jeśli odkryję, że nie działa tak, jakbym chciał, to wyrzucam całość i zaczynam od początku. To odpowiednik myślenia. Zwykle zaczynam kodować po to, by zobaczyć, czego dotyczy problem, a następnie wyrzucam tę wersję. Myślenie o tym, co się robi, jest ważne — bardzo ważne. Wydaje mi się, że to Richard Hamming powiedział: „Wpisywanie nie zastępuje myślenia” . Były to bardzo wczesne lata używania komputerów i bardzo niewiele osób potrafiło się nimi posługiwać, zatem krążyło wiele rad takich jak ta. Jaki jest najlepszy sposób nauczenia się nowego języka programowania?

Tom: Jeśli ktoś wie, jak się programuje, i zna pojęcia (na przykład jak jest alokowana pamięć), to nauczenie się nowego języka jest proste. W ystarczy mieć dostęp do podręcznika i dobrej implementacji (tzn. kompilatora). Nowych języków uczyłem się wiele razy. Uczestnictwo w kursach nowych języków jest właściwie stratą czasu. Każdy dobry programista w swoim życiu zawodowym poznaje wiele języków (osobiście używałem ponad 20). Sposobem na naukę nowych języków jest czytanie dokumentacji. Większość języków programowania, z kilkoma wyjątkami, ma podobną strukturę i sposób działania. W związku z tym nowe języki są stosunkowo łatwe do nauczenia się, jeśli jest dostępna dobra dokumentacja. Kiedy już zrozumie się znaczenie używanego żargonu (co oznacza słowo polimorfizm?), reszta jest dość prosta. Jednym z problem ów dzisiejszego stylu program ow ania jest brak podręczników — są tylko narzędzia tworzenia interfejsów. Są one zaprojektowane w taki sposób, aby programiści nie musieli wpisywać litera po literze wielu instrukcji. Działają raczej jak inżynierskie narzędzia CAD i CAM. Dla programisty starej daty, takiego jak ja, to przekleństwo. Ja wolę wpisywać cały kod litera po literze.

BASIC

123

W przeszłości podejmowano próby uproszczenia wpisywania (dla osób słabo piszących na maszynie lub studentów). Udostępniano makra (na przykład prosta kombinacja klawiszy dla wpisania słowa kluczowego LET). Próby te jednak nigdy się nie powiodły. Obecnie próbuję nauczyć się języka, który podobno jest obiektowy. Nie ma żadnego podręcznika. Przynajmniej ja żadnego nie znalazłem. Dostępne podręczniki pokazują najbardziej trywialne przykłady, a 90% miejsca w nich poświęcone jest udowadnianiu, jaką wspaniałą religią jest programowanie obiektowe. Miałem przyjaciół, którzy brali udział w kursach języka C++. Kursy te były katastrofą z pedagogicznego punktu widzenia. W mojej opinii programowanie obiektowe jest jednym z większych oszustw, jakie opanowały społeczność. Wszystkie języki pierwotnie zaprojektowano z myślą o określonej klasie użytkowników — na przykład język FORTRAN zaprojektowano do wykonywania rozszerzonych obliczeń numerycznych. Programowanie obiektowe wymyślono po to, aby ci, którzy potrafią się nim posługiwać, mogli chełpić się tym, że są w kręgu w tajem niczonych. Prawda jest taka, że najważniejszym aspektem program ow ania obiektowego jest podejście wymyślone wiele dekad wcześniej: enkapsulacja procedur i danych. Cała reszta to lukier.

Projekt języka Czy uważa pan, że obecna wersja języka Visual Basic Microsoftu to kompletny język obiektowy? Jeśli tak, to czy podoba się panu ten aspekt języka (biorąc pod uwagę pana zdanie o programowaniu obiektowym)?

Tom: Nie wiem. Po kilku prostych eksperymentach wydaje mi się, że Visual Basic jest stosunkowo łatwy w użyciu. Wątpię, że ktokolwiek spoza firmy Microsoft powiedziałby o języku VB, że jest to język obiektowy. W istocie True BASIC jest w takim samym stopniu obiektowy jak Visual Basic, a może jeszcze bardziej. W języku True BASIC uwzględniono m oduły, które są kolekcją procedur i danych. M oduły dostarczają najważniejszej własności program ow ania obiektowego — enkapsulacji danych (w języku True BASIC nie ma typów dziedziczonych, ponieważ język ten nie posiada typów definiowanych przez użytkownika innych niż wymiary tablic; prawie żaden z języków nie udostępnia polimorfizmu, który w istocie implikuje interpretację w fazie wykonywania). Wspomniał pan, że Visual Basic w porównaniu z językiem True BASIC miał poważne ograniczenia. Czy uważa pan, że w języku Visual Basic brakowało czegoś w rodzaju systemu modułów?

Tom: Nie wiem. W Visual Basic napisałem tylko kilka przykładowych programów. Właściwie można powiedzieć, że niczego nie napisałem w tym języku. Przekonałem tylko samego siebie, że mógłbym to robić. Visual Basic jest wyposażony w bardzo prosty interfejs użytkownika. Pod tym względem język ten różni się od innych, których

124

ROZDZIAŁ

PIĄTY

próbow ałem używać wcześniej. Visual Basic pow stał na bazie starszego Microsoft BASIC-a. Firma Microsoft twierdziła, że był to język obiektowy, ale w rzeczywistości tak nie było. Dodanie do języka mechanizmów tworzenia interfejsu nie zrobiło z niego języka obiektowego. Powiedział pan interesującą rzecz na tem at niektórych większych systemów przetwarzania strumieni wideo i audio. Stwierdził pan, że łatw iej stworzyć zaawansowane aplikacje tego rodzaju za pomocą języka obiektowego, na przykład Objective-C.

Tom: Tak. Prawdopodobnie dlatego, że pozwala na to środowisko języka. Obecnie próbuję — bezskutecznie — nauczyć się języka Objective-C, ale tak czy owak, środowisko języka jest dostępne. Jeśli wie się, co się robi, można w rozsądny sposób uzyskać dostęp do wszystkiego, co jest na tej platform ie — dźwiękowego lub wizualnego. Nie próbowałem tego robić praktycznie, więc nie wiem, czy to trudne, ale jest dostępne w środowisku programowym języka. Niekoniecznie jest to cecha samego języka, ale jego środowiska.

Tom: W rzeczywistości nie ma to nic wspólnego z samym językiem, ale należy do środowiska. Jeśli środowisko języka jest używane przez wiele osób, to są setki programistów sprawdzających, czy działa ono prawidłowo. Jakie wnioski z lekcji na tem at powstania, dalszego rozwoju i przystosowania się pańskiego języka do warunków współczesnych mogą wyciągnąć programiści, którzy tworzą systemy komputerowe dziś oraz będą je tworzyć w najbliższej przyszłości?

Tom: Żadnych. Przypominam sobie komputer Burroughs 5500 z początku lat sześćdziesiątych. Jego sprzęt zaprojektowano w taki sposób, by umożliwić wydajniejsze działanie aplikacji bazujących na stosie typu push-down — na przykład kompilatorów Algola. Dziś obowiązuje raczej inny trend — w kierunku maszyn RISC. Dla większości języków programowania nie potrzeba niczego specjalnego. Częściowo wynika to z tego, że komputery są bardzo szybkie i stają się coraz szybsze. W związku z tym czas kompilacji i przetwarzania określonego języka nie stanowi problemu. Twierdzenie odw rotne także może być prawdziwe. Na przykład do przetwarzania dużych tablic można stworzyć specjalny komputer, dla którego później trzeba będzie opracować odpowiedni język programowania.

BASIC

1 25

Gdyby dzisiaj musiał pan stworzyć całkowicie nowy język programowania w celach edukacyjnych, to w jakim stopniu przypominałby on BASIC?

Tom: Bardzo by go przypominał, ponieważ zasady, zgodnie z którymi postępowaliśmy, są w dalszym ciągu aktualne. Na przykład staraliśmy się stworzyć język, który będzie łatwo zapamiętać. Chcieliśmy, aby ktoś, kto nie używał języka przez długi czas, mógł szybko przypomnieć sobie, jak się z niego korzysta. Próbowaliśmy stworzyć język, który m a m inim um ezoterycznych wymagań. Jeśli ktoś chciał na przykład wyświetlić liczbę w FORTRAN-ie, m usiał użyć instrukcji formatującej i dokładnie wskazać sposób wyświetlania. Dla ludzi uczących się języka jest to ezoteryczne wymaganie, zwłaszcza jeśli nie używają oni języka zbyt często. W związku z tym w BASIC-u wyświetlaliśmy liczbę w taki sposób, jaki w naszej opinii jest najlepszym sposobem wyświetlania. Jeśli była to liczba całkowita — mimo że wewnętrznie wykorzystywaliśmy liczby zmiennoprzecinkowe — wyświetlaliśmy jak liczbę całkowitą, czyli bez kropki dziesiętnej. Jeśli ktoś chciał wpisać liczbę, nie m usiał m artw ić się form atem . W ystarczyło, że wpisał liczbę w wybranej przez siebie postaci — nie istniały ograniczenia związane z pewnym konkretnym form atem wprowadzania danych. W większości przypadków tak właśnie jest w arkuszach kalkulacyjnych. Pod tym względem są to dobre narzędzia. Można określić, że chcemy wyświetlać liczby o stałej pozycji kropki dziesiętnej dla wybranej kolumny, a jeśli jest nam wszystko jedno, możemy skorzystać z ogólnego formatu liczb. Myślę, że mniej więcej to samo zrobiliśmy w BASIC-u. Pozwoliliśmy na wprowadzanie znaków i zrobiliśmy to w bardzo prosty sposób: po prostu wystarczy wpisać liczbę, nie trzeba przestrzegać żadnych reguł. W latach siedemdziesiątych i osiemdziesiątych opracowaliśm y w D artm outh strukturalną wersję BASIC-a i dodaliśmy elementy dostarczające mechanizmów programowania obiektowego. Nie mogliśmy zrobić tego inaczej. Czy w programowaniu obiektowym najbardziej podoba się panu enkapsulacja?

Tom: Tak, to prawda. Zwykłem mówić, że enkapsulacja dostarcza 70% korzyści wynikających z zastosow ania program ow ania obiektowego. Teraz jednak myślę, że to 90%. Główna własność to scalenie procedur z danymi, na których one operują. To bardzo ważne. Nie piszę już teraz zbyt wiele, ale we wszystkim, co pisałem — na przykład podczas pracy nad językiem True BASIC — stosowałem enkapsulację. Stosowaliśmy sposób enkapsulacji grup procedur w czymś, co nazywaliśmy modułami. Po pogrupowaniu procedur są one izolowane od reszty programu. Dostęp do nich istnieje tylko poprzez w yw ołania. W rzeczywistości procedury te posługują się pryw atnym i danymi. Było to bardzo przydatne do izolacji zestawu funkcji.

1 26

ROZDZIAŁ

PIĄTY

Wielu projektantów języków programowania i systemów pytałem o to, do jakiego stopnia podobał im się matematyczny formalizm. Weźmy na przykład język Scheme, który pozwalał na bardzo wydajne zaprezentowanie rachunku lambda. Wystarczyło sześć prymitywów. Resztę stworzono na ich bazie. Wydaje się, że to jest podejście matematyczne.

Tom: Zgadza się. To bardzo ciekawe. To interesujący problem m atem atyczny, ale kiedy tworzy się język komputerowy, nie trzeba robić czegoś takiego, ponieważ każdy język komputerowy, z jakim się dotychczas zetknąłem, był znacznie prostszy. Nawet FORTRAN. Algol jest prosty. W ykorzystuje rekurencyjne definicje. Są one jednak stosunkowo proste i oczywiste. Nigdy nie studiowałem teorii języków programowania, dlatego nie mogę formułować żadnych uwag na ten temat. Czy bierze pan pod uwagę ludzi, którzy będą używali języka, oraz największe problemy, jakie będą oni rozwiązywać?

Tom: Tak. Największym problemem ludzi, dla których projektowaliśmy język, było zapam iętanie języka z tygodnia na tydzień, ale oni pisali jeden program co dwa tygodnie. Chcieliśmy również stworzyć język programowania i środowisko systemowe, którego można by nauczyć w ciągu kilku godzin, tak aby poznanie języka nie wymagało uczestniczenia w kursie. Właśnie w taki sposób ja i wielu moich kolegów uczyliśmy się programowania. Mieliśmy Microsoft BASIC na komputerach PC we wczesnych latach osiemdziesiątych, a także na komputerach Commodore 64 i Apple II. Były to wierszowe interpretery BASIC-a z możliwością tworzenia procedur, ale nic poza tym.

Tom: Krążą na ten temat dziwne opinie. Interpretery te zawierały dziwne właściwości. Na przykład ja używałem języka Apple Soft BASIC. Nie wiem, czy stworzyła go firma Microsoft, czy jakaś inna, ale wszystko, co w nim było, skopiow ano z projektu BASIC-a w Dartmouth. Wprowadzono pojęcie nazwy zmiennej składającej się z wielu znaków, ale parser nie analizow ał jej praw idłow o. Jeśli w wieloznakowej nazwie zmiennej było słowo kluczowe, program nie działał. Czy wynikało to z braku wrażliwości na spacje?

Tom: Nie. Twierdzono, że istnieje obsługa nazw zmiennych składających się z wielu znaków, a tak nie było. Jeśli ktoś chciał zdefiniować w ieloznakow ą zmienną, na przykład TOT, to interpreter rozpoznaw ał TO jako słowo kluczowe. Był to tylko chwyt marketingowy. Implementacje te tworzono z myślą o rynku. Sądzono, że obsługa nazw zm iennych składających się z wielu znaków będzie dobrym chwytem marketingowym. Użytkownikom języka udawało się obchodzić niedoskonałe działanie tego mechanizmu poprzez unikanie zbyt częstego wykorzystywania nazw zmiennych składających się z wielu znaków.

BASIC

127

0 tym problemie nie można się było dowiedzieć na podstawie lektury podręcznika.

Tom: Nie. Nie było w nim nic o błędach. Co było powodem wprowadzenia w języku braku wrażliwości na spacje?

Tom: Jedyne, co wiem na ten temat, zostało opublikowane. W języku wprowadzono własność braku wrażliwości na spacje po części dlatego, że John Kemeny słabo pisał na maszynie. Nie pamiętam dokładnie, czy to był prawdziwy powód. My kodowaliśmy język, ale własnościami takimi jak ta zajmował się Kemeny. Ponieważ nazwy zmiennych były unikatowe i nie wyglądały jak słowa kluczowe, spacje nie były potrzebne. Można dodawać spacje lub je usuwać, w miarę potrzeb. W szystko się rozwija. Dziś również są wersje języka True BASIC, które działają na różnych maszynach. To jedyny język, jakiego używam. Dla poprawy czytelności kodu używam spacji. Nie robi pan tego w celu obchodzenia ograniczeń komputera. Czy uważa pan, że w tym problemie rolę odgrywa wyłącznie czynnik ludzki?

Tom: Zgadza się, ponieważ głów ną własnością program u (o ile jest to poważny program, z którym chcemy pracować) jest pewność, że jeśli odłożymy go na sześć miesięcy, a po tym czasie sięgniemy do niego z powrotem, to czytanie go wystarczy, by udało nam się go zrozumieć. Jest bardzo ważne, aby wybierać nazwy zmiennych sugerujące wielkości, które one reprezentują, lub by stworzyć ich strukturę. Głównie m am tu na myśli używanie procedur służących do realizacji pojedynczych funkcji 1 nadawanie im nazw sugerujących, do czego one służą. Wszystkie współczesne języki komputerowe obsługują nazwy zmiennych dowolnej długości. Dzięki temu można w nazwie procedury umieścić informację o tym, co ona robi, naw et jeśli potrzeba do tego aż 20 liter. Dzięki tem u można sięgnąć do kodu kilka miesięcy później i go zrozumieć. Chuck Moore powiedział, że w języku Forth tworzy się słowniki. Dzięki temu, jeśli odpowiednio dobierze się słowa, można pisać programy przy użyciu języka określonej dziedziny. Interesujące, że ten pomysł po jaw ił się tak wiele razy.

Tom: Taki język kom puterow y jak BASIC jest stworzony dla osób, które nie są profesjonalnym i program istam i. Jeśli ktoś jest specjalistą w jakiejś dziedzinie i zdecyduje się na napisanie aplikacji, woli używać prostszego języka programowania niż zawodowy programista. Mam tu na myśli wszystkie te języki obiektowe, z którymi można się dziś spotkać. Mam pewne doświadczenia z jednym z takich języków. Dla mnie jest on groteskowo skomplikowany. To jedyny język komputerowy w moim życiu, którego nie udało mi się poznać, a pisałem programy chyba w 30 językach.

1 28

ROZDZIAŁ

PIĄTY

W ja k i sposób technologia WYSIWYG może wyeliminować potrzebę numerowania wierszy? Powiedział pan, że spełniały one rolę wskaźników lokalizacji docelowych dla instrukcji GOTO. Czy numery wierszy są potrzebne także po to, aby programiści mogli odwoływać się do wierszy podczas edycji pliku?

Tom: Absolutnie nie (to odpowiedź na to drugie pytanie). Edycja z numerami wierszy już dawno przeszła do lamusa. W ja k i sposób technologia WYSIWYG zmieniła programowanie?

Tom: Wcale go nie zmieniła. Edytory WYSIWYG są zaawansowane i ściśle związane z językiem, dla którego się ich używa (wcięcia, kolory itp.). Czyjest coś, co pomija się w dzisiejszym nauczaniu informatyki? Na przykład niektórzy twierdzą, że nie ma podejścia inżynierskiego.

Tom: Nie wiem, ponieważ nie mam pojęcia, jak dziś uczą informatyki. Odszedłem na emeryturę 15 lat tem u i nie uczyłem niczego oprócz statystyki i informatyki. W związku z tym nie mam pojęcia, jak rozwinęła się ta dziedzina od tamtego czasu. W ja k i sposób powinno się uczyć debugowania?

Tom: Najlepsze, co m ożna zrobić, to unikanie jego konieczności. Aby uniknąć debugowania, trzeba lepiej przemyśleć projekt. Jeden z naszych byłych studentów pracował dla Apple. Zanim przeszedł na emeryturę, w ykonywał dla nich ważne prace. Wcześniej pracow ał w D artm outh, w centrum obliczeniowym. Napisał kom pilator PL/1 — bardzo złożony program. Sprawdzał go, analizował, ale nigdy go nie testował i nigdy nie uruchomił, zanim nie skończył pisania. Proszę sobie wyobrazić 20 000, a może 30 000 wierszy kodu oraz że jedyny test to czytanie kodu. Następnie uruchomił kod. Zaczął działać za pierwszym razem. To ewenement na skalę całej historii komputerów! Trudno sobie wyobrazić, że ktoś pisze program składający się z 20 000 lub 30 000 wierszy, który działa poprawnie za pierwszym razem. Nieprawdaż? Ale on to zrobił i przez to pokazał sposób, w jaki powinno się pisać programy. Pracował w pojedynkę, nie pracował w zespole. Kiedy pracuje się samodzielnie, zawsze jest się bardziej wydajnym. Uważnie sprawdzał, w jaki sposób współpracują ze sobą różne części programu i bardzo uważnie czytał kod. Kiedy czytamy kod, w rzeczywistości em ulujem y to, co m a robić kom puter. Sprawdzamy każdy krok — to działa prawidłowo, to prawidłowo itd. Zatem kiedy w cisnął przycisk Start i okazało się, że wszystko działa, to było szaleństwo. Nikomu się to nie udaje, ale taka jest idea. Liczbę błędów redukujemy, nie popełniając ich. Dużo programów komercyjnych będących dziś w użyciu zawiera mnóstwo błędów dlatego, że nie są pisane przez dobrych programistów. Są pisane przez zespoły, a projekt

BASIC

1 29

ich funkcjonalności jest w ymuszany przez działy marketingu. Program musi być opublikow any w określonym czasie razem z innym program em i musi oferować określone możliwości. Z tego powodu zawiera on mnóstwo błędów. Punkt widzenia firm programistycznych jest taki, że większość użytkowników używa podstawowych funkcji komputerów. Dlatego też nie napotykają oni zbyt wielu błędów. Czy potrafiłby pan narysować linię rozgraniczającą to, co powinno się znaleźć w języku, od tego, co powinno trafić do biblioteki?

Tom: Na to również zwracaliśmy baczną uwagę. Wszystko, co było trochę ezoteryczne, interesujące tylko dla pewnej grupy użytkowników, umieszczaliśmy w bibliotece. W taki sposób rozwijał się język na przestrzeni lat. Stworzyliśmy bibliotekę dla wielu operacji. W nowoczesnej wersji BASIC-a — języku True BASIC — wykorzystujemy bibliotekę procedur dających dostęp do obiektów rodem z program ow ania obiektowego, takich jak przyciski, okna dialogowe i tego rodzaju rzeczy. Zastosowaliśmy do tego procedury biblioteczne. Dostęp do takich własności nie został włączony do samego języka. Trzeba zatem wywołać odpowiednią procedurę. Procedury są zapisane w jednej z wielu bibliotek. Myśleliśmy o tym bardzo dużo od samego początku. Kiedy oprogramowanie jest pisane w zespołach, często tworzy się wspólne biblioteki używane przez wszystkich członków zespołu. Czy ma pan jakieś wskazówki dotyczące tworzenia takich bibliotek w języku True BASIC?

Tom: Biblioteki pisałem sam, ale nie potrafię przekazać żadnej konkretnej wskazówki poza tym, aby starać się zachować prostotę. Są to techniki, które znają wszyscy: trzeba dążyć do zachowania prostoty i unikać popełniania błędów. Na przykład ważne są procedury spełniające jeden konkretny cel. Nie należy tworzyć procedur, które robią coś przy okazji — tylko z pozoru wydaje się to dobrym pomysłem. Skutki uboczne mogą być katastrofalne. Istnieją tysiące sposobów pisania bibliotek, które redukują bądź elim inują przyszłe błędy. Istnieją dobrze znane techniki zmniejszania liczby błędów, ale nie wiem, w jakim stopniu są one stosowane w branży. Programy pisane w branży są podyktowane w dużej części przez działy marketingu.

Cele pracy W ja k i sposób definiuje pan sukces w swojej pracy?

Tom: Przez wiele lat, z powodu naszej pracy, dlatego że byliśmy tacy otwarci, dlatego że daliśmy studentom nieograniczony dostęp do projektu — college D artm outh był jednym z ośrodków komputerowych o najlepszej na świecie reputacji. Odwiedzały nas osoby z Rosji, Japonii i innych miejsc, tylko po to, by zobaczyć, jak pracujemy. Było to jeszcze w czasach, kiedy komputery osobiste nie były powszechne. Nie każdy miał kom puter osobisty. Dziś już nikogo by to nie dziwiło, ale w tamtych czasach

130

ROZDZIAŁ

PIĄTY

bardzo interesujące było to, że pozwalaliśmy studentom , wszystkim studentom , na korzystanie z komputerów wtedy, kiedy chcieli, i bez konieczności uzyskania zgody. W tam tych czasach to była nowość. To dało college’owi D artm outh renom ę na 10 lub 15 lat. Dzięki niej otrzymywaliśmy większe fundusze, przyciągaliśmy studentów i kadrę dydaktyczną. Innym sukcesem było to, że wielu studentom, którzy przyszli do Dartmouth i nauczyli się używania komputerów, udało się zrobić wielkie kariery tylko dlatego, że znali swój zawód. Nie byli to ludzie z centrów komputerowych korporacji. Ludzie ci pracowali w innych działach. Jest całkiem sporo studentów D artm outh, którzy stali się m ilioneram i tylko dlatego, że wiedzieli, jak używać komputera! To jest główny miernik naszego sukcesu. Czego powinni nauczyć się młodzi ludzie z pańskiego doświadczenia?

Tom: Powinni pamiętać o tym, kto będzie ostatecznym użytkownikiem tworzonych przez nich programów. Obsługa wielu aplikacji, z których dziś korzystamy, jest bardzo trudna. W iem, że kilka lat tem u ludzie z firmy M icrosoft próbow ali w prowadzić pojęcie przyjazności dla użytkownika za pośrednictwem programu o nazwie Bob czy jakoś tak. Myślę jednak, że źle zrozum iano ideę. Pomyślano, że program przyjazny to program opiekuńczy — taki, który przem awia do użytkownika, tak jak mówi się do dziecka. W edług mnie taki program nie jest przyjazny. O bawiam się, że w branży powszechnie źle interpretuje się pojęcie program u przyjaznego użytkownikowi. Nie wiem, czy ludzie zajmujący się dziś informatyką w ogóle wiedzą, co oznaczają te słowa. Moja rada jest taka, aby pamiętać o ludziach, którzy będą korzystać z naszych programów. Czy należy tworzyć interfejs, który je st ła tw y do nauki, tak ja k ła tw y do nauki był BASIC?

Tom: Tak. Łatwy do nauczenia się, prosty do opisywania w podręcznikach oraz taki, który w mniejszym bądź większym stopniu robi to, czego można od niego oczekiwać, tak by nie było niespodzianek. Powiedział pan, że programista pracujący w pojedynkę jest bardziej wydajny. Chciałbym się dowiedzieć, co pan rozumie przez wydajność?

Tom: Rozumiem ją bardzo prosto. Myślę, że istnieje wiele dowodów na moją tezę. Nigdy w m oim życiu nie pracowałem w zespole program istów. Zawsze mówiłem sobie: „OK, to trzeba zrobić, napiszę do tego program ” . Wszystko, co napisałem, zdołałem napisać w pojedynkę.

BASIC

131

Korzystałem z kodu napisanego przez innych, ale nigdy nie należałem do żadnego zespołu programistów. Nie wiem, ile dokładnie wierszy kodu napisałem, ale jest kilka programów, których ciągle używam, a które mają 10 000 wierszy. Ich napisanie nie było trudne. Ich debugowanie również jest proste. Jeśli jakaś funkcja się komuś nie spodoba, może ją łatwo zmienić, a przy pracy w pojedynkę nie trzeba pisać listów, by to osiągnąć. Nie mam nic przeciwko tworzeniu dokumentacji programów. Większość moich programów nie ma jednak dokumentacji, ponieważ są to programy do mojego własnego użytku. Wierzę jednak we wszystko, co Fred Brooks napisał o programowaniu w swojej książce The Mythical Man-Month. Podoba mi się to, co powiedział na tem at szacowania czasu, jaki zajmie napisanie programu. Programiści piszą średnio po trzy wiersze dokumentowanego kodu dziennie. W końcu kierownictwo dochodzi do wniosku, że tworzenie aplikacji zajmuje zbyt dużo czasu. Zastanawiają się, co w ich pracy jest nie tak. Ustalają, że przyczyna tkwi w tym, że programiści pracują tylko 20 godzin tygodniowo. Są w zakładzie przez 40 godzin, ale 20 godzin spędzają na bezproduktywnym siedzeniu na naradach. Tego właśnie najbardziej nie lubię: narad. Czasami są one absolutnie konieczne. Pamiętam, kiedy była tworzona pierwsza wersja BASIC-a dla komputerów GE225, GE235. Studenci, którzy byli program istam i systemu, spotykali się co tydzień na naradzie trwającej około godziny. Spotkaniu przewodniczył John Kemeny. Kiedy mówił, podejm ow ał wszystkie nieważne decyzje — na przykład jakie zadanie ma priorytet w harmonogramie. Natomiast wszystkie ważne decyzje — na przykład jakie ma być przeznaczenie wybranego bitu — podejmowali studenci. W tam tych czasach korzystaliśmy ze środowiska złożonego z dwóch maszyn. Przy każdej maszynie był jeden student. Byli to studenci drugiego roku. Musieli pracować razem, musieli się komunikować. Ale ogólnie rzecz biorąc, każdy z nich wykonywał własną pracę. Zawsze, kiedy pisaliśmy kom pilator lub edytor, było to zadanie dla pojedynczej osoby. Powiedział pan to w kontekście tej historii o studencie, autorze kompilatora PL/1, który zaczął działać przy pierwszym uruchomieniu.

Tom: To był Phil Koch. Człowiek z firmy Apple. Obecnie jest już na emeryturze i mieszka w Maine. To był niezwykły program ista. Czytał kod bardzo długo jak świętą księgę. Gdyby miał pan wskazać jedną lekcję, jakiej warto by się nauczyć z pańskiego olbrzymiego, wieloletniego doświadczenia, co by to było?

Tom: Starajcie się, aby używanie waszych programów było łatwe dla użytkowników. Można by powiedzieć: twórzcie oprogramowanie przyjazne dla użytkowników. Według mnie jednak branża zmieniła znaczenie term inu „przyjazny” na „protekcjonalny” . Prawdziwy problem przyjazności dla użytkow ników polega na zdefiniow aniu

1 32

ROZDZIAŁ

PIĄTY

sensownych wartości dom yślnych w tworzonej aplikacji. Dzięki tem u osoba, dla której program jest nowością, nie musi zapoznawać się ze wszystkimi wariantami i możliwymi stopniami swobody. Użytkownik powinien móc usiąść i zacząć korzystać z program u. Należy również zadbać o to, aby w ykonanie jakiejś innej operacji za pomocą programu było stosunkowo łatwe. Aby można było to uzyskać, trzeba mieć pogląd na to, jak będzie wyglądała baza użytkowników. Często korzystałem z Microsoft Word, ale wedle moich standardów aplikacja ta w ogóle nie jest przyjazna użytkownikowi. Następnie, około 10 lat temu, firma Microsoft wpadła na pomysł programu Bob. Moim zdaniem był to zły pomysł. Źle zrozumiano, co oznacza przyjazny dla użytkownika. Uważam, że niektóre aplikacje są przyjazne użytkownikom. Wielką sprawą jest dziś projektowanie aplikacji internetowych. Ludzie, którzy tworzą witryny WWW, czasami wykonują doskonałą robotę, ale nie zawsze tak jest. Jeśli po wejściu na stronę WWW nie wiadomo, co zrobić, aby uzyskać więcej informacji, jej projekt jest do niczego. Nie jest łatwo nauczyć się dobrego stylu projektowania. Ben Shneiderm an, specjalista w dziedzinie czynnika ludzkiego w inform atyce z Uniwersytetu w M aryland, przeprow adził pewne b ad an ia1, które sugerowały, że struktury BASIC-a dla instrukcji DO, LOOP oraz IF były dla użytkowników bardziej przyjazne niż niektóre inne struktury, używane w innych językach — na przykład średnik w Algolu lub Pascalu kończący instrukcję. Ludzie zwykle nie używają średników do kończenia zdań, zatem jest to coś, czego trzeba się specjalnie nauczyć. Pamiętam na przykład, że w FORTRAN-ie były miejsca, w których był potrzebny przecinek, i inne miejsca, w których nie był on potrzebny. W efekcie wystąpił błąd w programie używanym w stacji kosmicznej na Florydzie. W jego wyniku uszkodzeniu uległa rakieta — tylko dlatego, że brakowało przecinka. 0 ile pamiętam, Ed Tufte udokumentował ten fakt. Należy unikać rzeczy, które mogą być niejednoznaczne. Zawsze powtarzam to wszystkim: Kemeny’emu i mnie się nie powiodło, ponieważ nie udało nam się doprowadzić do tego, aby komputery innych osób stały się przyjazne użytkownikom. Wykonaliśmy jednak dobrą robotę dla naszych studentów — przez ponad 20 lat nasi studenci opuszczali mury uczelni i otrzymywali lukratywne posady w branży dlatego, że wiedzieli, co robić. Dobrze jest odnieść taki sukces.

Tom: Jeśli ktoś jest nauczycielem, to jest właściwie najważniejsze.

1 Shneiderman B., W hen children learn programming: Antecedents, concepts, and outcomes, The Computing Teacher, tom 5: 1 4 - 17 (1985).

BASIC

133

134

RO ZDZIAŁ

PIĄTY

ROZDZIAŁ

SZÓSTY

AWK

Filozofię Uniksa — tworzenia wielu niewielkich narzędzi, które jeśli zostaną wykorzystane razem, dają duże możliwości — można doskonale zaobserwować w języku programowania AWK. Jego twórcy (Al Aho, Peter Weinberger i Brian Kernighan) opisali AWK jako składniowy język dopasowywania wzorców. Jego prosta składnia oraz inteligentny dobór przydatnych własności pozwalają na przetwarzanie tekstu za pomocą jednolinijkowych programów, bez konieczności rozumienia parserów, gram atyki i autom atów skończonych. Język AWK dał inspirację do uwzględnienia podobnych własności w językach ogólnego przeznaczenia, takich jak Perl. Pomimo to w dalszym ciągu jest zainstalowany w każdym nowoczesnym komputerze uniksowym, gdzie cicho i wydajnie wykonuje swoją pracę.

1 35

Życie algorytmów W ja k i sposób zdefiniowałby pan język AWK?

Al Aho: Powiedziałbym, że AWK jest łatwym do nauki i łatwym do używania językiem skryptowym, który koncentruje się na w ykonyw aniu rutynow ych aplikacji przetw arzania danych. Jaka była pańska rola w projektowaniu języka AWK?

Al: W latach siedemdziesiątych prowadziłem badania na temat wydajnych algorytmów parsowania i dopasowywania wzorców. Brian Kernighan i ja rozmawialiśmy na temat uogólnienia narzędzia grep w taki sposób, by m ożna je było wykorzystać do wykonywania ogólnych zadań dopasowywania wzorców i przetwarzania tekstu w wielu aplikacjach przetwarzania danych. Potem dołączył do nas Peter Weinberger, który wyraził wielkie zainteresow anie projektem . Dzięki tem u w 1977 roku zaimplementowaliśmy pierwszą wersję języka AWK. W ciągu kilku następnych lat język znacznie się rozwinął, ponieważ wielu naszych kolegów zaczęło go używać do wykonywania różnych zadań przetwarzania danych. Wielu z tych zastosowań nawet nie przewidywaliśmy. Do jakich zastosowań nadaje się język AWK?

Al: Myślę, że AWK w dalszym ciągu jest nie do pobicia w prostych aplikacjach do przetwarzania danych. W naszej książce na temat AWK są dziesiątki praktycznych przykładów, w których program AWK złożony z jednego lub dwóch wierszy kodu może wykonać działania, których zaimplementowanie wymagałoby dziesiątek lub setek wierszy kodu w C lub Javie. 0 czym powinni pamiętać ludzie projektujący programy napisane w AWK?

Al: AWK jest językiem skryptowym opracowanym w celu pisania krótkich programów wykorzystywanych do popularnych zadań przetwarzania danych. Nie planowaliśmy, że będzie on używany do programowania dużych aplikacji. Często jednak spotykaliśmy ludzi, którzy wykorzystywali go w takim celu, ponieważ język był łatwy w użyciu. Dla dużych aplikacji poleciłbym sprawdzone praktyki inżynierii oprogramowania: dobrą modularyzację, dobre nazwy zm iennych, dobre kom entarze itp. Praktyki te sprawdzają się także w przypadku krótkich programów. W ja k i sposób dostępność zasobów sprzętowych wpływa na sposób myślenia programistów?

Al: Z całą pewnością jest prawdą, że szybki sprzęt, dużo pamięci i dobre środowiska programistyczne spowodowały, że programowanie stało się o wiele bardziej przyjemne. Poza tym program y m ożna stosować do znacznie większych zbiorów danych niż

1 36

ROZDZIAŁ

SZÓSTY

kiedykolwiek w przeszłości. Obecnie często uruchamiam programy AWK dla danych wejściowych o kilka rzędów wielkości większych niż kiedyś. Tak więc szybki sprzęt spowodował, że jestem bardziej wydajnym użytkownikiem. Jest jednak cena, jaką trzeba zapłacić: uspraw nienia w sprzęcie doprow adziły do eksplozji rozmiaru i złożoności systemów oprogramowania. Programy stały się bardziej użyteczne w miarę postępu w sprzęcie, ale stały się także bardziej złożone — nie wiem, która strona bierze górę. W ja k i sposób podczas pracy nad algorytmami działającymi w języku AWK szacował pan rozmiar danych, z jakim i będzie działał pański kod?

Al: Wszędzie, gdzie było to możliwe, implementowaliśmy algorytmy, które były liniowe w czasie — w najgorszym przypadku lub w średnim przypadku. W ten sposób można było skalować AWK, tak by możliwa stała się obsługa coraz obszerniejszych danych wejściowych za jego pomocą. Testowaliśmy AWK na zbiorach danych różnych rozm iarów w celu sprawdzenia, jak zmieni się wydajność w miarę wzrostu rozmiaru danych wejściowych. Staraliśmy się, aby nasze implementacje były jak najwydajniejsze. Do testowania efektów naszych działań używaliśmy rzeczywistych danych testowych. Czy przewidział pan, ja k bardzo wzrośnie rozmiar danych w przyszłości?

Al: Kiedy projektowaliśmy AWK, uważałem plik o rozmiarze 1 megabajta za olbrzymi. Jeśli weźmiemy pod uwagę eksabajty danych dostępne dziś w internecie, to trzeba przyznać, że byliśmy wiele rzędów wielkości z tyłu w stosunku do tego, co dziś uważam y za duży zbiór danych. Oczywiście do skanow ania terabajtów danych naw et algorytm zależny liniowo od czasu jest o wiele za wolny. W związku z tym do przetwarzania danych z internetu potrzebne jest całkowicie nowe podejście. Słyszałem określenie języka AWK jako „języka dopasowywania wzorców do prostych zadań przetwarzania danych". Język AWK stworzono ponad 30 lat temu. Co się zmieniło od tamtego czasu w dziedzinie dopasowywania wzorców?

Al: Przez ostatnie 30 lat niezwykle rozwinęły się skala i różnorodność dopasowywania wzorców. Parametry problem ów znacznie się rozszerzyły, wzorce stały się bardziej złożone, a rozm iar zbiorów danych znacznie się powiększył. Obecnie rutynow o używamy wyszukiwarek do wyszukiwania wzorców tekstowych na wszystkich stronach WWW dostępnych w internecie. Jesteśmy również zainteresowani w ydobyw aniem danych (ang. data mining) — poszukiwaniem różnego rodzaju wzorców w obszernych bibliotekach cyfrowych — na przykład bazach danych o genomach oraz archiwach naukowych. Można śmiało powiedzieć, że dopasowywanie wzorców tekstowych jest jedną z najbardziej podstawowych aplikacji w informatyce.

AWK

137

Czy dzisiaj istnieją lepsze algorytmy dopasowywania wzorców oraz ich implementacje?

Al: Dopasowywanie wzorców w języku AWK zrealizowano z w ykorzystaniem szybkiego, kom paktow ego algorytm u bazującego na diagramie zm ian stanów. Jego działanie polegało na tw orzeniu na podstaw ie wyrażenia regularnego przejść determ inistycznego autom atu skończonego. Te przejścia były potrzebne do dopasowywania wzorców. Algorytm udokumentowano w tzw. Księdze Czerwonego Smoka (ang. Red Dragon book) \ Czas działania algorytmu, ogólnie rzecz biorąc, zależy liniowo od długości wyrażenia regularnego oraz rozm iaru tekstu wejściowego. Jest to najlepszy znany oczekiwany czas w ykonania dla wyrażeń regularnych. Mogliśmy zaim plem entow ać algorytm Boyera-Moore’a lub Aho-Corasick dla specjalnych przypadków, kiedy wyrażenie regularne jest pojedynczym słowem kluczowym lub skończonym zestawem słów kluczowych. Nie zrobiliśmy tego, ponieważ nie znaliśmy charakterystyki wyrażeń regularnych, których użytkownicy będą używali w programach pisanych w AWK. W arto zwrócić uwagę, że istnieje ciemna strona używania złożonych algorytmów w systemach programowych. Z tego powodu algorytmy mogą być niezrozumiałe dla innych (a nawet dla samego autora, jeśli minie wystarczająco długi czas). W języku AWK uwzględniłem zaawansowaną technologię dopasowywania wzorców. Chociaż jest ona udokum entow ana w Księdze Czerwonego Smoka, kiedyś Brian Kernighan rzucił okiem na moduł dopasowywania wzorców, który napisałem, i jedyne, co dodał, to komentarz po łacinie, który brzmiał mniej więcej tak: „Porzućcie wszelką nadzieję, wy, którzy tu w chodzicie”2. W konsekwencji ani Kernighan, ani W einberger nie dotykali tej części kodu. W tym m odule to ja m usiałem zawsze popraw iać wszystkie błędy!

Projekt języka Czy ma pan jakieś wskazówki dla projektantów języków programowania?

Al: Należy zawsze pam iętać o użytkow nikach. Jeśli ktoś powie, że użył naszego narzędzia do rozwiązania problemu, jest to bardzo satysfakcjonujące. Wielką satysfakcję daje również to, że ktoś wykorzysta naszą pracę do stworzenia bardziej zaaw ansow anych narzędzi.

1 Aho A.V. et al., Kompilatory. Reguły, metody i narzędzia, WNT, 2002. 2

1 38

Lasciate ogne speranza, voi ch’intrate — to napis na bramie piekielnej w komedii Dantego Alighieri.

ROZDZIAŁ

SZÓSTY

III

pieśni Boskiej

Co o projekcie języka myśleli Kernighan i Weinberger?

Al: Gdybym miał dobrać słowa opisujące nasze scentralizowane siły w projektowaniu języka, to powiedziałbym , że Kernighan kładł nacisk na łatwość nauki języka, Weinberger na sensowność implementacji, a ja na dostępność narzędzi. Myślę, że AWK posiada wszystkie te cechy. W ja k i sposób podejmuje pan decyzje projektowe w odniesieniu do narzędzi? Jaki ma to wpływ na sposób, w ja k i myśli pan o projekcie?

Al: Nie wiem, czy jest to świadome, czy nieświadome, ale z całą pewnością rzeczy, które przeżywają, są przydatne. Taki pogląd wzmacnia teorię Darwina. Tworzymy pojęcia i słowa, które są przydatne do rozwiązywania problemów będących w kręgu naszego zainteresowania, ale jeśli nie sprawdzą się one w rozwiązywaniu problemów, którymi są zainteresowani inni, pojęcia te znikną. Na tym polega szkoła przetrwania dla pojęć tworzących narzędzia. Nie używamy języków, które nie są przydatne. Jeśli programista nie jest jednocześnie artystą, to zwykle istnieje rozbieżność pomiędzy programem, który jest piękny, a takim, który jest funkcjonalny.

Al: Czy nie może mieć obu tych cech? Zazwyczaj rozgranicza się te dwie własności. Pytanie brzmi: czy programowanie jest kreatywnym przedsięwzięciem, czy sztuką?

Al: Knuth bardzo interesował się oprogramowaniem jako sztuką. Uważał, że programy pow inny być piękne. Prawie wszyscy programiści, których znam, uważają, że w pisanych przez nas programach powinna być elegancja. Stolarz mógłby powiedzieć: „Oto krzesło. Można na nim usiąść lub można na nim stanąć. Można poustawiać na nim książki telefoniczne. Spójrzcie jed n ak na ten elegancki wygląd, wspaniałe połączenia, rzeźby". To sztuka, mimo że dotyczy przedmiotu codziennego użytku.

Al: Ale piękny może być również minimalizm. Aby coś było piękne, nie potrzeba żadnych ornamentów lub elementów w stylu rokoko. W ja k i sposób można zostać lepszym programistą?

Al: Moja sugestia num er jeden brzmi: „Pomyśl, zanim zaczniesz program ow ać” . Następnie poradziłbym, aby pisać jak najwięcej kodu, dawać kod do recenzji ekspertom, czytać kod napisany przez innych oraz uczestniczyć w recenzjach kodu. Jeśli ktoś jest naprawdę odważny, może spróbować nauczyć studentów pisania dobrego kodu. Odkryłem, że nie ma lepszego sposobu poznania jakiegoś tematu niż próba nauczenia go innych. W procesie nauczania trzeba zorganizować materiał i prezentację w taki sposób, aby tem at stał się jasny dla innych. Kiedy robim y to w sali wykładowej,

AWK

1 39

studenci zadają pytania, które pokazują inne sposoby myślenia o problemach — różne od sposobu, w jaki widzieliśmy problem wcześniej. Nasze widzenie problemu staje się głębsze i o wiele ostrzejsze, niż było wcześniej. Z całą pewnością jest to prawda w odniesieniu do programowania. Jeśli nauczamy program ow ania, studenci zadają pytanie, czy nie m ożna rozwiązać tego w taki czy taki sposób. Wtedy dochodzisz do wniosku: „Tak. Jest wiele sposobów rozwiązania problemu za pomocą programu” . Zdajesz sobie sprawę, że ludzie myślą bardzo różnie, a ponieważ myślą różnie, mają różne podejście do rozwiązywania problemów. Dzięki nauczaniu można lepiej zrozumieć odmienne stanowiska w odniesieniu do konkretnych problemów. Odkryłem, że w trakcie pisania książek, w których om aw iałem program y, modyfikowałem je, dzięki czemu stawały się wydajniejsze i krótsze. Kiedy powstawała książka o AWK, program y, które w niej opisywaliśmy, stały się o 50% krótsze. Nauczyliśmy się bowiem stosować abstrakcje w AWK jeszcze wydajniej w stosunku do tego, co robiliśmy wcześniej. Czy podczas pisania książki znalazł pan niedoskonałości w projekcie języka AWK?

Al: Kiedy zaczęto używać AWK do rozwiązywania wielu innych zadań od tych, o których pierwotnie myśleliśmy, ujawniły się pewne cechy języka związane z tym, że nie planowaliśmy, aby był to język ogólnego przeznaczenia. Nie nazwałbym tego brakami, ale stało się jasne, że AWK jest językiem specjalizowanym, który nie był przeznaczony do pewnych zastosowań, do których próbowano go użyć. Czy potrafiliście rozwiązać niektóre z tych problemów, czy też mocno przeciwstawialiście się przekształceniu AWK w bardziej uniwersalny język?

Al: Po utworzeniu pierwszej wersji AWK język ewoluował przez około dziesięć lat. Dodawaliśmy do niego nowe konstrukcje i nowe operatory, ale pozostał on językiem do dopasowywania wzorców, językiem przeznaczonym do rozwiązywania problemów przetwarzania danych. Nie odeszliśmy od tej domeny. W ja ki sposób udostępnia pan ideę transformacji kierowanych składnią użytkownikom, którzy nie wiedzą zbyt wiele (lub nie wiedzą nic) na temat maszyn o skończonej liczbie stanów oraz automatach ze stosem?

Al: Użytkownik języka AWK nie musi znać tych pojęć. Z drugiej strony, jeśli ktoś zajmuje się projektow aniem języka i jego im plem entacją, wiedza o m aszynach o skończonej liczbie stanów i gramatyce bezkontekstowej ma zasadnicze znaczenie.

140

ROZDZIAŁ

SZÓSTY

Czy użytkownik narzędzi lex lub yacc powinien rozumieć gramatykę bezkontekstową, mimo że programy, które są generowane przez te narzędzia, nie wymagają od użytkowników rozumienia tego pojęcia?

Al: Większość użytkowników narzędzia lex potrafi z niego korzystać, choć nie wie, czym jest maszyna o skończonej liczbie stanów. Użytkownik program u yacc w rzeczywistości pisze gramatykę bezkontekstową. Zatem z tej perspektywy użytkownik programu yacc musi rozumieć gramatykę, ale nie musi być formalnym teoretykiem języka. Nieznajomość gramatyki może spowodować konieczność przebijania się przez wielostronicowe raporty o błędach dotyczących konfliktów shift-reduce.

Al: Użyteczną cechą programu yacc jest to, że ponieważ automatyzuje on konstrukcję deterministycznego par sera na podstawie gramatyki, informuje projektantów języka program ow ania na tem at konstrukcji w ich języku, które są niejednoznaczne lub trudne do parsowania. Bez tego narzędzia trudno byłoby zauważyć te niefortunne konstrukcje. Projektanci języka używający narzędzia yacc często mówią: „Nie zdawałem sobie sprawy, że istnieją dwa sposoby interpretacji tej konstrukcji gramatycznej!” . N astępnie elim inują lub m odyfikują wątpliw ą konstrukcję. Niejednoznaczności w kolejności wykonywania działań i łączności operatorów m ożna łatwo rozwiązać przy użyciu prostych mechanizmów, za pom ocą których określamy: „Chciałbym zastosować taką kolejność stosow ania operatorów w języku oraz taki porządek łączności” . W ja ki sposób należy tworzyć język przyjazny debugowaniu? Co podczas projektowania języka myśli się o własnościach, które trzeba dodać bądź usunąć, aby ułatwić fazę debugowania?

Al: W projektowaniu języków programowania obowiązuje trend, aby tworzyć języki poprawiające niezawodność programów oraz wydajność programisty. Powinno się projektować języki przy zastosowaniu dobrych praktyk inżynierii oprogramowania. Dzięki tem u zadanie tw orzenia niezaw odnych program ów jest rozproszone na przestrzeni całego cyklu życia oprogramowania, zwłaszcza we wczesnych etapach projektu systemów. Nie można projektować systemów z założeniem, że użytkownicy będą potrafili napisać wiele milionów wierszy kodu i nie popełnią przy tym błędów. Samo debugowanie nie jest skutecznym sposobem tworzenia niezawodnych systemów. Dobrą metodą eliminowania przypadkowych błędów jest regularność składni i semantyki.

AWK

141

Unix i jego kultura Wydaje się, że wczesna kultura systemu Unix promowała pogląd, że kiedy istnieje problem, to w celu jego rozwiązania należy napisać niewielki kompilator lub stworzyć prosty język. Jak pan uważa, w których przypadkach stworzenie języka w celu rozwiązania specyficznego problemu, zamiast napisania programu w innym języku, jest właściwe?

Al: Obecnie istnieją tysiące języków programowania. Można by zadać pytanie, dlaczego one wszystkie powstały. Prawie w każdej dziedzinie ludzkiej działalności obowiązuje specyficzny język. Muzycy wykorzystują specjalną notację do pisania muzyki, prawnicy używają swojego języka do rozmów na tem at prawa, chemicy używają specjalnych diagram ów do opisywania atom ów i m olekuł oraz sposobów ich wiązania. Nie jest niczym nienaturalnym, że ktoś mówi: „Stwórzmy bazujący na tej notacji język, który będzie służył do rozwiązywania problemów z tej dziedziny” . Można użyć języka ogólnego przeznaczenia do przedstawienia dowolnego algorytmu. Z drugiej strony często o wiele wygodniejsze, bardziej uzasadnione ekonomicznie i znacznie bardziej sugestywne jest używanie specjalizowanego języka do rozwiązywania specyficznych problem ów. M om ent, w którym należy stworzyć now y język, jest przedmiotem subiektywnej oceny. Jeśli jednak dziedzina jest interesująca oraz istnieją specjalne przesłanki, które predestynują ją do autom atyzacji, pow stanie języka program ow ania do rozwiązywania problem ów z danej dziedziny jest całkowicie naturalne. Uzasadnione ekonomicznie w kontekście inwestycji programisty czy czasu sprzętowego?

Al: Języki, przynajmniej we wczesnej fazie ich powstawania, były tworzone dlatego, że ludzie rozpoznawali określone ważne klasy problemów, które musieli rozwiązać. Wtedy opracowywali wydajne sprzętowo języki programowania, za pomocą których tworzyli program y rozwiązujące problem y w tych obszarach. W miarę jak sprzęt stawał się coraz tańszy i coraz szybszy, powstawały języki coraz wyższego poziomu, a wydajność sprzętowa okazywała się coraz mniej ważna. Czy uważa pan, że język AWK jako taki jest wystarczająco silny w swojej dziedzinie?

Al: Paradygmat wzorzec - działanie, typowy dla języka AWK, jest bardzo naturalnym podejściem podczas rozwiązywania dużych klas powszechnych problem ów przetw arzania danych. Zm iana tego paradygm atu zepsułaby wizerunek języka i m ogłaby spowodować, że przestałby on być tak atrakcyjny dla klas problemów, które mieliśmy na myśli. Język AWK jest również bardzo przydatny do programowania w wierszu polecenia w systemie Unix.

1 42

ROZDZIAŁ

SZÓSTY

Przypomina to filozofię Uniksa bazującą na łączeniu wielu niewielkich narzędzi, z których każde świetnie nadaje się do realizacji swojej funkcji.

Al: Myślę, że to bardzo trafny opis. Większość przypadków użycia języka AWK, z ja k im i się zetknąłem , to narzędzia wiersza polecenia lub skrypty powłoki.

Al: Aplikacje, w których m ożna kom ponować problemy w wierszu polecenia lub stworzyć skrypty pow łoki składające się z poleceń Uniksa, to bardzo popularne program y w AWK. Ten styl rozwiązywania problemów uosabia wczesne aplikacje AWK w systemie Unix, a nawet wiele współczesnych aplikacji uniksowych. W Uniksie „wszystko jest plikiem". Czy ma pan pogląd na to, co można by uznać za „plik" w internecie?

Al: Pliki to wygodna, prosta abstrakcja, której pow inno się używać wszędzie, gdzie jest to możliwe. Współczesny internet stał się jednak znacznie bogatszy w typy danych i programy często muszą obsługiwać wiele współbieżnych strumieni interaktywnych multimedialnych danych. Dziś do przetwarzania danych najlepszym rozwiązaniem wydaje się użycie ustandaryzow anych, dobrze zdefiniowanych interfejsów API. D odatkow o systemy zabezpieczające pow inny wiedzieć, w jaki sposób właściwie reagować na dane o złym formacie. Jakie ograniczenia widzi pan w narzędziach działających w wierszu polecenia, a jak ie w programach z graficznym interfejsem użytkownika?

Al: Język AWK przydaje się do konwersji formatu wyjściowego jednego programu, tak by można go było wykorzystać jako wejście do innego programu. Jeśli w programie z graficznym interfejsem użytkownika taki rodzaj konwersji danych przeprogramowano do takiej postaci, że można ją wywołać za pomocą kliknięcia myszą, to z całą pewnością jest to wygodniejsze. Jeśli tak nie jest, uzyskanie dostępu do wewnętrznego formatu w celu wykonania potrzebnych konwersji danych może być bardzo trudne. Czy istnieje związek pomiędzy ideą łączenia ze sobą programów działających w wierszu polecenia za pośrednictwem potoków a ideą pisania prostych języków — osobnych dla każdej specyficznej dziedziny?

Al: Myślę, że istnieje taki związek. Zwłaszcza we wczesnych latach Uniksa potoki ułatw iały składanie funkcji w wierszu polecenia. M ożna pobrać wejście, wykonać z nim pewne transformacje i przekierować wyjście do innego programu. Dostarcza to interesującego sposobu szybkiego tw orzenia now ych funkcji poprzez prostą kompozycję program ów. Są także inne narzędzia pozwalające na rozwiązywanie problemów w podobny sposób. Stworzony przez Larry’ego Walla język Perl, który według mnie jest potomkiem języka AWK i innych narzędzi uniksowych, łączy wiele aspektów takiego sposobu kompozycji programów.

AWK

143

Kiedy mówi pan „składanie funkcji", przywodzi to na myśl matematyczne podejście do składania funkcji.

Al: Właśnie to mam na myśli. Czy w momencie tworzenia potoków myśleliście o matematycznym formalizmie, czy też była to metafora dodana później, kiedy ktoś zorientował się, że mechanizmy te działają podobnie?

Al: Myślę, że o potokach w ten sposób m yślano od początku. Uznanie za potoki, przynajmniej w mojej książce, należy się Dougowi Mcllroyowi. On myślał jak matematyk i sądzę, że miał w głowie mechanizm składania funkcji od samego początku. Wiersz poleceń Uniksa można uznać za prototypowy język funkcyjny. Do jakiego stopnia formalizowanie semantyki i pojęć języka je st przydatne? Czy taki formalizm występuje w AWK?

Al: Język AWK zaprojektowano na bazie schematu translacji sterowanego składnią. Bardzo interesow ałem się kom pilatoram i i teorią kom pilatorów , dlatego kiedy tworzyliśmy AWK, implementację zrealizowałem w formie translacji zarządzanej składnią. Dla języka AWK istniała form alna składnia w postaci gramatyki bezkontekstowej. Translacja z języka źródłowego do docelowego była wykonywana na bazie działań semantycznych bazujących na tej formalnej gramatyce. To ułatwiło rozwój języka AWK. Mieliśmy do dyspozycji nowo powstałe narzędzia do tworzenia kompilatorów: lex i yacc. Pomagały one w eksperymentowaniu z tworzeniem języka. Simon Peyton Jones, jeden z twórców języka Haskell, powiedział, że dla 8 0 - 8 5 % języka istniały formalne podstawy matematyczne. Formalizowanie pozostałej części nie miało sensu ze względu na mizerne efekty.

Al: Ze względu na bezpieczeństwo w projektow aniu języków i systemów znacznie większego znaczenia nabiera specyficzność. Hakerzy często włamują się do systemów, korzystając z niestandardowych lub nieudokumentowanych własności i w ten sposób naruszają ich bezpieczeństwo. Jeśli dodać do tego projektowanie bibliotek, nagle problem rośnie jeszcze bardziej. „Wyspecyfikowałem formalne zasady dla mojego języka, ale teraz potrzebuję biblioteki do komunikowania się z internetem. Czy uwzględniłem formalizmy tej biblioteki? Czy są one zgodne z przyjętymi formalizmami języka? Czy przypadkiem nie naruszają formalizmów języka i jego gwarancji?"

Al: Podczas pracy w branży telekomunikacyjnej zauważyłem, że prawie wszystkie specyfikacje interfejsu, dla których firma Bell Labs konstruowała sprzęt, były zgodne z międzynarodowymi standardami. Wiele spośród tych standardów było pisanych w języku naturalnym. W związku z tym często były one niejednoznaczne, niekompletne

144

ROZDZIAŁ

SZÓSTY

i niespójne. Jednak pomimo tych trudności międzynarodowe sieci telekomunikacyjne i internet współpracują ze sobą i pracują dobrze głównie dzięki dobrze zdefiniowanym interfejsom pomiędzy systemami. Firmy zewnętrzne często tw orzą sterowniki urządzeń i aplikacje dla systemów operacyjnych innych producentów. Jeśli sterownik urządzenia lub aplikacja zawierają błędy, producenci systemów zyskują złą reputację za niskiej jakości oprogramowanie, podczas gdy nie jest to ich wina. Ostatnio naukowcy osiągnęli olbrzymie postępy w budowaniu narzędzi weryfikacji oprogramowania. Do sprawdzania, czy programy napisane przez dostawców sterowników urządzeń lub innych producentów aplikacji praw idłow o korzystają z API, stosuje się techniki sprawdzania modeli oraz inne zaawansowane metody. Te nowe narzędzia weryfikacji oprogramowania doprowadziły do znaczącego wzrostu jakości oprogramowania. Czy języki programowania skorzystały na wprowadzeniu tych formalizmów?

Al: W prawie wszystkich współczesnych językach występują pewne mechanizmy formalnego opisu gramatyki. Dużym problemem jest to, czy jesteśmy w stanie, bądź czy chcemy, opisać sem antykę języka przy użyciu bieżących sformalizowanych mechanizmów opisu semantyki języków programowania. O wiele bardziej mechaniczną częścią projektow ania języka od formalizmów semantycznych jest konstruow anie parsera na podstawie bezkontekstowej gramatyki. Mimo że opisywanie semantyki jest żmudne, wierzę w korzyści wynikające z planowania, opisywania oraz szkicowania semantyki języka przed przystąpieniem do jej implementowania. Przychodzą mi na myśl dwie historie. Słynna historia narzędzia Make, kiedy to Stuart Feldman zdecydował, że nie może zrezygnować z tabulacji, ponieważ ma ju ż 12 użytkowników, a także sytuacja z artykułu Dicka Gabriela „Worse Is Better"3, w którym opisano podejście stosowane na Uniwersytecie New Jersey oraz w MIT. Zwyciężyło podejście typowe dla systemu Unix, języka C oraz stanowisko z New Jersey.

Al: Zawsze uważałem to za sukces teorii Darwina. Wierzę, że język rozwija się i ewoluuje ze względu na to, że jest używany przez programistów. Języki wymagające wielkich grup roboczych do stworzenia wstępnego projektu są zazwyczaj ignorowane przez programistów. Jeśli ich użycie nie zostanie wymuszone, ich szanse na przetrwanie są nikłe. Jednym z bardziej alarmujących aspektów popularnych języków jest ich ciągły rozrost. Nie wiemy, jak skorzystać z własności istniejącego języka. Główne współczesne języki — C++ i Java — są znacznie bardziej rozbudowane dziś, niż były wtedy, kiedy je po raz pierwszy utworzono. Wydaje się, że trend do rozrastania się tych języków utrzyma się

3

http://www.dreamsongs.comfWorseIsBetter.html.

AWK

1 45

w przyszłości. Dziś nie ma już możliwości, aby pojedynczy człowiek zdołał zrozumieć wszystkie aspekty języka, a rozm iar kom pilatorów tych języków jest mierzony w milionach wierszy kodu. Wydaje się, że w badaniach nad systemami otwarta jest następująca kwestia: w ja k i sposób stworzyć język, który jest rozszerzalny poza jego początkową dziedzinę problemu, bez konieczności modyfikowania samego języka? Czy w języku AWK istnieje mechanizm rozszerzeń?

Al: Historia pokazała, że biblioteki są dobrym instrumentem realizacji rozszerzeń. Nawet w językach C+ + i Java są zapowiadane zmiany w języku.

Al: To prawda. Nawet podstawowe języki się rozwijają. Dąży się jednak do zapewnienia zgodności rdzenia języka z przeszłością, tak aby zapewnić działanie istniejących programów. To zapobiega totalnym zmianom w tych językach. Czy taka cecha jest dobra sama w sobie?

Al: Zapewnienie możliwości działania istniejących programów jest oczywiście bardzo pożądane. Kiedyś napisałem dla czasopisma Science Magazine artykuł „Software and the Future of Programming Languages”4. Próbowałem w nim oszacować, jaka jest skala w ykorzystywania program ów kom puterow ych do rozwiązywania różnych problemów. Chciałem przy tym uwzględnić wszystkie systemy programowe używane przez instytucje i ludzi z całego świata. Oszacowałem, że wykorzystywane program y zawierają od pięciuset bilionów do tryliona wierszy kodu źródłowego. Założywszy, że jeden wiersz kodu źródłowego kosztuje od 10 do 100 USD, doszedłem do wniosku, że po prostu nie możemy sobie pozwolić na ponow ne zaprogram ow anie znaczącej części bazy oprogram ow ania. Oznacza to, że istniejące języki i systemy będą wykorzystywane jeszcze przez długi czas. Pod wieloma względami sprzęt jest bardziej przenośny niż oprogramowanie, poniew aż zawsze dąży się do tw orzenia szybszego sprzętu, który byłby zdolny do uruchomienia starszych programów. Unix je s t bardzo przenośnym systemem operacyjnym. Czy wynikało to z tego, że istniała możliwość przenoszenia go na inne platformy, czy z dążenia do migracji istniejącego oprogramowania na różne platformy sprzętowe w miarę ich rozwoju?

Al: W miarę jak ewoluował system Unix, kom putery ewoluowały jeszcze szybciej. Jedna z większych zm ian w systemie Unix nastąpiła w momencie, kiedy Dennis Ritchie opracował język C w celu stworzenia trzeciej wersji Uniksa. Dzięki temu system Unix stał się przenośny. W ciągu stosunkow o krótkiego czasu, kiedy pracowałem w Bell Labs, korzystaliśmy z Uniksa na różnych platformach — od minikomputerów 4 Oprogramowanie i przyszłość języków programowania — przyp. tłum.

1 46

ROZDZIAŁ

SZÓSTY

do największych na świecie superkomputerów. Udało się to dzięki temu, że system ten był napisany w języku C i stosowaliśmy technologię przenośnych kompilatorów, którą m ożna było wykorzystać do szybkiego stw orzenia kom pilatorów języka C dla nowych maszyn. Rozwijając system Unix, koncentrowaliśmy się na stworzeniu systemu, który ułatwiłby rozwój oprogramowania wygodny dla programistów. Taki, jakiego chcieliby używać do tworzenia nowych programów. Myślę, że w systemie Unix to się wybitnie udało. Taką cechę miała większość najlepszych narzędzi oraz większość najlepszego oprogramowania.

Al: Można by zadać interesujące pytanie: czy twórcy najlepszych narzędzi są artystami, czy rzemieślnikami? Nie sądzę, aby istniała jednoznaczna odpowiedź na to pytanie, ale z całą pewnością we wczesnym okresie Uniksa większość przydatnych narzędzi była tw orzona przez program istów, którzy opracowali now atorskie narzędzia do rozwiązywania problemów. Był to jeden z powodów, dla których powstał język AWK. Briana, Petera i mnie interesowały określone klasy aplikacji, które chcieliśmy napisać. Chcieliśmy je jednak napisać za pomocą bardzo krótkich programów. Czy istnienie narzędzi oraz powstawanie coraz to nowych wymagań praktycznych skłaniały do tworzenia lepszych narzędzi i lepszych algorytmów?

Al: We wczesnych latach mojej kariery badawczej, które były jednocześnie wczesnymi latami Uniksa, m oją m otywacją było twierdzenie K nutha, że najlepsza teoria jest inspirowana praktyką, a najlepsza praktyka — teorią. Napisałem dziesiątki artykułów na temat sposobów wydajniejszego parsowania oraz na temat wygodnego i wydajnego parsowania konstrukcji występujących w rzeczywistych językach programowania. Steve Johnson, Jeff Ullman i ja bardzo blisko w spółpracowaliśm y w dziedzinie rozwoju tej teorii, a także narzędzia yacc. Tak więc program yacc był doskonałym połączeniem teorii z praktyką.

Rola dokumentacji Kiedy piszę dokumentację, samouczek lub artykuł dotyczący oprogramowania mojego autorstwa, często znajduję miejsca, w których projekt jest trudny lub kłopotliwy do objaśnienia. To inspiruje mnie do usprawniania programu. Czy pan postępuje podobnie?

Al: Bardzo często. Moje doświadczenie z językiem AWK m iało znaczący wpływ na sposób, w jaki w ykładałem przedm iot języki program ow ania i kom pilatory na Uniwersytecie Columbia. Kurs przedmiotu obejmował semestr, w którym studenci pracujący w pięcioosobowych zespołach realizowali projekt polegający na stworzeniu prostego języka i napisaniu dla niego kompilatora.

AWK

147

W ciągu 20 lat, podczas których wykładałem ten przedmiot, nigdy się nie zdarzyło, aby na koniec semestru jakiemuś zespołowi nie powiodło się stworzenie działającego kompilatora. Sukces ten nie był przypadkowy. Wynikał z moich doświadczeń w pracy nad językiem AWK, obserwacji rozwoju oprogramowania w Bell Labs, uznania roli procesu wytwarzania lekkiego oprogram ow ania dla projektów tego rodzaju oraz słuchania moich studentów. Proces inżynierii oprogramowania towarzyszący projektowi kompilatora ma istotne znaczenie dla sukcesu w stworzeniu nowego języka i napisaniu dla niego działającego kom pilatora w ciągu 15 tygodni. Studenci mieli dwa tygodnie na podjęcie decyzji 0 uczestnictwie w kursie. Po dw óch tygodniach studenci tworzyli zespoły, a po kolejnych dwóch musieli napisać krótki artykuł (wzorowany na artykule o Javie) opisujący język, który chcieli stworzyć. Artykuł form ułow ał propozycję języka — powody, dla których język jest potrzebny, oraz właściwości, jakie pow inien posiadać. Najważniejszym aspektem artykułu było to, że zmuszał on studentów do podjęcia szybkiej decyzji na tem at rodzaju języka, jaki chcieli stworzyć. Po miesiącu studenci pisali samouczek języka oraz dokum entację opisującą język (ang. languge reference). Samouczek był wzorow any na narzędziu opisanym w rozdziale 1. książki Kernighana i Ritchiego Język A N S IC [WNT 2000], natomiast opis języka jest wzorowany na dodatku A tej samej książki. Samouczek i opis języka był przeze m nie bardzo uważnie oceniany, poniew aż w tym m om encie studenci nie zdawali sobie sprawy z tego, jak tru dno jest stworzyć działający kom pilator naw et dla prostego języka. Prosiłem studentów, aby określili, jakie własności zaimplementują na pewno, a jakie zaim plem entują pod w arunkiem , że starczy im czasu (nigdy się nie zdarzyło, aby studentom udało się zaim plem entow ać jakiekolwiek dodatkow e własności). Celem tego ćwiczenia było zdefiniowanie zakresu projektu w taki sposób, aby udało się go zrealizować w czasie trwania semestru i aby projekty były mniej więcej równe pod względem nakładów pracy, jakie należało ponieść, by je zrealizować. Po sformowaniu zespołów studenci wybierali dla swojego zespołu menedżera projektu, architekta systemowego, integratora systemu, osoby zajmujące się weryfikacją oraz guru języka. Każda z tych osób odgrywała decydującą rolę w procesie tworzenia języka oraz opracowywania działającego kompilatora. Zadaniem m enedżera projektu było stworzenie harm onogram u czasowego dla składowych projektu i wymuszenie ich realizacji. Architekt systemowy tworzył diagram blokowy dla kompilatora, natomiast integrator systemu definiował narzędzia 1 środowisko projektowe wykorzystywane do realizacji kompilatora. Po opisaniu języka osoba odpow iedzialna za weryfikację tworzyła plan testów oraz zestaw testowy dla całego języka. Guru języka miał za zadanie sprawdzić, czy właściwości języka zdefiniow ane w artykule opisującym język rzeczywiście zostały zaimplementowane.

1 48

ROZDZIAŁ

SZÓSTY

Dla języka AWK stworzyliśmy zestaw testów regresji chyba nieco zbyt późno. Nasz zestaw testowy okazał się bezcenny. Podczas prac nad językiem zawsze uruchamialiśmy zestaw testów regresji przed zapisaniem propozycji do głównego katalogu. Dzięki tem u zawsze mieliśmy działającą wersję kom pilatora. Przed dodaniem now ych własności do języka tworzyliśmy i dodawaliśmy testy dla tych własności do zestawu testów regresji. Wspominałem, że nigdy się nie zdarzyło, aby na koniec semestru jakiemuś zespołowi studentów nie udało się dostarczyć działającego kompilatora. Zestaw testów regresji jest kluczem do osiągnięcia tego celu: na koniec semestru studenci dostarczali to, co w tym czasie działało. W działającym kom pilatorze musiały być jednak zaimplementowane te własności języka, które zostały zapowiedziane w dokumencie opisującym język. Architekci systemowi opracowywali diagram blokowy kom pilatora, specyfikację interfejsu oraz określali, kto i kiedy ma zaimplementować poszczególne komponenty. Każdy członek zespołu musiał wnieść do projektu co najmniej 500 wierszy kodu źródłowego. Każdy też, włącznie z menedżerem projektu, musiał zaimplementować jakieś własności. Tworzenie programów, które muszą kom unikow ać się z kodem innych osób, jest bardzo zdrowym podejściem (a jednocześnie stanowi spore wyzwanie). Integrator systemu jest odpowiedzialny za określenie platform y, na której jest budow any kompilator, oraz używanych narzędzi — na przykład lex, yacc, ANTLR. Musi także nauczyć się używania tych narzędzi oraz nauczyć właściwego posługiwania się narzędziami pozostałych członków zespołu. Dzięki tem u każdy zespół ma odpow iednie zasoby ludzkie oraz właściwy zestaw narzędzi. G uru języka ma najbardziej interesujące zajęcie. Jego zadaniem jest zapewnienie intelektualnej integralności języka, tak aby własności sform ułow ane w artykule opisującym język rzeczywiście zostały zaim plem entow ane. Musi zdefiniować ograniczenia dla zmian w projekcie i kodzie, aby w przypadku w prow adzenia modyfikacji w projekcie języka zmiany te zostały zarejestrowane i dostarczone do całego zespołu oraz by zostały uwzględnione w zestawie testów regresji. W trakcie realizacji projektu studenci uczą się trzech ważnych umiejętności: zarządzania projektem, pracy zespołowej oraz kom unikacji — zarówno ustnej, jak i pisemnej. Na koniec pytałem studentów o to, co jest najważniejszą umiejętnością, jakiej nauczyli się podczas kursu. Często wymieniali oni jedną z przywołanych wyżej umiejętności. D okum entacja steruje projektem , a studenci zdobywają wiele praktycznych umiejętności podczas pisania i rozmów na temat języka. Studenci muszą zaprezentować swój język przed grupą. Głównym celem tej prezentacji jest przekonanie kolegów, że cały świat powinien korzystać z tworzonego przez nich języka. Pierwszemu zespołowi osobiście pomagałem w prowadzeniu skutecznej prezentacji. Kolejne zespoły starały się stworzyć lepszą prezentację swojego języka ze względu na entuzjastyczne nastawienie

AWK

1 49

do niego. Tworzone języki obejmowały różną tematykę: od symulacji komputerów kwantowych, poprzez kom ponowanie muzyki, tworzenie komiksów, symulowanie cywilizacji, wykonywanie obliczeń na macierzach, po generowanie grafiki. Na koniec kursu studenci musieli przedstawić finalny rap ort języka zawierający następujące rozdziały: artykuł o języku, samouczek języka, opis języka, rozdział napisany przez menedżera projektu na temat sposobu zarządzania projektem, rozdział napisany przez architekta systemowego zawierający diagram blokowy i specyfikacje interfejsu, rozdział napisany przez integratora systemu na tem at platform y programowania i używanych narzędzi, rozdział napisany przez osobę odpowiedzialną za weryfikację zawierający plan testów i zestawy testowe i na koniec — rozdział napisany przez guru języka na tem at sposobów zapewnienia zgodności języka z ustalonymi celami. Ostatni rozdział był zatytułowany „Czego się nauczyliśmy?” . Znalazły się w nim odpowiedzi na następujące pytania: Czego nauczyłeś się jako członek zespołu? Czego nauczyłeś się indywidualnie? Czy kurs pow inien być przeprowadzony w następnym roku? Co twoim zdaniem powinno pozostać bez zmian? Co twoim zdaniem powinno się zmienić? W dodatku był zamieszczony listing kodu, w którym autorzy podpisywali każdy z napisanych przez siebie modułów. Jeśli coś się ulepsza przez dłuższy czas, zazwyczaj uzyskuje się bardzo dobry produkt. W kolejnych latach uwzględniałem rady udzielane mi przez studentów. W efekcie kilka lat temu za ten kurs otrzymałem wyróżnienie Great Teacher Award od Stowarzyszenia Absolwentów Uniwersytetu Columbia. Wielu pracowników firm rekrutacyjnych przeprowadzających rozmowy kwalifikacyjne z moimi studentami stwierdziło, że życzyliby sobie, aby ich systemy oprogramowania były tworzone z wykorzystaniem takiego procesu. Jaki był poziom zaawansowania studentów biorących udział w tym kursie?

Al: Byli to w większości studenci starszych lat studiów lub studenci po pierwszym roku. Aby móc uczestniczyć w kursie, trzeba było jednak spełnić wiele w arunków wstępnych, między innymi wykazać się znajomością zagadnienia zaawansowanego program ow ania, teorii informatyki, a także struktury danych i algorytmów. Jeśli chodzi o pracę studentów, to moje uznanie wzbudzało stosowanie przez nich technik tworzenia oprogram ow ania rozproszonego — używanie takich mechanizmów jak wiki oraz zaaw ansow anych środowisk IDE. Wielu m oich studentów znalazło zatrudnienie w branży. W czasie trw ania kursu zwracałem szczególną uwagę na to, aby w miarę rozwoju języka studenci dbali o zachowanie aktualności zestawu testów regresji. Stosowanie zestawu testów regresji sprawia, że studenci stają się znacznie bardziej wydajni. Zwykle bowiem znajdują swoje własne błędy, a nie błędy innych członków zespołu.

150

ROZDZIAŁ

SZÓSTY

Kiedy i w ja k i sposób należy uczyć debugowania?

Al: Myślę, że debugowania należy uczyć razem z programowaniem. Brian w swoich różnych książkach prezentował wiele porad dotyczących debugowania. Nie słyszałem jednak o żadnej uniwersalnej teorii debugowania. Techniki używane do debugowania kompilatora bardzo różnią się od tych, których używa się do debugowania programu do analizy numerycznej. Myślę zatem, że najlepszym podejściem jest uwzględnianie w każdym kursie program ow ania przykładów testów jednostkowych, procesów systematycznego testow ania oraz posługiwania się narzędziami do debugowania. Myślę również, że zdrowym podejściem jest nakłanianie studentów do pisania specyfikacji tego, co m ają robić ich programy, przed przystąpieniem do pisania programu. Jednym z błędów, które popełniliśmy przy okazji pracy nad językiem AWK, było to, że od początku nie wdrożyliśmy rygorystycznej strategii testowania. Rozpoczęliśmy rygorystyczne testowanie po jakimś czasie od rozpoczęcia projektu, ale bylibyśmy znacznie bardziej wydajni, gdybyśmy rozwinęli rygorystyczną strategię testowania od samego początku. Jakie własności powinni mierzyć programiści w czasie powstawania bazy kodu i w jaki sposób powinni to robić?

Al: Najważniejszą cechą jest poprawność implementacji, ale nie istnieje uniwersalny sposób na osiągnięcie poprawności. Proces ten obejmuje różnorodne zadania, takie jak dobór niezmienników, testowanie i recenzowanie kodu. Kod należy optymalizować, ale nie w olno tego robić przedwcześnie. Utrzym anie zgodności dokum entacji i kom entarzy z kodem jest ważne, bardzo łatw o to jednak zaniedbać. Nowoczesne zintegrow ane środowisko program isty oraz dobre narzędzia w ytw arzania oprogramowania to konieczność. W ja k i sposób wznawia pan sesję programowania, jeśli nie zaglądał pan do kodu przez kilka dni? A ja k pan to robi, jeśli przerwa trwa kilka miesięcy?

Al: Kiedy pisze się system programowania (lub na przykład książkę), wówczas trzeba mieć obraz całego systemu w głowie. Przerwy mogą doprowadzić do przerwania łańcucha myślenia. Jeśli jednak przerwa nie trwa zbyt długo, zwykle wystarczy chwila na przejrzenie kodu, by do niego powrócić. Po przerwie trwającej kilka miesięcy lub lat często sięgam do artykułów, książek lub notatek, w których dokum entowałem swoje algorytmy. W ten sposób odświeżam pamięć i przypominam sobie swój kod. Chcę przez to powiedzieć, że dobre kom entarze i dokum entacja to doskonały instrument zarówno dla projektantów systemu, jak i dla osób, które będą zajmowały się utrzymywaniem kodu przez dłuższy okres. Brian prowadził dziennik najważniejszych decyzji, jakie podejmowaliśmy podczas projektowania języka. Jego notatki okazały się dla mnie bezcenne.

AWK

151

Informatyka Jakie badania prowadzi się w informatyce?

Al: To doskonałe pytanie, na które nie ma dobrze zdefiniowanej odpowiedzi. Myślę, że zakres badań w informatyce bardzo mocno się rozszerzył. W dalszym ciągu istnieją głębokie, nierozwiązane i zasadnicze pytania. W jaki sposób udowodnić, że problem faktoryzacji lub problem NP-zupełny jest faktycznie trudny? W jaki sposób zam odelować złożone systemy, takie jak ludzka kom órka lub ludzki mózg? Jak tworzyć skalowalne, zaufane systemy? W jaki sposób programiści mogą budować oprogramowanie o dowolnym stopniu niezawodności? Jak stworzyć oprogramowanie o cechach przyjaznych człowiekowi, czyli posiadające emocje lub inteligencję? Do jakiego stopnia można rozszerzać prawo Moore’a? Skala i zakres problemów przetwarzanych dziś komputerowo bardzo się rozszerzyły. Próbujemy zorganizować i uzyskać dostęp do wszystkich informacji całego świata, a kom putery i obliczenia dotyczą wszystkich aspektów codziennego życia. W konsekwencji pow stały całkowicie nowe dziedziny badań w informatyce, w zastosow aniach interdyscyplinarnych, w których obliczenia kom puterow e są połączone z innymi obszarami nauki lub ludzkiej działalności. Do tych nowych dziedzin należy biologia kom puterow a, robotyka oraz systemy cyberfizyczne. Nie wiemy, w jaki sposób najlepiej wykorzystać komputery w edukacji lub medycynie. Większego znaczenia niż kiedykolwiek nabrały prywatność i bezpieczeństwo. Według m nie informatyka, jak m ało która dziedzina ludzkiego życia, stała się niezwykle interesującym przedmiotem badań. Jaka jest rola matematyki w informatyce i programowaniu?

Al: Myślę, że projekty inżynierskie muszą mieć solidne podstawy naukowe. Język AWK zaprojektowaliśmy wokół kilku eleganckich abstrakcji w teorii informatyki, takich jak wyrażenia regularne oraz tablice asocjacyjne. Konstrukcje te zostały następnie przyjęte w głównych językach skryptowych: Perlu, JavaScript, Pythonie oraz Ruby. Do zaimplementowania prymitywów dopasowywania ciągów znaków skorzystaliśmy także z wydajnych algorytmów bazujących na teorii automatów skończonych. W sumie uważam, że AWK to doskonały mariaż dobrej teorii ze zdrową praktyką inżynierską. Pracował pan nad teorią automatów skończonych oraz je j zastosowaniami w językach programowania. Co zaskoczyło pana najbardziej, kiedy zaczął pan implementować wyniki swoich badań?

Al: Myślę, że największą niespodzianką był szeroki zakres zastosowań. Spróbujmy zinterpretować teorię automatów na przykładzie języków formalnych oraz automatów, które je rozpoznają. Teoria autom atów dostarcza przydatnej notacji do opisywania ważnych własności syntaktycznych języków programowania, w szczególności wyrażeń regularnych oraz gram atyki bezkontekstowej. A utom aty rozpoznające te języki

1 52

ROZDZIAŁ

SZÓSTY

formalne, na przykład maszyny stanów skończonych oraz automaty ze stosem, mogą służyć za modele dla algorytm ów używanych przez kom pilatory do skanow ania i parsow ania program ów . Jedną z największych korzyści z zastosow ania teorii autom atów do kompilacji jest możliwość budow ania narzędzi do konstrukcji kompilatorów, na przykład lex i yacc. Narzędzia te automatyzują tworzenie wydajnych skanerów i parserów. Co stoi na przeszkodzie, aby zbudować kompilator (czy też język), który identyfikuje wszystkie potencjalne błędy? Gdzie przebiega linia pomiędzy błędami wynikającymi ze złego projektowania programu a błędami, które można było zauważyć lub którym można było zapobiec, gdyby język był bardziej proaktywny?

Al: Zaprojektow anie kom pilatora, który potrafiłby znaleźć wszystkie błędy w program ach jest niemożliwe z powodu nierozstrzygalności. Osiągnęliśmy jednak znaczący postęp w tw orzeniu użytecznych narzędzi weryfikacji oprogram ow ania, wykorzystujących zaawansowane techniki, na przykład testowanie modelu. Dzięki stosowaniu tych technik m ożna wyszukiwać istotne klasy błędów w programach. Sądzę, że w środowisku projektowania oprogramowania przyszłości znajdzie się szereg narzędzi weryfikacyjnych, które programista będzie mógł wykorzystać do znalezienia wielu popularnych przyczyn błędów w programach. Moja długoterm inow a wizja jest taka, że dzięki użyciu silniejszych języków, bardziej rozbudow anych narzędzi weryfikacji oraz lepszych praktyk w inżynierii oprogram ow ania jakość i niezawodność oprogramowania poprawi się. W jaki sposób można projektować algorytmy dopasowywania wzorców wykorzystujące zalety współbieżności procesorów wielordzeniowych?

Al: Obecnie jest to przedm iot wielu badań. Naukowcy badają współbieżny sprzęt oraz implementacje programowe algorytmów dopasowywania wzorców, na przykład algorytm Aho-Corasick lub algorytmy stanów skończonych. Do najpopularniejszych dziedzin inspirujących prowadzenie takich badań należą analizy genetyczne oraz systemy wykrywania intruzów. Co skłoniło pana i panią Corasick do opracowania algorytmu Aho-Corasick?

Al: To bardzo interesująca historia. Na początku lat siedemdziesiątych pracowałem w spólnie z Johnem Hopcroffem i Jeffreyem Ullanem nad książką The Design and Analysis o f Computer Algorithms [Addison-Wesley]. Jednocześnie wykładałem techniki projektow ania algorytmów w Bell Labs. W kursie uczestniczyła Margaret Corasick z Bell Labs. Po wykładzie podeszła do m nie i powiedziała, że napisała program przeszukiwania bibliotek dla funkcji Boolean, pozwalający na wyszukiwanie słów kluczowych i fraz. Jednak w przypadku złożonych obliczeń uruchamianie programu pow odow ało przekroczenie lim itu kosztów za wyszukiwanie, który wynosił 600 dolarów.

AWK

153

W pierwszej implementacji programu wyszukiwania wykorzystano prosty algorytm dopasow yw ania wzorców. Zasugerowałem, aby spróbow ała wyszukiwać słowa kluczowe, równolegle wykorzystując automat skończony. Wskazałem też na możliwość skutecznej konstrukcji automatu dopasowywania wzorców liniowo zależnego od czasu dla dowolnego zbioru słów kluczowych. W róciła do mojego biura kilka tygodni później i powiedziała: „Pam ięta pan ten program do wyszukiwania, który przekraczał budżet 600 dolarów? Zaimplementowałam algorytm, który pan zasugerował. Wyszukiwanie kosztuje teraz 25 dolarów. Dowolnie złożone wyszukiwanie kosztuje tylko 25 dolarów. Są to koszty czytania taśmy” . W ten sposób narodził się algorytm Aho-Corasick. Kiedy o wszystkim dowiedział się Sam Morgan, mój kierownik, powiedział: „Dlaczego nie zajmiesz się opracowywaniem algorytmów? Wydaje mi się, że mogą one przydać się w przyszłości” . Na tym polegała magia Bell Labs w tamtych czasach: byli ludzie, których nurtowały problemy, oraz ludzie, którzy w niekonwencjonalny sposób myśleli o tych problemach. Dzięki połączeniu ich wysiłków można było osiągnąć zadziwiające rezultaty.

Hodowla niewielkich języków Dlaczego zajął się pan programowaniem?

Brian Kernighan: Nie przypominam sobie żadnego konkretnego wydarzenia. Pierwszy komputer zobaczyłem, dopiero kiedy zacząłem studia w college’u. Przez około rok nie miałem zbytniej ochoty na program ow anie (wtedy w FORTRAN-ie). Myślę, że największą przyjemność z programowania miałem podczas wakacyjnej pracy nad projektem MAC w MIT latem 1966 roku. Pracowałem tam nad programem, który tworzył zadanie obsługi taśmy dla nowego komputera GE 645 we wczesnych latach systemu Multics. Pisałem w języku MAD, znacznie łatwiejszym i przyjemniejszym od FORTRAN-a i COBOL-a — języków, w których program ow ałem wcześniej. Korzystałem z systemu CTSS — pierwszego systemu z podziałem czasu, który był o wiele łatwiejszy i przyjemniejszy od kart perforowanych. Był to okres, w którym programowanie traktowano jak łamigłówkę sprawiającą wielką przyjemność. Mogło tak być dlatego, że zbytnio nie przeszkadzały w tym mechaniczne szczegóły. W ja k i sposób uczy się pan nowego języka?

Brian: Dla mnie najłatwiejszym sposobem uczenia się nowego języka jest analiza dobrze dobranych przykładów realizujących zadania podobne do tych, które mam do wykonania. Kopiuję przykład i adaptuję go do moich potrzeb. Następnie rozwijam swoją wiedzę w sposób kierow any przez konkretną aplikację. Przyglądam się wystarczająco dużej liczbie języków, tak że po jakimś czasie obraz zaczyna mi się

154

ROZDZIAŁ

SZÓSTY

rozmywać. Kiedy przechodzę z jednego na drugi, mam pewne problemy, zwłaszcza jeśli języki te nie są podobne do języka C, którego nauczyłem się dawno temu. Dobrze, jeśli ma się dostęp do dobrych kompilatorów, które wykrywają podejrzane i nielegalne konstrukcje. W tym przypadku pomocne okazują się języki ze ścisłą kontrolą typów, takie jak C++ i Java. Dobre są również opcje wymuszające ścisłą zgodność ze standardam i. Mówiąc bardziej ogólnie, nie ma niczego lepszego od pisania dużej ilości kodu — najlepiej dobrego kodu, używanego przez inne osoby. W dalszej kolejności, również dobre, ale rzadziej wykonywane jest częste czytanie dobrego kodu, które pozwala zobaczyć, w jaki sposób piszą inni. Na koniec pomaga doświadczenie — każdy nowy problem, nowy język, nowy system i każde nowe narzędzie pozwala na doskonalenie się i łączy się z wiedzą, którą znaliśmy wcześniej. W ja k i sposób powinien być zorganizowany podręcznik dla nowego języka programowania?

Brian: Podręcznik powinien zapewniać możliwość łatwego wyszukiwania informacji. Oznacza to, że musi mieć napraw dę dobry skorowidz. Tabele zawierające takie elementy, jak operatory lub funkcje biblioteczne, powinny być zwięzłe i kompletne (a także łatwe do znalezienia), a przykłady — krótkie i bardzo czytelne. Pod tym względem podręcznik różni się od samouczka. Samouczek i podręcznik to zdecydowanie nie to samo. Myślę, że samouczek powinien być zorganizowany jako swego rodzaju spirala, w której jest zaprezentowany niewielki zbiór podstawowych elementów. Zbiór ten pow inien być na tyle kom pletny, aby pozw alał na pisanie kompletnych i przydatnych programów. Następna rotacja spirali powinna obejmować kolejny poziom szczegółów lub na przykład alternatywne sposoby wyrażania tych samych rzeczy. Przykłady powinny w dalszym ciągu być przydatne, choć mogą być bardziej rozbudowane. Na końcu powinien się znaleźć wyczerpujący opis języka. Czy przykłady — nawet te dla początkujących — powinny zawierać kod obsługi błędów?

Brian: Nie m am sprecyzowanego zdania na ten temat. Kod obsługi błędów zwykle jest obszerny, m ało interesujący i niewiele można się z niego nauczyć. W związku z tym często przeszkadza w nauczeniu się i zrozumieniu podstawowych konstrukcji języka. Z drugiej strony ważne jest, by przypom inać program istom , że błędy się zdarzają, a ich kod powinien być zdolny do radzenia sobie z błędami. Osobiście uważam, że w początkowej części samouczka powinno się ignorować kod obsługi błędów. Wystarczy wspomnieć, że błędy się zdarzają. Na podobnej zasadzie powinno się ignorować kod obsługi błędów w większości przykładów w opisie języka, poza sekcją dotyczącą błędów. Takie podejście może jednak wzmacniać nieświadomą wiarę, że ignorowanie błędów jest bezpieczne, a to zawsze okazuje się złym pomysłem.

AW K

1 55

Co pan sądził o pomyśle cytowania błędów w podręczniku Uniksa? Czy taka praktyka ma sens również dziś?

Brian: Podobały mi się sekcje BUGS. Było to jednak w czasach, kiedy programy były niewielkie i raczej proste. Dzięki temu możliwa stawała się identyfikacja pojedynczych błędów. W sekcji BUGS często znajdowały się własności, które nie zostały jeszcze zaim plem entow ane, lub takie, które nie były zaim plem entow ane prawidłowo. Nie były to błędy w takim sensie, w jakim się je zwykle rozum ie — na przykład polegające na przekroczeniu górnego indeksu tablicy. Nie sądzę, aby wskazywanie błędów było wykonalne dla większości błędów, które można znaleźć w naprawdę dużych, nowoczesnych systemach. Na pewno nie pow inno się tego robić w podręczniku. Repozytoria błędów online to dobre narzędzie zarządzania procesem tw orzenia oprogramowania. Nie sądzę jednak, aby mogły one pomóc zwykłym użytkownikom. Czy współcześni programiści powinni zapoznać się z informacjami, ja kie zamieścił pan w książce na temat stylu programowania „The Elements o f Programming Style" [Computing McGraw-Hill]?

Brian: Tak! Podstawowe idee dobrego stylu, który ma fundamentalne znaczenie dla czytelnego i prostego pisania, są dziś tak samo ważne jak 35 lat temu, kiedy Bill Plauger i ja po raz pierwszy o nich napisaliśmy. Szczegóły trochę się różnią. Do pewnego stopnia zależą od właściwości różnych języków, ale podstawowe zasady są dziś takie same, jak były wtedy. Z prostym , czytelnym kodem znacznie łatwiej się pracuje i istnieje o wiele mniejsze prawdopodobieństwo wystąpienia problemów. W związku z tym, w miarę jak programy stają się większe i bardziej złożone, posiadanie czytelnego, prostego kodu jest jeszcze ważniejsze. Czy sposób, w ja k i piszemy tekst, ma wpływ na sposób, w ja k i piszemy oprogramowanie?

Brian: Może tak być. Zarówno tekst, jak i programy zwykle wielokrotnie przeglądam, zanim poczuję, że są poprawne. Do tej prozy można by oczywiście dodać znacznie więcej szczegółów, jednak dążenie do tego, aby słowa lub kod były jak najbardziej czytelne, to podobny wysiłek. W ja k i sposób znajomość problemów rozwiązywanych przez programy pomaga programiście w pisaniu lepszego oprogramowania?

Brian: Jeśli programista nie ma czytelnego obrazu tego, do czego będzie używane jego oprogramowanie, to istnieje bardzo duże prawdopodobieństwo, że jego programy będą działały źle. W niektórych, szczęśliwych przypadkach programista rozumie użytkownika, ponieważ sam zamierza być użytkownikiem. Jedną z przyczyn, dla których wczesny system Unix był tak dobry, tak dobrze przystosowany do potrzeb programistów, było to, że jego

1 56

ROZDZIAŁ

SZÓSTY

twórcy, Ken T hom pson i Dennis Ritchie, dążyli do stworzenia systemu, który pomagałby im w tworzeniu oprogramowania. W rezultacie Unix doskonale nadawał się dla programistów piszących nowe programy. To samo można powiedzieć o języku C. Jeśli program ista nie zna i nie rozum ie dobrze dziedziny zastosow ania, pow inien w maksymalnym stopniu wykorzystać wiedzę i doświadczenie użytkownika. Bardzo pouczająca jest obserwacja nowych użytkowników naszych programów — w ciągu minuty typowy nowicjusz spróbuje czegoś lub przyjmie jakieś założenie, które nigdy nie przyszłoby nam do głowy. Jeśli jednak nie będziemy monitorować użytkowników w czasie, gdy po raz pierwszy stykają się z program em , nie będziemy świadomi problem ów, jakie napotykają. Jeśli zaczniemy obserwować ich później, praw dopodobnie już zdążą przyzwyczaić się do naszego złego projektu. W ja k i sposób programiści mogą poprawić programowanie?

Brian: Pisząc więcej kodu! Należy też myśleć o kodzie, który się pisze, i starać się przekształcać go w taki sposób, by stał się lepszy. W arto również, jeśli to możliwe, dać kod do przeczytania innym użytkownikom — współpracownikom z firmy lub członkom projektu open source. Pomocne jest również pisanie różnych rodzajów kodu oraz pisanie w różnych językach. Pozwala to na rozszerzenie repertuaru technik oraz dostarcza dodatkowych sposobów podejścia do problemu programowania. Należy czytać kod innych osób — na przykład po to, by podejm ować próby dodaw ania nowych własności bądź poprawiania błędów. W ten sposób uczymy się, w jaki sposób inne osoby podchodzą do rozwiązywania problemów. Doskonałym sposobem, który pomaga poprawić własny kod, jest podejmowanie prób nauczenia programowania innych osób. Każdy wie, że debugowanie je st dwa razy trudniejsze od pisania programów. W ja k i sposób należy zatem uczyć debugowania?

Brian: Nie jestem pewien, czy debugow ania m ożna nauczyć. Z całą pewnością m ożna jednak próbow ać mówić ludziom, w jaki sposób robić to systematycznie. Debugowaniu poświęcony został cały rozdział w książce The Practice o f Programming [Addison-Wesley], którą napisałem wspólnie z Robem Pike’em. Próbowaliśmy w tym rozdziale wskazać sposoby podniesienia skuteczności w debugowaniu. Debugowanie jest sztuką, ale z całą pewnością istnieje możliwość poprawienia swoich umiejętności w tym zakresie. Początkujący programiści popełniają lekkomyślne błędy, na przykład przekraczają początek lub koniec tablicy, stosują nieprawidłowe typy w w yw ołaniach funkcji lub (w przypadku języka C) wykorzystują nieprawidłowe znaki konwersji w funkcjach p r i n t f i scanf. Na szczęście błędy te są zwykle łatwe do wykrycia, ponieważ pow odują charakterystyczne awarie. Poza tym są łatwe do w yelim inow ania podczas pisania kodu. By ich uniknąć, wystarczy sprawdzać warunki graniczne, a przede wszystkim myśleć o tym, co może spowodować problemy,

AWK

157

już na etapie pisania. Błędy zazwyczaj występują w kodzie napisanym niedaw no oraz takim, który zaczęliśmy testować. Jest to zatem dobre miejsce do koncentracji wysiłków zmierzających do wyszukiwania błędów. Kiedy błędy stają się bardziej skomplikowane lub bardziej subtelne, ich wykrycie jest trudniejsze. Jednym ze skutecznych sposobów wykrywania błędów jest stosowanie zasady „dziel i zwyciężaj” . Polega ona na eliminacji z wyszukiwania części danych lub części kodu, tak aby błędy można było lokalizować w coraz to mniejszym obszarze. Często błędy można wykrywać według wzorca. „Numerologia” niepoprawnych danych wejściowych lub niepraw idłow ych danych w ynikowych często stanowi istotną wskazówkę tego, co może działać nieprawidłowo. Najtrudniejsze do wykrycia są błędy w przypadku, kiedy układamy sobie w myślach nieprawidłowy model sytuacji. Wtedy po prostu możemy wcale nie zobaczyć problemu. Wówczas zwykle robię sobie przerwę, czytam listing, opowiadam o problemie komuś innem u lub korzystam z debugera. Wszystkie te techniki pom agają mi zobaczyć problem w inny sposób. Często to wystarczy, by go wykryć. Niestety, debugowanie zawsze jest trudne. Najlepszym sposobem uniknięcia debugowania jest bardzo uważne pisanie kodu. W ja k i sposób zasoby sprzętowe wpływają na sposób myślenia programistów?

Brian: Posiadanie większych zasobów sprzętowych jest prawie zawsze pożądane — oznacza to na przykład, że nie trzeba się zbytnio martwić zarządzaniem pamięcią. Zawsze było to wielką bolączką, a 20 lub 30 lat temu (wtedy, kiedy pisaliśmy AWK) stanowiło jedno z głównych źródeł błędów. Brak problemów z zarządzaniem pamięcią oznacza możliwość używania potencjalnie niewydajnego kodu, zwłaszcza bibliotek ogólnego przeznaczenia. Faza wykonania programu stanowi bowiem dziś znacznie mniejszy problem niż 20 lub 30 lat temu. Na przykład dziś nie waham się przetwarzać za pomocą AWK plików o rozmiarze 10 lub nawet 100 MB. Dawniej było to bardzo trudne. W miarę powstawania coraz szybszych procesorów i pamięci o coraz większej objętości łatwiej w ykonuje się szybkie eksperymenty, a naw et pisze produkcyjny kod w językach interpretow anych (takich jak AWK). Kilka dekad tem u było to niewykonalne. Wszystko to jest wielkim zwycięstwem. Jednocześnie dostępność zasobów często prowadzi do bardzo rozbudow anych projektów i implementacji. Takie systemy m ogłyby być szybsze i łatwiejsze do wykorzystania, gdyby projektow ano je z uwzględnieniem pewnych ograniczeń. Z pewnością problem ten dotyczy nowoczesnych systemów operacyjnych. Wydaje mi się, że moje komputery uruchamiają się coraz dłużej, choć dzięki prawu Moore’a są one znacznie szybsze od starszych maszyn. Wszystkie te program y działające w systemie operacyjnym bardzo spowalniają działanie komputera.

1 58

ROZDZIAŁ

SZÓSTY

Jaka jest pana opinia o językach domenowych (ang. Domain-Specific Languages — DSL)?

Brian: M iałem okazję pracować nad wieloma językami, które dziś najczęściej są określane jako języki domenowe, choć ja zazwyczaj nazywałem je małymi językami (inni określają je jako języki specyficzne dla aplikacji). Idea tych języków jest taka, że dzięki skoncentrowaniu się na specyficznej i zwykle wąskiej dziedzinie można stworzyć składnię, która dobrze pasuje do tematyki. Dzięki tem u łatw o pisać kod, który rozwiązuje problemy z tej dziedziny. Istnieje wiele przykładów tego rodzaju języków — jednym z nich jest SQL. Sam AWK także jest doskonałym przykładem języka specyfikacji przetwarzania plików. Pozwala to robić w bardzo łatwy i zwięzły sposób. Największym problem em m ałych języków jest to, że wykazują one tendencję do rozrastania się. Jeśli są przydatne, ludzie chcą stosować je szerzej. W ten sposób pierwotny zakres zastosowań języka się rozszerza. To zazwyczaj implikuje dodawanie do języka now ych własności. Na przykład język pierw otnie może być czysto deklaracyjny (bez warunków if oraz pętli), może też być pozbawiony zmiennych lub wyrażeń arytmetycznych. Wszystkie one są jednak przydatne, zatem są dodawane. Kiedy zaś są dodawane, język się rozrasta (już nie jest mały). W ten sposób stopniowo zaczyna przypominać inne języki uniwersalne, tyle że z inną składnią, semantyką, a czasami także ze słabszą implementacją. Kilka spośród małych języków, nad którymi pracowałem, dotyczyło przygotowywania dokumentów. Pierwszy, który opracowałem wraz z Lorindą Cherry, nosił nazwę EQN i służył do składu wyrażeń matematycznych. Język EQN odniósł sukces, a ponieważ możliwości sprzętu do składu popraw iły się, dodatkow o opracowałem język do tw orzenia rysunków i diagramów, któremu nadałem nazwę PIC. Najpierw język PIC pozwalał tylko na rysowanie, ale szybko okazało się jasne, że będą potrzebne wyrażenia arytmetyczne do obsługi obliczeń na współrzędnych itp. Potrzebne były również zmienne do zapamiętywania wyników oraz pętle do tworzenia konstrukcji iteracyjnych. Wszystkie te m echanizm y dodano, ale były one niezgrabne i słabo wykonane. Na koniec możliwości języka PIC okazały się dość rozbudowane. Język stał się systemem zupełnym w rozumieniu Turinga (ang. Turing-complete), ale nikt nie chciał pisać w nim obszernego kodu. W ja k i sposób definiuje pan sukces w swojej pracy?

Brian: Nic nie daje takiej satysfakcji, jak sytuacja, w której ktoś mówi, że używał naszego języka bądź narzędzia i pom ogło m u ono lepiej wykonać jego pracę. Daje to napraw dę wielkie zadowolenie. Oczywiście czasami później pojawia się lista problem ów lub brakujących własności, ale przekazywanie tych informacji także jest cenne.

AWK

159

W jakich zastosowaniach język AWK w dalszym ciągu się przydaje?

Brian: AWK w dalszym ciągu wydaje się najlepszy do szybkiej, zgrubnej analizy danych: wyszukiwania wszystkich wierszy o określonych cechach, podsumowania pewnego aspektu danych czy też wykonania pewnych prostych transformacji. Często potrafię zrobić więcej za pomocą kilku wierszy kodu AWK niż inni za pomocą 5 lub 10 wierszy kodu w Perlu lub Pythonie. Z doświadczenia wiem, że mój kod działa prawie tak samo szybko. Mam kolekcję krótkich skryptów w AWK do w ykonyw ania takich operacji, jak dodawanie wszystkich pól we wszystkich wierszach czy też obliczenia dla zakresów pól (jest to sposób szybkiej analizy zbioru danych). Mam program w AWK, który wypełnia wiersze dowolnej długości do postaci wierszy składających się z co najwyżej 70 wierszy. Czasami używam go naw et 100 razy dziennie do porządkow ania wiadomości e-mail i podobnych informacji. Każdy ze w spom nianych programów mógłby z łatwością być napisany w jakimś innym języku skryptowym i działałby równie sprawnie, ale napisanie ich w AWK było łatwiejsze. 0 czym powinni pamiętać ludzie piszący programy w AWK?

Brian: Język został zaprojektowany z myślą o pisaniu program ów o rozmiarach jednego bądź dwóch wierszy. AWK nie najlepiej nadaje się do pisania większych programów, ponieważ nie posiada mechanizmów, które pomagają w przetwarzaniu takiego kodu. Niektóre decyzje projektowe m ogą utrudnić wyszukiwanie błędów — na przykład fakt braku deklaracji zmiennych i ich automatycznej inicjalizacji jest bardzo wygodny dla jednowierszowych programów. Oznacza to jednak, że w dużych programach błędy w pisowni oraz literówki stają się niewykrywalne.

Projektowanie nowego języka W ja k i sposób zabrałby się pan do tworzenia nowego języka programowania?

Brian: Prawdopodobnie jest jakiś zbiór zadań, jakaś dziedzina aplikacji, dla których sądzimy, że nowy język programowania będzie lepszy od każdego z istniejących języków Trzeba spróbować pomyśleć, co ludzie będą chcieli wyrazić za pomocą nowego języka. Do jakich problemów lub jakich aplikacji będzie używany nowy język programowania? Jaki jest pożądany sposób opisywania tych problemów w nowym języku? Jaki byłby najbardziej naturalny sposób zapisywania kodu? Jakie są najważniejsze przykłady? Przykłady najprostsze, które umożliwiają rozpoczęcie pracy z językiem? Należy dążyć do tego, aby były one jak najbardziej oczywiste. Zasadniczą ideą jest próba pisania kodu w języku, zanim ten język powstanie. Trzeba się zastanowić, w jaki sposób najlepiej wyrazić określony problem. Myślę, że powyższe zasady dość dobrze pasują do języka AWK, poniew aż wszystko, co zostało uwzględnione w projekcie tego języka, miało na celu ułatwienie pisania użytecznych

160

ROZDZIAŁ

SZÓSTY

programów bez konieczności zbytniego rozpisywania się. Oznaczało to, że nie mieliśmy deklaracji częściowo z tego powodu, że nie mieliśmy typów. Oznaczało też, że nie mieliśmy jawnych instrukcji do w prow adzania danych, ponieważ wprowadzanie danych było całkowicie niejawne — po prostu się działo. Oznaczało, że nie mieliśmy instrukcji do dzielenia danych wejściowych na pola, ponieważ to działo się automatycznie. Wszystkie cechy języka wywodziły się z postawionego celu: próby stworzenia języka, za pomocą którego łatwo będzie powiedzieć bardzo proste rzeczy. Standardowe przykłady użyte w białej księdze języka AWK, podręczniku i innych dokum entach zwykle składały się z pojedynczego wiersza. Jeśli m oim celem było wyświetlenie wszystkich wierszy, których długość przekraczała 80 znaków, to wystarczyło napisać "length > 80". W tym konkretnym języku było dość oczywiste to, co próbowaliśmy osiągnąć. Później oczywiście zdarzało się, że odkrywaliśmy jakieś braki — na przykład możliwość czytania informacji z określonych plików wejściowych identyfikowanych przez nazwę. W związku z tym byliśmy zmuszeni do dodawania nowych własności. Konstrukcje potrzebne do tworzenia programów dłuższych niż kilka wierszy — na przykład funkcje — zostały dodane później. Język EQN, który opracowaliśmy wspólnie z Lorindą Cherry, to całkowicie odmienny przykład. EQN jest językiem do opisywania wyrażeń matematycznych w taki sposób, aby mogły one być wyświetlone. Celem było stworzenie języka, który byłby w m aksym alnym stopniu zbliżony do sposobu czytania wyrażeń matematycznych przez ludzi. Co powiedziałbym, gdybym chciał opisać komuś wzór przez telefon? Co m ów iłbym podczas zapisywania składowych wyrażeń wzoru na tablicy w klasie? Kiedyś nagrywałem książki dla osób niewidom ych. Zastanaw iałem się, w jaki sposób pow inienem czytać wyrażenia matematyczne, aby ktoś, kto nie widzi, potrafił je zrozumieć. Język EQN był w całości skupiony na ułatwieniu zapisywania wyrażeń matematycznych podczas ich czytania. W języku nie przejmowaliśmy się zbytnio jakością wyjścia. Pod tym względem język EQN różnił się od języka TeX, który nie jest tak łatwy do pisania, ma wiele konstrukcji składniowych, ale jest potężnym językiem dającym znacznie większą kontrolę nad wyjściem. W języku TeX dodatkow e możliwości uzyskano kosztem łatwości posługiwania się językiem — stało się ono trudniejsze. Czy podczas projektowania języka poświęcał pan dużo czasu na myślenie o

implementacji?

Brian: Dość dużo, poniew aż zawsze byłem zaangażow any zarów no w projekt, jak i implementację. Jeśli nie wiem, jak coś zaimplementować, nie próbuję uwzględniać tego w projekcie. Prawie wszystkie języki, które opracowałem, albo miały na tyle prostą składnię, że można je było parsować za pomocą prostego parsera, albo miały bogatszą składnię, ale mogłem sprecyzować gramatykę za pomocą programu yacc.

AWK

161

Myślę, że bez wykorzystania narzędzia yacc takie języki jak EQN lub AWK nigdy by nie powstały, ponieważ ręczne pisanie parserów jest bardzo trudne. Nie oznacza to, że nie m ożna tego zrobić, ale jest to bardzo kłopotliw e. Pisanie języków z w ykorzystaniem takich narzędzi jak yacc pozwala na łatwe wykonywanie interesujących operacji i szybkie wprowadzanie zmian w projekcie, jeśli coś wyda się nieprawidłowe. Wystarczy jedynie zmodyfikować trochę gramatykę. Aby zasadniczo zmienić język lub dodać nową własność, nie trzeba wprowadzać żadnych istotnych zm ian w kodzie. W przypadku użycia takiego narzędzia jak yacc w prow adzanie zm ian było napraw dę łatwe. Byłoby znacznie trudniejsze, gdybym posługiwał się konwencjonalnym, rekurencyjnym parserem zstępującym. Czy projektanci języków powinni wymuszać jakiś preferowany styl w celu uniknięcia powtarzających się pomyłek? Na przykład formatowanie kodu źródłowego w stylu Pythona lub brak arytmetyki na wskaźnikach typowy dla Javy?

Brian: W odniesieniu do tej kwestii mam mieszane uczucia. Jeśli coś jest wymuszone, to przydaje się dopiero wtedy, kiedy ktoś się do tego przyzwyczai. Reguły wcięć w Pythonie początkowo wydały mi się irytujące, ale kiedy się do nich przyzwyczaiłem, przestały sprawiać mi problem. Przy projektow aniu języka należy dążyć do tego, aby był on wyposażony we właściwe konstrukcje pozwalające ludziom powiedzieć to, co chcą powiedzieć, bez dwuznaczności. Nie pow inno być również zbyt wielu sposobów na wyrażenie tego samego. Niezależnie od okoliczności ludzie zawsze znajdą najbardziej naturalny sposób wyrażania myśli. Jeśli zatem w Javie nie ma wskaźników — co jest znaczącą zm ianą w porów naniu z C lub C++ — to są referencje, które w wielu sytuacjach stanowią rozsądną alternatywę dla wskaźników. W Javie nie ma również instrukcji goto. Nigdy nie uważałem tego za problem. W języku C istnieje instrukcja goto, której zwykle nie używam, ale raz na jakiś czas skorzystanie z niej ma sens. Tak czy owak, potrafię dostosować się do przyjętych decyzji projektowych. W spom niałem o języku PIC do tw orzenia rysunków. Był on dobry do prostych rysunków, takich jak strzałki, ram ki i diagramy. Ludzie chcieli jednak tworzyć rysunki za pom ocą regularnych struktur. Aby spełnić oczekiwania tych osób, dość niechętnie dodałem pętle whi le i for, a nawet instrukcję if. Oczywiście konstrukcje te powstały później i miały dość nieregularną składnię, trochę niepasującą do języka PIC, ale pom im o to różniły się od konstrukcji spotykanych w konw encjonalnych językach program ow ania. W efekcie pow stały przydatne, choć dość niezgrabne konstrukcje. Zwykle język najpierw jest prosty, a potem zaczyna się rozrastać. Wprowadzane dodatki stopniowo nadają m u charakter kompletnego języka programowania zawierającego zmienne, wyrażenia, instrukcję if, pętlę whi 1e oraz wszystkie inne funkcje występujące

1 62

ROZDZIAŁ

SZÓSTY

w językach programowania. Zazwyczaj jednak konstrukcje te są niezgrabne. Mają nieregularną lub co najmniej inną składnię. Czasami mechanizm nie działa prawidłowo, a całość sprawia złe wrażenie. Czy jest to spowodowane tym, że języki te powstały jako małe i nie przewidziano ich ewolucji do postaci języków ogólnego przeznaczenia?

Brian: Myślę, że dokładnie o to chodzi. Mówię to tylko w swoim własnym imieniu, ale kiedy przystępowałem do projektowania języka, zwykle miałem na myśli bardzo proste narzędzie — nie miałem zamiaru tworzyć języka do pisania skomplikowanych program ów . W żadnym razie nie m iał być to język program ow ania ogólnego przeznaczenia. Jeśli jednak język jest przydatny, użytkownicy z czasem osiągają kres jego możliwości i chcą coraz więcej. Zwykle dążą do wprowadzenia własności języków program ow ania ogólnego przeznaczenia — takich, dzięki którym język staje się program ow alny, a nie wyłącznie deklaracyjny. Chcą konstrukcji pozwalających na powtarzanie operacji, tak aby można było uniknąć konieczności wielokrotnego powtarzania tego samego. To prowadzi do stworzenia pętli, makr lub funkcji. W ja k i sposób zaprojektować język, który będzie spełniał oczekiwania każdego użytkownika? Wspominał pan o małych językach skoncentrowanych na konkretnym celu. Mam jednak wrażenie, że podoba się panu idea pisania języka przez programistów w celu spełnienia swoich własnych potrzeb. Kiedy już mamy język, który działa, to w jaki sposób możemy go rozszerzyć, aby stał się przydatny dla innych osób?

Brian: Nie wydaje się prawdopodobne, aby kiedykolwiek powstał język, z którego wszyscy byliby zadowoleni — taki, który nadaje się do każdego zastosowania lub nawet stosunkowo obszernej grupy w obrębie dużej kolekcji aplikacji. Obecnie mamy wiele dobrych języków ogólnego przeznaczenia. Język C sprawdza się w pewnych zastosowaniach. Języki C++, Java, Python — każdy jest dobry w swoim obszarze zastosow ań i m ożna go wykorzystać niem al w każdej innej dziedzinie. Nie chciałbym jednak pisać systemu operacyjnego w Pythonie. Nie chcę też pisać w języku C kodu przetwarzania tekstu. W ja k i sposób rozpoznaje pan obszar, w którym język je st szczególnie przydatny, oraz jakie są jego silne strony? Powiedział pan na przykład, że Python nie jest dobry do pisania systemów operacyjnych. Czy jes t to cecha charakterystyczna języka, czy implementacji?

Brian: Myślę, że i jednego, i drugiego. Z powodu implementacji niektóre mechanizmy m ogą działać zbyt wolno. Gdybym jednak pisał system operacyjny dla zabawy lub tworzył jego wersję demo, to myślę, że Python nadaw ałby się całkiem dobrze. Pod pewnymi względami mógłby być lepszy od innych. Nie sądzę jednak, że podjąłbym się napisania w Pythonie systemu operacyjnego, który na przykład obsługiwałby infrastrukturę serwisu Google.

AWK

163

Prawdziwi programiści czasami nie m ają luksusu wyboru. Muszą robić to, czego wymaga lokalne środowisko. Jeśli zatem jestem programistą zajmującym się obsługą dużych operacji finansowych na Wall Street, muszę programować w określonym języku lub w języku z pewnego ograniczonego zbioru — najczęściej w C++ i Javie. Nie mam zamiaru mówić: „Chcę to napisać w C” lub „Myślę, że do tego zadania Python byłby lepszy” . W iedziałem, że w pewnej firmie wykorzystywano C++, Javę i Pythona. Do pisania niektórych program ów lepiej nadaw ałby się język Ruby, ale nie było możliwości pisania w tym języku. Wiele osób nie ma wolnego wyboru języka, w którym pisze. Z drugiej strony, jeśli mają do wykonania określone zadanie, mogą mieć możliwość wybierania języka z określonej grupy, na przykład C++, Javy lub Pythona. W takim przypadku można wziąć pod uwagę względy techniczne i na tej podstawie zdecydować, jakiego języka użyć. Być może każdy powinien mieć do dyspozycji osobisty język?

Brian: Taki, który należy tylko do jednej osoby i nikt inny go nie używa? Taki, w którym każdy dysponuje własną składnią, która jest translowana na ogólny kod bajtowy. Po dokonaniu tej konwersji staje się on uniwersalny.

Brian: Myślę, że utrudniłoby to pracę zespołową. © Co należy robić po napisaniu pierwszego prototypu?

Brian: Najpierw należy spróbować samodzielnie napisać kod w tym proponowanym języku. Trzeba się dowiedzieć, jak to jest, kiedy pisze się coś, co osobiście chce się napisać i co będą chcieli pisać ludzie z naszego kręgu. W przypadku języka EQN było to bardzo czytelne. Jakim językiem mówią matematycy? Nie jestem matematykiem, ale m iałem na to dość jasny pogląd, poniew aż uczestniczyłem w wielu kursach matematyki. Chcesz jak najszybciej używać języka sam, a następnie chcesz, by inni spróbowali go użyć. Najlepiej by było, gdyby użytkow nicy języka byli dobrymi krytykami — czyli aby spróbowali używać wszystkich mechanizmów i powiedzieli, co o nich sądzą. Jedną z doskonałych cech instytutu Bell Labs w latach siedemdziesiątych i osiemdziesiątych było to, że w grupie pracującej nad systemem Unix oraz wokół niej znajdowało się wiele osób, które były bardzo dobre we wspomnianej krytycznej ocenie pracy innych osób. Krytycyzm często był bardzo łagodny, ale dzięki niemu można było szybko uzyskać informacje o tym, co było dobre, a co nie. Wszyscy na tym korzystaliśmy, ponieważ krytyka pomagała wygładzać ostre krawędzie, dzięki krytyce systemy były ze sobą zgodne, a napraw dę złe pomysły zostały wyeliminowane.

164

ROZDZIAŁ

SZÓSTY

Myślę, że dziś znacznie trudniej osiągnąć taki stan. Dzięki internetowi można uzyskać krytykę od szerszej grupy ludzi znacznie bardziej rozproszonych, ale może się zdarzyć, że nie będzie to szczegółowa krytyka osób niezwykle utalentow anych, z którym i jesteśmy blisko — takich, które m ożna spotkać co godzinę na korytarzu przy w chodzeniu i wychodzeniu z pokoju. W ja k i sposób udało się panu pogodzić rozwijanie swoich pomysłów i

eksperymentowanie z tworzeniem unikatowego i stabilnego systemu?

Brian: Nie było to takie trudne, ponieważ język AWK był bardzo mały. Pierwsza wersja składała się zaledwie z około 3000 wierszy kodu. Jeśli dobrze pamiętam, pierwszą wersję napisał Peter Weinberger. Gramatyka została opracowana za pomocą narzędzia yacc. Była bardzo prosta. Część leksykalną zrealizowano przy użyciu programu lex. Semantyka w tam tym m omencie była dość regularna. Dysponowaliśmy kilkoma różnymi wersjami maszyny interpretera, ale nie były one zbyt rozbudowane. Dość łatwo można było wprowadzać zmiany i utrzymać nad nimi kontrolę. W rzeczywistości język w dalszym ciągu jest dość niewielki. Wersja, którą rozprowadzam, nie ma wiele ponad 6000 wierszy — dziś, 30 lat później. Czy to prawda, że każdy z was musiał napisać moduły testowe dla każdej nowej własności, którą chcieliście uwzględnić?

Brian: Nie, absolutnie nie. Mniej więcej w tym okresie, kiedy opublikowano książkę (1988 rok), zaczęliśmy bardziej systematycznie dobierać przypadki testowe. Kilka lat później zacząłem uważniej dobierać testy. W tam tym okresie zdecydowałem, że po dodaniu jakiejś własności dodam kilka testów, które będą sprawdzały, czy ona rzeczywiście działa. Nie dodaw ałem now ych własności przez dość długi czas, ale kolekcja testów w dalszym ciągu się rozrastała, ponieważ kiedy ktoś znalazł błąd, dodawałem jeden test lub dwa testy, które wcześniej spowodowałyby powstanie błędu. Dzięki temu zyskiwałem pewność, że błąd nie powróci. Myślę, że to dobra praktyka. Żałuję, że nie stosowaliśmy jej bardziej uważnie i bardziej systematycznie znacznie wcześniej. Zestaw testów obejmuje dziś praktycznie wszystkie program y z pierwszego i drugiego rozdziału książki o AWK. Ale te zostały dodane dopiero po wydaniu książki, która pojawiła się dużo później niż sam język. W ciągu ostatnich 40 lat było prowadzonych wiele badań w informatyce, które dotyczyły także języków programowania. Czy poza lepszymi narzędziami zauważył pan postęp w projektowaniu języka?

Brian: Nie wiem, czy mam wystarczającą wiedzę, by udzielić prawidłowej odpowiedzi na to pytanie. W przypadku niektórych języków, na przykład języków skryptowych, proces projektow ania języka w dalszym ciągu jest dość specyficzny. Bazuje na preferencjach, zainteresowaniach i poglądach osoby projektującej język. Mamy dziesiątki języków skryptowych. Nie sądzę, aby ich powstanie było bezpośrednim efektem badań w takiej dziedzinie, jak teoria typów.

AWK

1 65

W latach siedemdziesiątych było dostępnych wiele typów języków programowania. Języki C i Smalltalk bardzo się od siebie różnią. Dziś także mamy bardzo różne języki, ale w dalszym ciągu są język i C, C + + i Smalltalk. Czy spodziewa się pan więcej nowatorstwa i większego postępu w sposobach projektowania języków i interakcji z komputerami?

Brian: Myślę, że nie wiem wystarczająco dużo o całej dziedzinie. Sądzę, że praw dopodobnie osiągniemy postęp w tym, aby komputery wykonywały za nas więcej pracy. Oznacza to możliwość powstania języków jeszcze wyższego poziomu i jeszcze bardziej deklaracyjnych. Dzięki nim nie będziemy musieli opisywać tak wielu szczegółów. Można mieć nadzieję, że języki staną się bezpieczniejsze, przez co trudniej będzie pisać programy, które nie działają. Być może będą to języki, które łatwiej będzie tłumaczyć na bardzo wydajną postać wykonalną. Poza tym jednak naprawdę nie wiem, jak będzie. Czy może powstać nauka zajmująca się projektowaniem języków? Czy do projektowania języka można podejść za pomocą metod naukowych, tak by można się było uczyć z poprzednich odkryć lub wynalazków i w ten sposób osiągać postęp? Czy projekt języka zawsze będzie uwzględniał osobisty gust projektanta?

Brian: Myślę, że w projekcie języka zawsze będzie doza osobistego gustu i intuicji projektanta. Prawie wszystkie języki są w rzeczywistości produktem jednej, dwóch, a może trzech osób. T rudno znaleźć taki język, który pow stałby w wyniku pracy grupowej. To mówi samo za siebie — istnieje prawdopodobieństwo, że język będzie odzwierciedlał indywidualne upodobania. Jednocześnie nasze rozumienie prawie wszystkich aspektów języków programowania jest lepsze i jak m ożna się spodziewać, w dalszym ciągu będzie się poprawiać. M ożna stąd wnioskować, że nowe języki będą bazow ały na zdrowych zasadach, a ich właściwości będą dobrze rozum iane. W tym sensie projekt języka będzie w większym stopniu oparty na naukowych podstawach, niż było to 10, 20 czy 30 lat temu, kiedy większość decyzji projektowych nie miała żadnych podstaw. Pomimo wszystko uważam, że znaczna część projektu języka w dalszym ciągu będzie zdeterminowana indywidualnym gustem. Projektanci wymyślają konstrukcje, które do nich przemawiają. N iektóre z nich przemawiają także do większości innych osób. Będzie jednak coraz więcej obowiązkowych elem entów języka, takich, których istnienie będzie uznaw ane za gw arantow ane. A zatem na przykład każdy znaczący współczesny język musi posiadać pewne m echanizm y program ow ania obiektowego. M echanizm ten musi być uwzględniony od początku, a nie doklejony później. Innym ważnym obszarem jest współbieżność. Ponieważ współczesne komputery mają wiele procesorów, to języki będą musiały obsługiwać współbieżność na poziomie samego języka, a nie w ramach jakiejś dołączonej biblioteki.

1 66

ROZDZIAŁ

SZÓSTY

W miarę coraz lepszego rozumienia zagadnień próbujemy budować większe systemy. W związku z tym pod pewnymi względami zyskujemy większe możliwości, ale zawsze próbujemy podnosić poprzeczkę coraz wyżej. Tworzymy coraz większe zespoły.

Brian: Myślę, że te spostrzeżenia są jak najbardziej prawdziwe. Zawsze staramy się robić większe rzeczy, dlatego ciągle mamy ręce pełne roboty. Staramy się robić większe rzeczy, ponieważ mamy dostęp do coraz nowszego sprzętu, lepiej rozumiemy sposoby, w jakie pisze się programy, oraz dysponujemy lepszymi językami programowania. Zadania, które w latach siedemdziesiątych wym agały roku lub dwóch lat pracy zespołu złożonego z kilkuset osób, dziś potrafi wykonać student w kilka tygodni. Dzieje się tak dlatego, że istnieje obszerne wsparcie, rozbudow ana infrastruktura, a kom putery mają olbrzymią moc obliczeniową. Nie bez znaczenia jest również to, że istnieje tak wiele programów, które można wykorzystać. Myślę zatem, że zawsze będziemy starali się wykorzystywać możliwości do maksimum. Czy będziemy pracow ać w wielkich zespołach złożonych z kilku tysięcy osób — podobnych do tego, jaki firma Microsoft wykorzystała do pracy nad systemem Vista? Prawdopodobnie tak, ale z całą pewnością będziemy potrzebowali sposobów podziału wielkich projektów na szereg małych projektów współpracujących ze sobą w bezpieczny i właściwie zorganizowany sposób. N iektóre z tych zadań będą wymagały uspraw nień w językach, a inne lepszych mechanizmów scalania ze sobą komponentów niezależnie od tego, w jakich językach były napisane. Będą również potrzebne mechanizmy tworzenia pakietów złożonych z informacji przechodzących przez interfejsy. Wcześniej powiedział pan, że nowoczesny język programowania musi koniecznie umożliwiać programowanie obiektowe. Czy techniki obiektowe są dobre w takiej postaci, w ja k ie j są dostępne? Czy istnieje cokolwiek innego, co można by zrobić, wynaleźć lub dodać, aby uprościć proces budowania dużych systemów?

Brian: Programowanie obiektowe jest bardzo przydatne w niektórych okolicznościach. Gdy piszemy w Javie, nie m am y innego w yboru. W Pythonie lub C++ możemy korzystać z programowania obiektowego bądź nie. Uważam, że to prawidłowy model: używamy program ow ania obiektowego lub nie w zależności od aplikacji. W miarę rozwoju języków programowania z pewnością pojawią się inne mechanizmy pakowania jednostek obliczeniowych i organizowania programów. Obiektowy model kom ponentów firmy Microsoft COM bazuje na programowaniu obiektowym. Jest to jednak coś więcej niż program ow anie obiektowe, ponieważ komponent to zbiór obiektów, a nie tylko jeden obiekt. Jak obsługiwać go w bardziej uporządkow any sposób, niż pozwala na to model COM, tak by m ożna było lepiej odzwierciedlić sposób powiązania obiektów między sobą? Potrzebny jest mechanizm obsługi dużej liczby obiektów. W miarę korzystania z coraz większych programów lub programów, których elementy pochodzą z większej liczby miejsc, korzystamy z coraz bardziej skomplikowanych struktur obiektów.

AWK

167

Unix korzystał z języka C, który nie obsługiwał obiektów. Przy użyciu języka C można tworzyć komponenty — obiekty — jako niewielkie narzędzia, które można łatwo ze sobą łączyć w celu tworzenia bardziej zaawansowanych własności. Zamiast uwzględniać koncepcje obiektów wewnątrz języka, być może należałoby budować obiekty (lub inaczej komponenty) ja ko niewielkie narzędzia będące osobnymi programami. Jeśli weźmiemy pod uwagę arkusz kalkulacyjny, to — ogólnie rzecz biorąc — jest to duży, składający się z obiektów program, który być może obsługuje wtyczki bądź dodatki. Idea jest jednak taka, że obiekty są wewnątrz. Są zintegrowane i zarządzane przez środowisko języka.

Brian: Excel to dobry przykład, ponieważ zawiera w sobie wiele obiektów oraz powiązanych z nimi metod i właściwości. Można napisać kod zarządzający Excelem. W efekcie Excel staje się gigantyczną procedurą lub dodatkow ym m odułem obliczeniowym. Połączenia nie są co prawda tak zgrabne jak na przykład w potokach Uniksa, ale dość dobrze je przypominają. Wykorzystanie Excela jako elementu potoku nie powinno być zbyt trudne. Podobną architekturę tworzą aplikacje mashup: stanowią duże bloki budulcowe, które można ze sobą scalać w sposób adekwatny do potrzeb. Nie robi się tego tak łatwo jak w przypadku potoków Uniksa, ale idea jest taka sama: łączenie dużych, samodzielnych elementów w większe systemy. Serwis Yahoo! Pipes to dobry przykład. Jest to interesujące podejście do kwestii: w jaki sposób m ożna scalić ze sobą stosunkow o złożone operacje? Aplikacja jest wyposażona w interesujący fronton graficzny, ale można sobie wyobrazić, że te same operacje dałoby się zrealizować za pomocą mechanizmów tekstowych. W ten sposób m ożna stworzyć system, który pozwala na wykonyw anie dow olnych obliczeń poprzez pisanie tekstowych programów. Z całą pewnością warto poczynić wysiłki, aby odpowiedzieć sobie na pytania, jak dobrze tworzyć tego rodzaju systemy, jak wydajnie tworzyć systemy z istniejących kom ponentów oraz jak wykorzystać języki programowania do wspomagania tego procesu. W erze wiersza polecenia komunikacja z komputerem musiała odbywać się za pomocą pisanego języka: wprowadzania tekstowych danych wejściowych i czytania tekstu na wyjściu. Dziś komunikujemy się z komputerem, wykorzystując klawiaturę, ale używamy do tego także myszy. Komputer generuje w części graficzne, a w części tekstowe wyjście. Czy język w dalszym ciągu jest najlepszym sposobem komunikacji z komputerem? Czy wiersz polecenia był w pewien sposób lepszym sposobem komunikacji z powodu wykorzystania języka?

Brian: Graficzne interfejsy są bardzo dobre dla użytkowników o niskim poziomie wiedzy technicznej, początkujących użytkow ników systemu, aplikacji, których nie używa się zbyt często, a także dla aplikacji o graficznym charakterze — na przykład do tw orzenia dokum entów . Jednak po pew nym czasie zaczynamy dostrzegać,

1 68

ROZDZIAŁ

SZÓSTY

że w ielokrotnie pow tarzam y tę samą czynność. Kom putery idealnie nadają się do wykonywania powtarzających się operacji. Czy nie byłoby lepiej, gdyby można było zlecić komputerowi wykonanie tej czynności jeszcze raz? W spółczesne aplikacje są wyposażone w podobne mechanizmy. Właśnie taką rolę spełniają makra w Wordzie lub Excelu. Istnieją również programowalne API dla takich systemów, jak Google, Yahoo!, Amazon lub Facebook. M ożna zautom atyzow ać wszystkie operacje, które są możliwe do wykonania za pomocą klawiatury i myszy. Co więcej, nie trzeba przy tym korzystać z techniki screen scraping oraz parsowania HTML, tak jak miało to miejsce 10 lat wcześniej. W efekcie sprowadza się to do powrotu do wiersza polecenia, gdzie najlepsze są czyste operacje tekstowe. Na początku, dopóki nie w ykona się pewnej liczby operacji za pomocą klawiatury i myszy, można nie wiedzieć, jakie operacje oferuje aplikacja. Kiedy jednak już dostrzeżemy powtarzające się operacje, wtedy interfejs wiersza polecenia i interfejsy API pozwalają na automatyzację tych czynności i zwalniają człowieka z brania udziału w wykonywaniu tej pętli. Czy podczas projektowania języka myślał pan o zapewnieniu możliwości debugowania? Jedna z krytycznych uwag na temat języka AWK dotyczyła automatycznej inicjalizacji zmiennych — czyli bez deklaracji. Jest to wygodne, ale w przypadku popełnienia błędu w pisowni lub literówki znalezienie błędu może być bardzo trudne.

Brian: Zawsze trzeba zdecydować się na jakiś kom prom is. W każdym języku są kompromisy, a w języku AWK zdecydowaliśmy, że najważniejszą cechą języka ma być łatwość posługiwania się nim. Celem była możliwość tworzenia programów składających się z jednego wiersza. Przewidywaliśmy bowiem, że większość programów będzie składała się z jednego lub dwóch wierszy. Do tego założenia pasowały zmienne, które nie są deklarowane i którym automatycznie zostają nadane wartości początkowe. Gdyby trzeba było zadeklarować zmienne, a potem je zainicjować, rozmiar programu uległby potrojeniu. Przyjęta koncepcja doskonale sprawdziła się dla niewielkich programów, ale dla dużych programów jest zła. A zatem co można z tym zrobić? W Perlu jest tryb, który ostrzega użytkownika. Można go opisać słowami: „Powiedz mi, jeśli zrobię coś głupiego” . Większą ostrożność m ożna by zachować, gdyby zastosować sposób używany w Pythonie. W Pythonie trzeba inicjować zmienne, ale zwykle można obyć się bez specjalnych deklaracji. Można by również zastosować osobne narzędzie działające obok programu AWK, które mogłoby komunikować: „Masz dwie zm ienne o bardzo podobnych do siebie nazwach, czy napraw dę o to ci chodziło?” . Decyzją projektową znacznie gorszą od braku deklaracji w AWK jest to, że konkatenację wyraża się poprzez sąsiadowanie ze sobą dwóch wartości. Nie istnieje jawny operator konkatenacji. Jeśli w programie występuje kilka wartości obok siebie, to są one scalane

AWK

1 69

ze sobą. Jeśli połączymy to z faktem braku deklaracji zmiennych, spowodujemy, że niemal wszystko, co się napisze, będzie poprawnym programem w AWK. Popełnianie błędów jest zbyt łatwe. Myślę, że to jest przykład niezbyt mądrej decyzji projektowej. Nic na niej nie zyskaliśmy — pow inniśm y byli skorzystać z operatora. Zm ienne inicjowane autom atycznie były świadomym kom prom isem projektow ym , który świetnie się sprawdzał dla niewielkich programów.

Kultura tradycji Przypuśćmy, że chcę napisać nowy, niewielki język, który ma działać w dwóch megabajtach pamięci, w telefonie komórkowym lub urządzeniu wbudowanym. Do jakiego stopnia szczegóły takiej implementacji wpływają na poziom interfejsu? Czy użytkownik korzystający z mojego programu musi rozumieć pewne moje decyzje projektowe, czy też odeszliśmy od tego rodzaju ograniczeń?

Brian: Sądzę, że jesteśmy znacznie dalej od takich ograniczeń, niż byliśmy dawniej. Jeśli spojrzymy na historię wczesnych programów uniksowych i oczywiście na język AWK, zauważymy wiele miejsc, w których fakt ograniczonego rozm iaru pamięci ujawniał się w języku lub w różnych fragmentach systemu operacyjnego. Na przykład przez wiele lat w języku AWK obowiązywały wewnętrzne ograniczenia: można było otworzyć tylko określoną liczbę plików, tablica asocjacyjna mogła mieć tylko pewną liczbę elementów itd. Te ograniczenia wynikały z niewielkich rozmiarów pamięci oraz z tego, że procesy nie były zbyt szybkie. Stopniowo wszystkie te ograniczenia przestały istnieć. W mojej implementacji nie ma już limitów ustalonych na sztywno. Takie limity to miejsca, w których ujawniają się ograniczenia zasobów i stają się widoczne dla użytkowników. Język AWK próbował chronić stan zmiennych. Jeśli zmienna została użyta jako liczba, a następnie przekształcono ją na tekst w celu wyświetlenia, to AWK wiedział, że obie wartości: num eryczna i znakowa były aktualne. Dzięki tem u nie było potrzeby ponownego przeprowadzania konwersji. W nowoczesnych maszynach działających 1000 razy szybciej nie ma potrzeby uciekania się do takich sztuczek. Kiedy wartość jest potrzebna, po prostu przeprowadza się konwersję. Wykonywanie takich sztuczek nie było zbyt dobre nawet dawniej. Dlatego właśnie istnieje tak dużo zawiłego kodu, praw dopodobnie nie zawsze prawidłowego, do ich obsługi. Gdybym dziś projektował język, w ogóle bym się tym nie przejmował. Jestem pewien, że w Perlu lub Pythonie wcale nie obsługiwano tego stanu.

170

ROZDZIAŁ

SZÓSTY

Co ciekawe, w Perlu 5 w dalszym ciągu wykorzystuje się tę sztuczkę.

Brian: Pierwsza wersja Perlą została napisana mniej niż 10 lat po języku AWK. Wtedy ciągle istniało wiele ograniczeń zasobów. Tak czy inaczej, przytoczone przykłady pokazują sytuacje, w jakich ścisłe ograniczenia zasobów zmuszały do wykonywania operacji, które z perspektywy czasu prawdopodobnie byłyby wykonane inaczej. Ja zaczynałem na maszynach, które — o ile pamiętam — miały w sumie 64 kB pamięci. W tamtych czasach byliśmy mocno związani ze środowiskiem Uniksa. Peter Weinberger powiedział, że w początkowych latach Uniksa zawsze można było napisać program od nowa w ciągu roku. Nie musiało to być zrobione perfekcyjnie, ponieważ programy nie były zbyt skomplikowane. Zawsze można je było napisać od nowa. Czy stosował pan tę praktykę?

Brian: Programy dość często były przepisywane. Nie m am pojęcia, czy były przepisywane od podstaw. Osobiście nie przypom inam sobie sytuacji, abym kiedykolwiek przepisywał program w takim sensie, że coś wyrzucałem i zaczynałem całkowicie od początku. Zmiany w moich programach miały charakter przyrostowy. Było jednak wiele miejsc, które trzeba było przemyśleć od nowa. Do dobrego tonu należało szukanie miejsc, w których m ożna było zmniejszyć objętość program u. Podczas rozmowy z nim odniosłem wrażenie, że była to sprawa subkultury. Nigdy nie zakładano w projekcie, że program będzie wykorzystywany przez 10, 2 0 czy 4 0 lat? Czy zauważył pan zwrot od myślenia krótkoterminowego do długoterminowego?

Brian: Nie wiem, czy dziś ktokolwiek myśli długofalowo o oprogramowaniu, ale były osoby, które dawniej to robiły. Niektórych zmuszało do tego życie. Było tak na przykład w starych, dobrych czasach, kiedy pracowałem w firmie telekomunikacyjnej. Wtedy kod musiał działać przez długi czas. Podczas wymiany oprogramowania trzeba było zadbać, aby był zgodny z kodem, który działał wcześniej. W związku z tym wszystkie zmiany wprowadzano ostrożniej. Być może byliśmy większymi realistami, jeśli chodzi o możliwość pisania kodu od początku. Nie było na to wystarczająco dużo czasu. Inna rzecz, że w świecie Uniksa lat siedemdziesiątych istniało tak wiele interesujących nowych rzeczy, że programiści chętnie modyfikowali swoje programy. Nie sądzę, aby ktokolwiek myślał o tym, że pisze programy, które będą trw ały wieki. Gdyby w 1978 roku ktoś powiedział mnie lub Alowi, że będziemy rozmawiać o AWK 30 lat później, nie uwierzylibyśmy.

AWK

171

Jądro Uniksa bardzo mocno się zmieniło. Wiele osób chciałoby, by było inaczej, ale język C w dalszym ciągu jest jednym z najlepszych narzędzi do tworzenia takich programów ja k AWK lub jądra systemów operacyjnych. Dlaczego niektóre programy potrafią przetrwać, a inne nie?

Brian: To, że programy trwają, w części wynika z tego, że są naprawdę dobre w swoim obszarze zastosowania. Język C świetnie nadaje się do im plem entacji systemów operacyjnych. Jest bardzo ekspresywny, a jednocześnie nie jest skomplikowany ani zbyt rozbudowany. Jest również wydajny, a to zawsze ma znaczenie. To przyjemny język do pracy, ponieważ jeśli chce się coś wyrazić, nie istnieje zbyt wiele różnych sposobów, aby to zrobić. Mogę spojrzeć na czyjś kod i powiedzieć: „Widzę, co chcesz zrobić” . Nie sądzę, aby to samo udało się w przypadku takich języków, jak Perl czy C++. Gdybym spojrzał na czyjś kod w Perlu, niewiele bym z niego zrozumiał, ponieważ w Perlu istnieje wiele sposobów wyrażania tego samego. C++ jest rozbudowany i zawiły. Istnieje bardzo wiele różnych sposobów wyrażania tego samego. Gdybyśmy obaj pisali w C++, moglibyśmy wymyślić zupełnie różne m etody wykonywania obliczeń. Cecha ta nie dotyczy języka C. Język C przetrwał, ponieważ znalazł odpowiednią równowagę pomiędzy ekspresywnością a wydajnością. Dla podstawowych aplikacji w dalszym ciągu jest to najlepsze narzędzie. Dlatego nigdy niczym nie zastąpiliśmy systemu X Window w systemie Unix. Wszystkie programy korzystają z biblioteki Xlib lub innej biblioteki, która korzysta z Xlib. Choć biblioteka XIib może być uznana za zawiłą, jest powszechna.

Brian: Dokładnie tak. Spełnia swoje zadanie. Robi to wystarczająco dobrze. Tworzenie podobnej biblioteki od początku to po prostu zbyt wiele pracy. Kiedy przyjrzymy się dziś językowi C + + , dostrzeżemy, że jednym z jego podstawowych celów projektowych było zachowanie wstecznej zgodności z C. Cecha ta wyszła językow i C + + na dobre, ale także na złe. M am taką teorię, że gdyby ktoś chciał zastąpić system X, musiałby stworzyć coś, co potrafi w przezroczysty sposób uruchamiać programy środowiska X. Język C + + nie wyparł języka C w wielu miejscach, choć jego projektanci stawiali sobie taki cel.

Brian: Bjarne strzelił sobie w stopę, próbując zachować zgodność z językiem C w maksymalnym możliwym stopniu. Jedną z przyczyn tego, że język C++ odniósł sukces tam , gdzie innym językom się nie pow iodło, było zachowanie wysokiego stopnia zgodności z językiem C. Stopień zgodności był dobry zarówno na poziomie kodu źródłowego, jak i obiektowego. Oznaczało to, że programiści nie musieli zbytnio się wysilać, aby przejść ze środowiska języka C++ do C. Jestem pewien, że niektóre decyzje, jakie Bjarne podjął w celu zachowania zgodności, w pewnym sensie uderzyły w niego samego. Z pewnością często słyszał coś w stylu:

1 72

ROZDZIAŁ

SZÓSTY

„To jest okropne, poniew aż...” . Podjął je jednak świadomie i dobrze przemyślał, ponieważ zgodność z istniejącą rzeczywistością była ważna. Zachowanie zgodności zwiększało prawdopodobieństwo osiągnięcia sukcesu w dłuższej perspektywie. Jednym z największych grzechów języka C + + jest jego zbytnie podobieństwo do języka C.

Brian: Być może, choć im dalej byłby języka C++ od języka C, tym trudniej byłoby mu odnieść sukces. To bardzo trudny kompromis. Myślę, że Bjarne wykonał dobrą robotę. Do jakiego stopnia można dążyć do zachowania zgodności wstecz, zamiast próbować wprowadzić coś nowego i rewolucyjnego?

Brian: Ten dylem at dotyczy absolutnie każdej dziedziny. Nie widzę sposobu, aby można go było uniknąć. Wspomniał pan, że wiele małych języków, do których zaczęto dodawać nowe własności, stało się systemami zupełnymi w rozumieniu Turinga i utraciło swoją koncepcyjną czystość. Czy istnieją jakieś zasady projektowe, które należałoby stosować przy przekształcaniu niewielkiego języka w język bardziej uniwersalny, tak aby nie stracić jego charakteru?

Brian: Myślę, że są. Pamiętam, że pow tarzałem to przy wielu okazjach. Często zastanawiałem się, pod jakim względem był to zaściankowy sposób myślenia. Mam tu na myśli to, że w spom nianą cechę miały wszystkie języki, w których tworzeniu brałem udział, i być może żadne inne. Być może widziałem tylko moje własne problemy. Teraz widzę, że byłoby lepiej, gdybym zadbał, aby nowe własności były syntaktycznie zgodne z istniejącym językiem, tak by użytkownicy nie musieli się uczyć nowej składni. Czy zanosi się na odrodzenie małych języków?

Brian: Nie wiem, czy „odrodzenie” to właściwe słowo, ale jestem pewien, że małe języki na pewno będą powstawały. Czynnikiem sprzyjającym tej sytuacji do pewnego stopnia jest rozpowszechnienie się interfejsów API usług sieciowych. Każdy dysponuje API, które pozwala sterować usługą sieciową w sposób programowy, więc nie trzeba tego robić ręcznie. Większość API korzysta z JavaScript, ale potrafię sobie wyobrazić sposoby, dzięki którym mogłyby być one bardziej dostępne z wiersza polecenia Uniksa lub W indowsa. Byłoby to wygodniejsze niż pisanie program ów w JavaScript, który jest w budow any w przeglądarce, skoro uruchomienie kodu i tak wymaga kliknięcia.

AWK

173

Brzmi to prawie ja k zapowiedź odrodzenia wiersza polecenia Uniksa, który w całości działa w internecie.

Brian: Doskonale pan to ujął. Czyż nie byłoby wspaniale?

Technologie transformacji Wspomniał pan, że narzędzie yacc ułatwiło eksperymentowanie ze składnią języka, ponieważ w przypadku modyfikacji gramatyki można było j ą łatwo zaktualizować i

ponownie uruchomić. Nie trzeba było w tym celu pisać kompletnego, rozbudowanego

parsera zstępującego. Czy program yacc można zaliczyć do technologii transformacji?

Brian: Z całą pewnością yacc wywarł duży wpływ na rozwój języków. Jeśli chodzi o mnie, bez tego narzędzia nigdy nie udałoby mi się ruszyć z miejsca. Nie wiem dlaczego, ale nie miałem zdolności w pisaniu rekurencyjnych parserów zstępujących. Zawsze miałem kłopoty z kolejnością wykonywania działań i łącznością. W przypadku narzędzia yacc nie trzeba było o tym myśleć. M ożna było napisać gramatykę, która miała sens, a następnie powiedzieć: „Taka jest kolejność wykonywania działań, a taka jest łączność. O to sposób obsługi niewygodnych przypadków — operatory jednoargumentowe są pisane tak samo jak dwuargumentowe”. Osiągnięcie tego wszystkiego było znacznie prostsze. Istnienie narzędzia umożliwiło myślenie 0 w ykonyw aniu z poziom u języka takich operacji, których w ykonanie bez niego byłoby trudne. Narzędzie yacc bardzo dobrze sprawdziło się dla języka EQN. Gramatyka nie była zbyt skom plikowana, ale zawierała dziwne konstrukcje. N iektóre z nich nie były wcześniej przemyślane w kontekście języka programowania. W rzeczywistości język EQN był deklaracyjny, a nie proceduralny. Kiedyś na konferencji CACM rozwinęła się dyskusja o zawiłości stosowania indeksów górnych i dolnych w tym samym elemencie. Za pomocą gramatyki yacc można było obsłużyć tę sytuację bez kłopotów. Zrobienie tego w inny sposób było niezwykle trudne. Narzędzie yacc było doskonałym programem z teoretycznego punktu widzenia — czyli jeśli wziąć pod uwagę technologie języka, rozumienie sposobów parsowania konstrukcji 1 przekształcenia ich na program. Narzędzie to było jednak przede wszystkim świetnie zaprojektowane — znacznie lepiej niż jakikolwiek inny program z tamtego okresu. Przez długi czas żaden program nie zbliżył się jakością pracy inżynierskiej do programu yacc. Narzędzie lex miało pewne właściwości podobne do yacc, ale nie osiągnęło takiego samego poziom u. Stało się tak praw dopodobnie dlatego, że tworzenie własnych analizatorów leksykalnych jest łatwiejsze od tworzenia gramatyki. W języku AWK pierwotnie występował analizator leksykalny stworzony za pom ocą program u lex,

174

ROZDZIAŁ

SZÓSTY

jednak z czasem uznałem, że jego obsługa w różnych środowiskach jest trudna. Z tego powodu zastąpiłem go analizatorem leksykalnym stworzonym od podstaw w języku C. Było to przyczyną wielu błędów w programie jeszcze wiele lat później. Czy istnieją inne — oprócz narzędzi lex i yacc — technologie, które upraszczają lub podnoszą wydajność procesu tworzenia języków czy programów?

Brian: Posiadanie systemu operacyjnego Unix, który działał pod spodem, oznaczało, że wszystkie zadania obliczeniowe były łatwe. Tworzenie skryptów powłoki, pisanie program ów i przechwytywanie ich wyjścia, edycja i przekształcanie tego wyjścia na inną postać w czasach, kiedy komputery były wolne — to wszystko dawało wielkie możliwości. Posiadanie tych narzędzi, a zwłaszcza tych podstawowych, takich jak programy sort, grep i diff, ułatwiało zachowanie kontroli nad wykonywanymi operacjami oraz śledzenie niewielkich fragmentów całości. Nie potrafię sobie wyobrazić kompilowania programów bez narzędzia Make, ale z całą pewnością nie mogę sobie wyobrazić programowania bez instalacji łatek (ang. patch). A to był 1986 lub 1987 rok.

Brian: Do czasu, kiedy zostałem wykładowcą, nigdy nie używałem łatek, ponieważ nigdy nie napisałem niczego, co byłoby na tyle rozbudowane, żeby zastosowanie łatek miało więcej sensu niż napisanie źródeł od początku. Kilka lat temu zdecydowałem, że studenci uczestniczący w kursie mojego przedmiotu powinni coś wiedzieć o łatkach, poniew aż w ten sposób rozprow adza się bardzo dużo kodu, zwłaszcza w świecie systemu Linux. W jednym z ćwiczeń, jakie wykonują studenci w ram ach mojego przedmiotu, wymagam od nich, aby ściągnęli z internetu wersję języka AWK, dodali określoną własność — na przykład konstrukcję repeat unti 1 — opracowali testy i uruchom ili za pom ocą skryptów pow łoki, a następnie wysłali plik z łatką. W ten sposób zdobywają um iejętność pobierania program u open source, w prow adzania w nim drobnych zmian, a następnie przesyłania go z powrotem . Nigdy nie myślałem, że będę korzystał z łatek. Zaakceptow ałem je jako sposób przesyłania do mnie źródeł. Czy przeglądanie kodu w formie łatek jest łatwiejsze?

Brian: Myślę, że to druga sprawa. Pliki z łatkami są znacznie bardziej kompaktowe. Znacznie szybciej można się zorientować, jakie działania w nich wykonano. Wspomniał pan o testowaniu. Czy dziś pisałby pan kod inaczej — tak by móc wykorzystać testy jednostkowe?

Brian: Dla tego rodzaju programów, które pisałem przez lata, testy jednostkowe nie miały zbyt wielkiego sensu, ponieważ owe program y były zbyt małe i z reguły samodzielne. Idea testów jednostkowych — polegająca na wykonywaniu wielu operacji postaci „wywołaj tę funkcję i zobacz, jak się zachowuje” w spreparowanym programie

AWK

175

— nie m iała uzasadnienia w przypadku tych program ów. W związku z tym nie korzystałem z testów jednostkowych na tym poziomie. Próbowałem z nich korzystać podczas wykładów, ale uzyskałem mizerny efekt. ma i n

Jeżeli programy są małe, wolę wykonywać testy czarnej skrzynki. Należy opracować zbiór przypadków testowych, zazwyczaj w formie bardzo specjalizowanego niewielkiego języka, a następnie automatycznie uruchomić testy i poinformować o sytuacjach, które zakończyły się niepowodzeniem. To jest sposób, który sprawdza się dla fragmentów języka AWK. Jest doskonały dla wyrażeń regularnych. Nadaje się również do koderów i dekoderów Base64, o których wykonanie czasami proszę studentów. Dla wszystkich tych przypadków stosuję testowanie zewnętrzne, a nie wewnętrzne. Nie wprowadzam konstrukcji testujących do programu. Z drugiej strony dziś na pew no wprow adziłbym ułatw ienia w ykonywania testów wewnętrznej spójności. Zastosowałbym asercje i funkcje sprawdzające wewnętrzną poprawność (ang. sanity check). Być może użyłbym więcej punktów testowych lub sposobów pobierania wewnętrznych stanów bez konieczności wykonywania zbyt wielu działań. M echanizmy te zastosowałbym zamiast w budow anych autotestów używanych przez projektantów sprzętu. Przypomina to prawie w równym stopniu debugowanie kodu, ja k i jego testowanie. Być może nie istnieją zbyt ostre różnice pomiędzy nimi?

Brian: Według mnie idea asercji polega na tym, że ma się przekonanie, że coś działa poprawnie w pewnym punkcie, ale nie jest to pewność stuprocentowa. Rozkładamy więc spadochron po to, aby m ożna było bezpiecznie wylądować, gdyby coś się nie powiodło. To dość zagmatwana metafora. Asercje i testy poprawności wewnętrznej są przydatne, ponieważ w przypadku, gdy coś się nie powiedzie, to debugowanie staje się łatwiejsze. W iadomo bowiem, od którego miejsca należy rozpocząć testowanie nieprawidłowego działania. Mechanizmy te pozwalają również wykryć testy, które powinny być przeprowadzone, a nie były. Kiedyś zadałem studentom zadanie stworzenia klasy obsługi tablic asocjacyjnych, której idea, ogólnie rzecz biorąc, była taka sama jak idea tablic asocjacyjnych w AWK. Programy były pisane w C, co oznaczało, że błędy zwykle pojawiały się w operacjach na ciągach znaków. Kiedy tw orzyłem m oją wersję, napisałem oddzielną funkcję sprawdzania wewnętrznej popraw ności, która przeglądała struktury danych i sprawdzała, czy liczba elementów uzyskanych w wyniku liczenia wewnątrz struktury danych była taka sama jak liczba uzyskana przy spraw dzaniu tablicy z zewnątrz. Funkcja ta działała jak odm iana funkcji malloc, która sprawdza arenę przed każdą transakcją i po każdej transakcji. Test miał następujący sens: „Jeśli pojawi się błąd, pewnie będzie to w tym miejscu, dlatego lepiej będzie, jeśli się upewnię”. Zastosowałem znacznie więcej podobnych testów.

1 76

ROZDZIAŁ

SZÓSTY

Czy fakt, że zabezpieczał pan miejsca powstawania możliwych błędów, wynika z pańskiej dojrzałości jako programisty, czy też wykorzystał pan ten sposób dlatego, że koszty jego stosowania były niskie?

Brian: Nie sądzę, abym był uprawniony do mówienia o sobie, że jestem dojrzałym programistą. Piszę mniej kodu, niż bym chciał, a kiedy już to robię, jest on kiepskiej jakości, niezależnie od tego, co mówię. Można by to określić stwierdzeniem: „Róbcie tak, jak mówię, a nie tak, jak robię” . Nasz redaktor słyszał, ja k na pewnej konferencji chwalił pan języki Tcl i Visual Basic. Co dziś sądzi pan o tych językach?

Brian: We wczesnych latach dziewięćdziesiątych dużo programowałem w środowisku Tcl/Tk. Dokładnie zrozumiałem jego wewnętrzne mechanizmy. Napisałem systemy, których co najmniej przez jakiś czas używano w Bell Labs. Dzięki Tcl/Tk mogłem bardzo szybko tworzyć interfejsy. Tcl/Tk to doskonałe środowisko do budow ania interfejsów użytkownika. Jest w tym znacznie lepsze od wszystkich swoich poprzedników . Tcl jako samodzielny język jest dość specyficzny. Dobry do tego, do czego był przeznaczony, ale na tyle specyficzny, że wiele osób miało z nim problemy. Język ten pewnie by zniknął, gdyby nie biblioteka Tk, która świetnie nadaje się do tworzenia interfejsów. Visual Basic w swoim początkowym okresie był przyjemnym językiem i środowiskiem do pisania aplikacji systemu W indows. W pew nym m om encie VB był jednym z najpopularniejszych języków programowania. Za jego pom ocą m ożna było łatw o tworzyć i urucham iać program y z graficznym interfejsem użytkownika, zatem w świecie W indow sa spełniał tę samą rolę, jaką spełniała biblioteka Tk w uniksowym świecie systemu X I I . Firma Microsoft powoli niszczyła język Visual Basic. W tym momencie nie użyłbym go do stworzenia niczego nowego. Naturalny wybór stanowi dziś język C#. Jakie są pańskie odczucia, kiedy rezygnuje pan z jakiejś własności lub pomysłu i prosi użytkowników o aktualizację do nowej wersji?

Brian: Niestety jest to jedna z sytuacji, w której nie ma właściwego sposobu postępow ania. Niezależnie od tego, co się zrobi, ktoś będzie niezadowolony. Jeśli to jest mój program, chcę, aby użytkownicy poszli za mną, a jeśli kogoś innego, chcę, by utrzymali wszystkie specyficzne konstrukcje, których użyłem. Byłem już po obu stronach. Jedną z m oich bolączek przez wiele lat były różne wersje języka AWK stworzone w Bell Labs. Al, Peter i ja rozwijaliśmy jedną wersję. Była też druga wersja — NAWK, którą stworzyła inna grupa. Chcieli oni, by język ewoluował w inną stronę. Z tego powodu w pewnym stopniu posługiwaliśmy się niezgodnymi wersjami.

AWK

177

To ma sens. Pytanie brzmi: co sprawia, że życie staje się prostsze? Jeśli pozbycie się jakiejś własności, której utrzymanie lub udokumentowanie je st trudne, sprawia, że w dłuższej perspektywie utrzymanie programu staje się łatwiejsze, trzeba to wziąć pod uwagę. Jeśli natomiast aktualizacja do nowej wersji programu wymusza przepisanie dużych ilości kodu, to zupełnie coś innego.

Brian: W niektórych środowiskach można sobie z tym poradzić. Na przykład firma M icrosoft stworzyła kreator konwersji, który przekształcał program y w VB 6 na VB.NET. Pierwsze wersje tego narzędzia nie działały najlepiej, ale nowsze wersje działały znacznie sprawniej. Od tego m om entu aktualizacja do VB.NET stała się wykonalna. Do jakiego stopnia projektant powinien postrzegać elegancki interfejs jako podstawowy cel implementacji? Czyjest to aspekt, o którym zawsze myśli pan w pierwszej kolejności, czy też zależy to od innych celów?

Brian: Jeśli to jest język programowania, trzeba myśleć o tym, jak użytkownicy będą pisali program y za jego pom ocą. Jakie program y będą pisać? Przed zamrożeniem projektu trzeba samodzielnie wypróbować wiele przykładów. Jeśli to jest API, trzeba dokładnie przemyśleć sposób, w jaki ludzie będą go używać, oraz to, jak obsługuje ono trudne pytania — na przykład o to, kto jest właścicielem różnych zasobów. Michi Henning napisał bardzo interesujący artykuł o projektowaniu API w magazynie ACM Queue z maja 2007 roku. Artykuł ten czytam zawsze przed wygłoszeniem wykładu na temat API. Henning powiedział między innymi, że interfejsy API są obecnie dużo ważniejsze niż kiedyś, ponieważ jest ich znacznie więcej i spełniają znacznie bardziej złożone funkcje. Przykładem m ogą być interfejsy API usług sieciowych. Na przykład interfejs API serwisu Google Maps jest dziś dość rozbudowany. Nie pamiętam, aby był tak duży w czasie, gdy bawiłem się nim po raz pierwszy trzy lata temu. Od tam tego czasu znacznie się rozrósł. W mojej opinii został on dobrze zaprojektowany. Posługiwanie się innymi interfejsami nie jest takie łatwe. Prawidłowe zaprojektowanie interfejsu API to ciężka praca. Trzeba też oczywiście przygotować się na sytuację, w której zmieniamy zdanie. Można sobie wyznaczyć dzień, w którym zostaną zaktualizowane wszystkie serwery.

Brian: Albo zmienić szereg nazw, tak by były zgodne z nową wersją. Czy p o trafi pan wprowadzać zmiany w sposób ewolucyjny? Czy to nie Stuart Feldman powiedział: „N ie mogę zmienić tabulacji w programie M ake — mam ju ż 12 użytkowników!"?

Brian: Zgadza się. To jedna z osobliwości programu Make. Myślę, że Stuart jest prawie tak samo niezadowolony z niej teraz, jak był wtedy. Wprowadzanie zmian w sytuacji, kiedy są już realni użytkownicy, jest bardzo trudne. Joshua Bloch podczas udzielania

1 78

ROZDZIAŁ

SZÓSTY

wywiadu na temat projektowania API powiedział: „API są wieczne” . Kiedy się je raz opracuje, wprowadzenie jakichkolwiek zmian jest bardzo trudne. Czasami można opracować konwertery. Rozmawialiśmy o konwerterze języka VB. Mike Lesk wiele lat tem u zm odyfikow ał system TBL. Tabele były tw orzone kolum nam i. Mike zdecydował, że będzie lepiej, jeśli będą tworzone według wierszy, dlatego napisał konwerter. Nie była to doskonała praca, ale wystarczająco dobra, by wziąć tabelę i przekształcić ją na nową postać. Takie podejście bardzo się przydaje do wykonywania pewnych operacji. Istnieje translator z AWK na Perlą. Co prawda jego możliwości są ograniczone, ale wystarczają do tego, aby ruszyć z miejsca. Gdyby miał pan wymienić jedną lekcję, którą wyniósł pan ze swojego doświadczenia, co by to było?

Brian: Trzeba bardzo uważnie myśleć o tym, co się robi, a następnie testować, ulepszać i poprawiać tak długo, aż uzyska się satysfakcjonujący efekt. Nie należy publikować pierwszego programu, który się opracuje. W przypadku niektórych systemów m ożna odnieść wrażenie, że ktoś wypuścił ich pierwszą wersję. Po opublikowaniu takiej wersji od razu wiadomo, że coś jest z nią nie tak. Weźmy pod uwagę geniusz Beethovena. Jego rękopisy to wielki bałagan. Jedynym kom pozytorem , który potrafił od razu pisać doskonałą muzykę, był prawdopodobnie Mozart. Istnieje wyraźna linia pomiędzy oszałamiającą pracą geniusza pojawiającego się raz na tysiąc lat a resztą ludzi.

Brian: Isaac Asimov w swojej autobiografii powiedział, że po prostu pisał słowa, potem je opublikow ał, a większość jego prac była doskonałej jakości. Mówił, że nigdy nie przepisywał tekstów. To spraw dzało się w tym indyw idualnym przypadku, ale nie sądzę, aby było normą. Na ścianie jednej z sal tu, na Uniwersytecie, wisi poem at Paula M uldoona, który przypom ina mi rękopisy Beethovena. Praca artysty polega na ciągłym kreśleniu, ponow nym pisaniu i przepisywaniu, aż w końcu ktoś oprawia to w ramkę i wiesza na ścianie jako przypomnienie tego, jak ciężko stworzyć coś prawidłowo za pierwszym razem. Z programowaniem jest tak samo. Nie należy publikować programu natychmiast po jego napisaniu.

Rzeczy, które zmieniły wszechświat Czy to prawda, że inspiracją do powstania języka AWK była rozmowa pomiędzy panem a Alem Aho na tem at dodania parsera dla rozszerzalnych języków w projekcie bazodanowym, który wtedy realizowaliście?

Peter W einberger: Nie zapam iętałem tej sytuacji w taki sposób, ale pamięć jest zawodna. Pracowałem w dziale zajmującym się danymi (na kom puterach Univac),

AWK

179

a Al i Brian chcieli dodać coś bazodanow ego do poleceń Uniksa. Możliwe, że początkowo mieli bardziej ambitne plany, ale o ile sobie przypominam, wcześniej zdecydowaliśmy, że skanowanie danych jest wydajnym sposobem ich przetwarzania. Dlaczego postanowiliście skoncentrować się na narzędziu do wydobywania danych z plików? Dlaczego na przykład unikaliście tworzenia narzędzia do wprowadzania danych?

Peter: Jedną ze wspólnych cech uniksowych narzędzi działających w wierszu polecenia było to, że korzystały one z plików składających się z wierszy tekstu (w tam tych czasach był to tekst w formacie ASCII). M ożna było wprowadzać dane za pomocą edytora. Aktualizacja pliku zwykle oznaczała stworzenie nowego pliku zawierającego zm odyfikowaną zawartość. Były też możliwe inne operacje i wykonywało się je, ale nie należały one do głównej linii. Słyszałem, że skoncentrowaliście się na czytaniu danych, ponieważ nie chcieliście zajmować się problemami równoległego zapisywania.

Peter: Cóż, niezupełnie tak było. © Czy taką samą decyzję podjęlibyście dziś?

Peter: Nie. Gdybyśmy pisali AWK dziś i pam iętali o zachow aniu jego prostoty, nie sądzę, by były w nim jakiekolwiek elementy współbieżności widzialne dla użytkownika. Jestem jednak pewien, że byłyby one wbudowane, aby można było wykorzystać lokalne m echanizm y obsługi procesorów wielordzeniowych lub współbieżności. Jestem pewien, że sprawiłoby to nam pewne kłopoty, ale przezwyciężylibyśmy je. M ożna zadać interesujące pytanie: w jakim stopniu zmieniłoby to projekt języka? Nie wiem, trzeba by było nad tym pomyśleć. Jeśli weźmiemy pod uwagę, że dysponujemy wolnymi procesorami, wieloma wolnymi procesorami, to można sobie wyobrazić kilka rzeczy, które można z nimi zrobić. Jedna z możliwości to stwierdzenie: „Nie będziemy ich używać, zostawimy je dla innych działających program ów ” . W przypadku języka AWK lub narzędzi podobnych do AWK nie jest to zły wybór, ponieważ jeśli wziąć pod uwagę, że zaprojektow ano go przede wszystkim z myślą o wykorzystywaniu w potokach, to trzeba uwzględnić, że inne programy należące do potoku także potrzebują czasu procesora. Z drugiej strony, jeśli narzędzie m iało służyć do stosunkow o złożonych operacji przetwarzania plików, to m ożna było stworzyć mechanizmy korzystające z kilku procesorów działających jednocześnie. Oczywiście tego nie zrobiliśmy, ponieważ komputery wtedy nie miały takiej architektury.

180

ROZDZIAŁ

SZÓSTY

W jakich kontekstach według pana AWK jest lepszym narzędziem niż na przykład SQL?

Peter: Cóż, w zasadzie nie da się porów nać tych dw óch narzędzi. W AWK nie ma jawnie deklarow anych typów. W SQL jest ich nadm iar. Tak więc AWK czyta i zapisuje ciągi znaków, ale jest przygotowany do interpretowania pewnych ciągów znaków jako liczb, jeśli wydamy taką dyspozycję. W SQL można tworzyć złączenia. Aby zrobić to samo w AWK, trzeba by było wcześniej uruchomić program, na przykład o nazwie join, który zrealizowałby złączenie. SQL umożliwia sortowanie i agregację danych. W systemie Unix te zadania wykonuje program sort. Wynik jego działania jest następnie przekazywany za pom ocą potoku do AWK lub innego polecenia uniksowego. Krótko mówiąc, AWK zaprojektow ano z myślą o wykorzystaniu go w ram ach sekwencji poleceń używających potoku. SQL zaprojektow ano do wykorzystania z danymi ukrytymi w nieprzezroczystej strukturze, której schemat jest widoczny dla użytkownika. Na koniec trzeba pam iętać o wielu latach optymalizowania zapytań w języku SQL, podczas gdy w przypadku AWK mamy to, co widzimy. Jakie zalety dostrzega pan w zapisywaniu dzienników systemu Unix w plikach tekstowych i manipulowaniu nimi za pomocą AWK?

Peter: Pliki tekstowe to wielka zaleta. Czytanie ich nie wymaga specjalnych narzędzi, a przetwarzanie ułatwiają wszystkie dostępne narzędzia systemu Unix. Gdyby tego było m ało, m ożna je łatw o przekształcić i załadować do innego program u. Pliki tekstowe są uniw ersalnym typem danych wejściowych dla każdego typu oprogram ow ania. Co więcej, są one niezależne od kolejności bajtów stosowanej w procesorach. N aw et tak niewielka optym alizacja jak kom presja wiąże się z koniecznością pam iętania, które narzędzie kompresji zastosowano, a zwykle jest kilka do wyboru. Jeśli chodzi o przetwarzanie ich za pom ocą AWK, to dobra własność, gdy za pomocą potoku można zrealizować potrzebne operacje. Poza tym pliki tekstowe m ożna czytać równie skutecznie za pom ocą języków skryptowych, na przykład Perlą lub Pythona. Można też skorzystać z języka C lub Javy. Pliki tekstowe doskonale nadają się do tworzenia dzienników systemowych. Dawniej jedynym argumentem przeciwko nim była konieczność parsowania oraz zamiany liczb na postać binarną lub odwrotnie. Ta ostatnia w ada jest jednak ledwo dostrzegalna w czasach silnych procesorów, a parsowanie plików tekstowych w porównaniu z XML jest trywialne. Z drugiej strony struktury binarne o stałych rozmiarach nie wymagają parsow ania, ale są bardzo niestandardow e. W związku z tym zalety stosow ania form atów binarnych nie są dostrzegalne.

AWK

181

Język AWK był jednym z pierwszych dowodów na siłę uniksowej koncepcji wielu małych programów działających razem. Programy te w większości przetwarzały dane w formacie tekstowym. W ja k i sposób koncepcja ta sprawdza się dla formatów nietekstowych oraz multimediów?

Peter: W arto powiedzieć kilka słów o tym, czym naprawdę była koncepcja Uniksa. Był to styl, w którym można było wykorzystać wiele programów z jednym wejściem i jednym wyjściem poprzez zastosowanie składni wiersza polecenia. Pozwalały na to jednolitość wejścia i wyjścia na poziomie systemu (dzięki w yw ołaniom systemowym read i write niezależnym od typu urządzenia), a także m echanizm systemowy (potoki), dzięki którem u możliwe było uniknięcie nadaw ania nazw i alokowania tymczasowych plików. Konwersja kodowania i kompresja to przykłady operacji, do których taki układ idealnie się nadaje, także wtedy, gdy dane są w formacie audio lub wideo. Jednak nawet w przypadku formatu tekstowego istnieje wiele aplikacji, które nie działają w taki sposób. Zwłaszcza jeśli ludzie się z nimi kom unikują. Na przykład polecenie spei 1 tworzy listę słów o nieprawidłowej pisowni, ale nie jest to mechanizm interaktywny. Użytkownik musi powrócić do dokumentu i go wyedytować. Tak więc pańskie pytanie powinno raczej brzmieć: „Gdybyśmy mieli do dyspozycji tylko wiersz polecenia, to jakich poleceń można by użyć do przetwarzania danych lub multimediów?”. Ale to nie ma nic do rzeczy. Dysponujemy teraz innymi sposobami interakcji z kom puteram i oraz m am y większe możliwości podziału zadań. Nowe sposoby niekoniecznie są lepsze bądź gorsze od starych. Są po prostu inne. Jednym z przykładów może być program TeX w zestawieniu z takimi programami jak Word. Czy jeden jest lepszy od drugiego? Wątpię, czy udałoby się udzielić jednoznacznej odpowiedzi. Jakie ograniczenia widzi pan w narzędziach działających w wierszu polecenia, a ja k ie w programach z graficznym interfejsem użytkownika?

Peter: To stary temat, a granice pomiędzy wymienionymi dwoma typami programów nieco się rozmyły. M ożna by na ten tem at napisać rozprawę. Spróbuję udzielić uproszczonej odpowiedzi. Jeśli potrzebuję połączenia kilku program ów ze sobą, wtedy sprawdza się skrypt powłoki, który wywołuje narzędzia działające w wierszu polecenia. Jest to również sposób weryfikacji, czy opcje i preferencje dla różnych komponentów są spójne. Graficzne interfejsy użytkownika są jednak znacznie lepsze do tego, by użytkow nik m ógł obejrzeć szereg opcji i wybrać jedną spośród nich. Zapewniają również potencjalnie większe możliwości zorganizowania informacji.

1 82

ROZDZIAŁ

SZÓSTY

Wielu moich rozmówców podkreślało ważność nauki m atem atyki w procesie stawania się lepszym programistą. Zastanawiam się, do jakiego stopnia możemy uczyć się tego, czego potrzebujemy, gdy tego potrzebujemy. Na przykład dzięki internetowi można dość szybko uczyć się nowych rzeczy, prawda?

Peter: Tak i nie. Niestety, żeby się czegoś nauczyć, nie wystarczy tylko o tym myśleć, ale trzeba mieć pew ną praktykę. Na przykład interesuje nas jakaś rzecz, czytamy o niej w internecie i mówimy: „O tak, to jest to ” . Istnieją jednak takie zagadnienia, w których nie ma innego wyjścia, jak tylko poświęcić im wiele lat ciężkiej pracy. Jeśli zatem w czasie realizacji jakiegoś projektu stwierdzimy, że do rozwiązania problemu przydałaby się znajom ość zagadnień program ow ania liniowego, to informacje z internetu niewiele nam pomogą. Jeśli musimy rozwiązać problem w tydzień, istnieje małe prawdopodobieństwo, że wybierzemy metodę wymagającą długotrwałej nauki. Jedyną szansą jest to, że wcześniej znaliśmy to zagadnienie. A trzeba pamiętać, że takie zadania się zdarzają. Jaka jest rola matematyki w informatyce, a w szczególności w programowaniu?

Peter: Jestem m atem atykiem , dlatego chciałbym wierzyć, że m atem atyka ma podstawowe znaczenie. Istnieją jednak dziedziny informatyki oraz wiele obszarów programowania, w których można odnosić znaczące sukcesy bez użycia matematyki. M atem atyka jest przydatna w pew nych warstwach. Ludzie, którzy nie rozum ieją statystyki lub rachunku praw dopodobieństw a, będą czuli się zagubieni w gąszczu rzeczywistych danych. M atem atykę wykorzystuje się w grafice. Wiele zagadnień matematycznych ma odzwierciedlenie w systemach sztucznej inteligencji, zwłaszcza systemach uczących się (ang. machinę learning). Także w kryptografii wykorzystuje się wiele elementów teorii liczb. Bez znajomości matematyki ludzie po prostu nie są w stanie zrozumieć dużych fragmentów informatyki. Jakie różnice dostrzega pan pomiędzy pracą nad twierdzeniami a budowaniem implementacji?

Peter: Jeśli spojrzeć z najwyższego poziomu, to kiedy udow adniam y twierdzenie, dow iadujem y się na tem at świata czegoś, czego prawdziwość wcześniej tylko zakładaliśmy. To wiedza bezwarunkowa. Kiedy piszemy program, możemy zrobić coś, czego wcześniej nie byliśmy w stanie zrobić. W pewnym sensie w ten sposób zmieniamy świat. W większości przypadków zmiany te są bardzo małe. M atem atyka i program ow anie różnią się od siebie. Być może najłatwiejszym sposobem, aby się o tym przekonać, jest porów nanie artykułów m atem atycznych oraz dow odów tw ierdzeń lub program ów tw orzonych przez matematyków udowadniających twierdzenia. Artykuły matematyczne często zawierają sedno sprawy. Dowody maszynowe takie nie są. N apisanie program u ma coś

AWK

183

z charakteru dowodów generowanych maszynowo w tym sensie, że wszystkie niewielkie szczegóły muszą działać właściwie, łącznie ze zrozumieniem tematu przez programistę oraz jego umiejętnościami testowania. Czy tworzenie implementacji uczy czegoś więcej?

Peter: Oczywiście. Zazwyczaj dowiadujesz się, że powinieneś całość wyrzucić i zaimplementować ją od początku. Każdy projekt składa się z dziesiątek decyzji projektowych. Większość z nich początkowo wydaje się nieistotna lub wybierane alternatywy bazują na intuicji. Prawie zawsze bywa tak, że kiedy kod jest gotowy, staje się oczywiste, że można było podjąć lepsze decyzje. Następnie, wraz z upływem czasu, kod jest używany w nieoczekiwanych okolicznościach i kolejne decyzje wyglądają nie najlepiej. Czy programowanie funkcyjne mogłoby tu pomóc?

Peter: Jeśli pytał pan o to, czy programy funkcyjne, które mają bardziej matematyczny charakter, lepiej prezentują wyniki od zwykłych programów, odpowiadam, że nie widzę dużej różnicy. Każdy język jednokrotnego przypisania pozwala na łatwiejsze wnioskowanie, ale to nie ułatwia pisania programów. Nie jest to również przekonujący dowód na to, że programy są łatwiejsze do pisania. Większość pytań porównawczych na tem at języków, technik kodowania, metodologii wytwarzania oprogramowania oraz inżynierii programowania ma charakter zatrważająco nienaukowy. Oto cytat z książki Barkera R. Bausella Snake Oil Science [Oxford University Press]: „Dokładne, kontrolowane badania (na przykład losowe, kontrolowane próby) obejmujące dane numeryczne okazały się bardziej wiarygodne w pokazywaniu tego, co działa, i tego, co nie działa, niż bazowanie na opiniach ekspertów, przeczuciach lub naukach tych, których szanujemy”.

Tworzenie oprogram ow ania to w dalszym ciągu rzemiosło. Są artyści klasy Chippendale, są rzemieślnicy oraz praktykanci. Ale trochę odbiegłem od pańskiego pytania. Co według pana należy zrobić, aby zostać lepszym programistą?

Peter: A co pan powie na naukę matematyki? Cóż, może inna odpowiedź byłaby lepsza. Na przykład taka: „Trzeba zrozumieć operacje zmiennoprzecinkowe”. A może to także nie to. Ludzie bardzo się od siebie różnią pod tym względem. Myślę, że istotne jest uczenie się nowych technik i algorytmów. Bez tego ludzie stają się specjalistami w zbyt wąskiej dziedzinie. Ponadto dziś trzeba być dobrym w pisaniu kodu bezpiecznego i odpornego na ataki. Istnieje wiele ataków na użytkowników i systemy. Trzeba zadbać o to, aby kod, który piszemy, nie był wrażliwy. Jest to szczególnie trudne w przypadku witryn WWW.

184

ROZDZIAŁ

SZÓSTY

Kiedy należy uczyć debugowania? I w ja k i sposób?

Peter: Wykłady dotyczące debugowania powinny stanowić integralną część wszystkich kursów program ow ania. Debugowanie pow inno być również uwzględniane w projektach wszystkich języków. Pisanie sekwencyjnych programów działających na odizolowanych kom puterach nie jest łatwe. Pisanie kodu wielowątkowego jest jeszcze trudniejsze, a narzędzia debugowania nie są zadowalające. W projekcie trzeba wziąć pod uwagę między innymi to, czy projekt ułatw ia debugowanie. Nie będzie wielką przesadą stwierdzenie, że programista albo podejmuje decyzję o tym, co będzie robił w następnej kolejności, albo debuguje. Wszystkie inne czynności zajmują bardzo mało czasu. Czy istnieje coś, co uważa pan za swój największy błąd popełniony w projekcie lub programowaniu? Czego owa sytuacja pana nauczyła?

Peter: Nie wiem, czy istniał ten jeden największy błąd. Ludzie stale popełniają błędy. Na ich podstawie uczymy się (być może nawet nie umiejąc tego poprawnie wyrazić) zbioru zasad projektowych, które zwykle się sprawdzają. Następnie wykorzystujemy je do granic możliwości, aż przestają się sprawdzać. Czasami pozwala to na wyciągnięcie nowych wniosków, innym razem kod zawsze nosi piętno przestarzałych reguł projektowych. Zauważyłem, że nie umieszczam zbyt wielu przydatnych objaśnień w komunikatach o błędach. Często muszę do nich powracać i dodawać nowe szczegóły. W ystępuje tu typow y konflikt: jeśli wystąpi błąd, chcemy uzyskać kom pletne, przydatne informacje. Jeśli błąd nie wystąpi, jest dużo pisania oraz dużo zajętego miejsca na ekranie. Trzeba zdecydować się na jakiś kompromis. Czego pan najbardziej żałuje w związku z językiem AWK?

Peter: Myślę, że dyskusja dotycząca użycia spacji do konkatenacji ciągów znaków nie sprawdziła się tak dobrze, jak oczekiwaliśmy. Byłoby lepiej, gdybyśmy użyli jawnego operatora. Przy projektowaniu składni zwykle występuje konflikt pomiędzy dążeniem do krótkich wierszy polecenia a umożliwieniem tw orzenia dużych program ów . Początkowo nie braliśmy pod uwagę tego drugiego, w związku z tym niektóre nasze decyzje były błędne. Co zyskało popularność (lub okazało się przydatne) ku pana zaskoczeniu?

Peter: Cały język zyskał znacznie większą popularność, niż oczekiwaliśmy, w każdym razie niż ja oczekiwałem. Jedna z idei, która przyświecała projektow i, była taka, że powinien to być język łatwy do nauki dla osób znających narzędzia środowiska Unix, w szczególności język C i program grep. Te wymagania sprawiały, że język nie mógł trafić do masowego grona odbiorców, na przykład do sekretarek lub hodowców owiec. Zdarzyło mi się jednak spotkać na weselu na początku lat dziewięćdziesiątych hodowcę owiec, który używał Uniksa do przechowywania danych o swojej hodowli i był wielkim fanem języka AWK. Myślę, że do tej pory zrobił duże postępy.

AWK

185

W ja k i sposób stymuluje pan kreatywność w zespole tworzącym oprogramowanie?

Peter: Najlepszą drogą do wysokiej jakości oprogram ow ania są utalentow ani eksperci, którzy mają czytelny obraz tego, co chcą stworzyć. Istnieją inne sposoby, ale wymagają one więcej pracy. Nie m am pojęcia, w jaki sposób m ożna tworzyć dobre oprogram owanie bez utalentow anych program istów, choć przypuszczalnie jest to możliwe. W ja k i sposób tworzy się język, gdy pracuje się w zespole?

Peter: Wszyscy rozmawialiśmy o składni i semantyce, a następnie każdy z nas pisał kod. N astępnie każdy m ógł modyfikować kod. Kod nie był osobistą własnością żadnego z członków zespołu, choć Brian opiekow ał się nim przez lata. Mieliśmy ograniczone ambicje. Myślę, że pomagała nam w tym docelowa maszyna, która miała tylko 128 kB pamięci. Jeśli chodzi o projekt, to siadaliśmy, rozmawialiśmy i pisaliśmy na tablicy, a następnie, podczas kodowania czasem, okazywało się, że brakowało czegoś istotnego. Stawało się to inspiracją do nieformalnej dyskusji. Kiedy w kodzie znajduje pan powtarzający się problem, to w ja k i sposób rozpoznaje pan, co jest najlepszym rozwiązaniem — lokalne obejście czy globalna poprawka?

Peter: Istnieją dwa rodzaje projektów oprogram ow ania: takie, które kończą się niepowodzeniem, oraz takie, które zamieniają się w horror utrzymania istniejących programów (ang. legacy software). Jedynym sposobem uniknięcia tej drugiej sytuacji byłoby przepisywanie kodu wraz ze zmianami w środowisku. Problem polega na tym, że jest to luksus, na jaki w większości projektów nie m ożna sobie pozwolić. Rzeczywistość zmusza zatem do wprowadzania lokalnych poprawek. Po wprowadzeniu wielu lokalnych popraw ek kod staje się sztywny i bardzo trudny do utrzymania. Bez osób, które pierwotnie tworzyły oprogramowanie, lub bardzo dobrej specyfikacji przepisanie kodu staje się niezwykle trudne. Życie bywa ciężkie. Gdyby miał pan na podstawie własnego doświadczenia udzielić Czytelnikom tej książki jednej rady, co by to było?

Peter: Zacytow ałbym , być może niezbyt dokładnie, Einsteina: „Najprościej, jak się da, ale nie prościej” . Problem w tym, by nie być zbyt pobłażliwym, o co bardzo łatwo. Jeśli użytkownicy zaczynają o coś prosić, m ożna to uwzględnić. W ymaga to oceny, czy uzyskane rozwiązanie będzie proste, ale nie prostsze, niż to konieczne.

1 86

ROZDZIAŁ

SZÓSTY

Najprostsze rozwiązanie, które będzie działać? To chyba powiedział Kent Beck. W ja k i sposób rozpoznaje pan prostotę i nie zgadza się na dodawanie elementów, które w danym momencie nie są potrzebne?

Peter: To zależy od tego, kogo mamy wokół siebie. Dla wielu osób dobrym testem może być odpowiedź na pytanie: „Czy potrafisz wyjaśnić to swoim rodzicom ?” . Czasami może to być niemożliwe, ale jako punkt wyjścia wydaje mi się rozsądne. Bardziej uniwersalny test wymaga zwrócenia się do osób, które praw dopodobnie będą użytkow nikam i naszego systemu: „Czy potrafisz wyjaśnić to przeciętnem u użytkow nikow i?” — w odróżnieniu od: „Czy najbardziej bystry użytkownik to zrozumie?” .

Teoria i praktyka Zanim zaczął pan pracę w Bell Labs, uczył pan matematyki. Czy informatyki należy nauczać w taki sam sposób, w ja k i naucza się matematyki?

Peter: Matematyki uczymy z kilku różnych powodów. Jednym z nich jest nauczanie przyszłych matematyków. Właśnie w ten sposób starałem się postępować wtedy, kiedy ja uczyłem tego przedmiotu. Innym powodem jest nauczanie matematyki z uwagi na jej przydatność. Ale w przypadku matematyki sytuacja jest nieco klarowniejsza w porównaniu z informatyką. W informatyce występują różne rodzaje programowania i trudno stwierdzić, co jest potrzebne, a co nie. Istnieje wiele struktur danych i wiele różnych algorytmów o różnym poziomie złożoności. Potrzeby różnych użytkowników informatyki są nieco mniej klarowne od potrzeb potencjalnych użytkowników matematyki. Kiedy zatem wykłada się matem atykę, w iadom o, czego będą potrzebowali inżynierowie. Myślę, że dziś można stwierdzić, czego będą potrzebowały osoby zajmujące się statystyką, ekonomią lub inną dziedziną. Sądzę jednak, że w przypadku matematyków te problemy są nieco prostsze. Z drugiej strony uważam, że informatycy powinni lepiej znać matematykę. To pewna pozostałość z czasów, kiedy byłem matematykiem. A zatem musimy zestawić to pytanie z tym, co nazywamy rzeczywistością: wydziały informatyki w tym kraju5, przynajmniej w ciągu kilku ostatnich lat, miały problemy z przyciąganiem studentów. Nie wiadomo, dlaczego tak się dzieje, ale te wydziały, którym udało się przyciągnąć większą liczbę studentów, znacznie zmieniły programy nauczania. A zatem zakres materiału informatyki, jakiego należy nauczać, zmienia się.

5 Weinberger miał na myśli Stany Zjednoczone — przyp. tłum.

AWK

187

Z pańskich poprzednich wypowiedzi można wyciągnąć wniosek, że złoty środek dla programowania leży pomiędzy czysto teoretycznym podejściem, które może być zbyt daleko od potrzeb realnego życia, a podejściem w pełni praktycznym, umożliwiającym rozwiązywanie problemów poprzez składanie fragmentów kodu z różnych źródeł. Czy to ma sens?

Peter: Oczywiście, że ma, ale największym problem em jest to, że bardzo tru dno stwierdzić, gdzie należy przeprowadzić linię podziału. Wszystko zależy od tego, jakie są ambicje programisty tworzącego kod. Jeśli spodziewamy się, że będzie używany przez długi czas, trzeba napisać go tak, aby łatwo było poprawiać błędy. Inna trudność pojawia się wtedy, gdy oprogramowanie za wcześnie zdobywa zbyt dużą grupę użytkowników. W takiej sytuacji jest bardzo trudn o poprawić jakiekolwiek problemy projektowe. Jeśli piszę kod dla siebie, to za każdym razem, kiedy nie podoba mi się sposób implementacji, po prostu poprawiam go lub zmieniam. Jeśli piszemy program dla stosunkowo niewielkiej grupy, to upływa jakiś czas, zanim użytkownicy zaczną narzekać na wprowadzenie zmian niezgodnych z poprzednią wersją, ponieważ uważają te zmiany za eksperymentalne. Jeśli jednak piszemy program dla dużej grupy lub jeśli jest on używany przez dużą grupę osób, w tedy wprowadzenie zmian niezgodnych z wcześniejszą wersją staje się trudniejsze. Jesteśmy wówczas zmuszeni do pozostania przy wcześniej podjętych decyzjach. Problem ten może dotyczyć długowiecznego oprogramowania. Programiści pobierają kod z różnych źródeł, a problemy, które w nim występują, podlegają propagacji i pozostają w kodzie przez dziesięciolecia.

Peter: Zgadzam się. Sądzę, że w dalszym ciągu w użyciu jest sporo oprogramowania, które napisano wiele lat temu przez ludzi, którzy nie mieli pojęcia, że ich programy przetrwają tak długo. Jednym z czynników, które utrzymują język AWK przy życiu, jest to, że tak wielu użytkowników bierze skrypty napisane przez kogoś innego i modyfikuje je tak, by wykonywały coś innego.

Peter: Tak właśnie jest i prawdę mówiąc, taki był cel projektowy. To jeden ze sposobów używania języka, jaki przewidywaliśmy. Wiedzieliśmy, że będzie używany często. Użytkownicy biorą kod wykonujący operacje zbliżone do tych, które chcą wykonywać, i odpowiednio go modyfikują. Czy ta idea programowania przez przykład może być stosowana w odniesieniu do większych projektów?

Peter: Myślę, że do nieznacznie większych, ponieważ przykład musi być wystarczająco mały, aby był zrozumiały. Najlepiej wykorzystuje się ten sposób w odniesieniu do kodu o rozm iarach kilku wierszy kodu. Kod pow inien mieścić się na jednym ekranie,

1 88

ROZDZIAŁ

SZÓSTY

tak by ktoś, kto go analizuje, potrafił śledzić, co się w nim dzieje. Program powinien być dostatecznie prosty — dzięki temu można będzie na niego spojrzeć i powiedzieć, co należy w nim zmienić, lub co najmniej widzieć tyle potrzebnych zmian, aby można było doświadczalnie sprawdzić, czy proponowane zmiany są słuszne. Idea pisania bardzo krótkich, jednorazowych skryptów brzmi dość kusząco. Czy pańskie doświadczenia z obszernym kodem oraz innymi językami programowania nauczyły pana, kiedy należy modyfikować kod, a kiedy trzeba pisać go od podstaw?

Peter: W praktyce tru d n o rozpocząć pisanie od podstaw. Jeśli społeczność użytkow ników jest niezbyt liczna, można z nimi rozmawiać. W innym przypadku, jeśli kod ma dobrze zdefiniowany interfejs, pisanie od podstaw jest możliwe. Jeśli interfejsy nie są dobrze zdefiniowane, a społeczność użytkowników jest liczna, uniknięcie popsucia czegoś wydaje się naprawdę trudne. Niestety, dotyczy to również mniej poważnych zmian. W ynika z tego jednak także dobra wiadomość. Ponieważ każda znacząca zmiana coś psuje, implementacja programu od podstaw nie jest dużo gorszym rozwiązaniem dla użytkowników. Po upływie kilku lat nowy kod będzie niemal na pewno musiał być całkowicie przebudowany. Użytkownicy będą używali go w sposób, o którym programiści nie pomyśleli. Wiele decyzji optymalizacyjnych okaże się dalekich od optym alnych, szczególnie w przypadku korzystania z oprogram ow ania na nowym sprzęcie. Doświadczenia z projektu języka AWK są nieco inne. Język był przepisywany kilka razy, ale po zakończeniu implementacji ogłaszaliśmy, że jest to wersja finalna. Aktualizacja mogła być możliwa, ale nasze pomysły wydawały się niezgodne z podstawowymi zasadami. Myślę, że była to decyzja bliska właściwej. Zamiast rozszerzać zakres systemu, wszyscy zajęliśmy się innymi projektami. Jedyną brakującą rzeczą w niewielkiej niszy, jaką zajmuje język AWK we współczesnych środowiskach, jest możliwość użycia danych wejściowych w formacie UTF-8. Brian Kernighan powiedział, że bardzo szybko tw orzył pan implementację. Jaki je s t pański sekret?

Peter: Nie sądzę, abym miał jakiś sekret. Ludzie są po prostu bardzo różni. Na przykład nie jestem pewien, czy gdybym dziś miał robić to samo, byłbym równie szybki. Moja szybkość w pewnym stopniu wynikała z optymistycznej ignorancji. Z przekonania, że mogę coś napisać, i to wystarczyło. W części szybkość pisania wynika ze stosowanych narzędzi oraz dostępnego języka. Dla niektórych narzędzia są wygodne, a dla innych nie. To tak jak zdolność dobierania kolorów przy malowaniu akwarelami. Jednym wydaje się to łatwe, a dla innych jest trudne. Wydaje mi się, że jeśli ktoś chce pisać kod zawodowo, musi m u to wychodzić łatwo. W przeciwnym wypadku cały czas będzie ze sobą walczył. Z pisaniem programów jest tak jak z pisaniem krótkich historii: jeśli dla kogoś nie wydaje się to łatwe na pewnym

AWK

1 89

poziomie, będzie m u sprawiało trudności na każdym poziomie, chociaż nie wiem tego na pewno, ponieważ nie potrafię pisać takich historii. D oprowadzenie pracy do ostatecznego kształtu wymaga dużych wysiłków. Czy pisze pan prototypy, a następnie je modyfikuje, tak by uzyskać kod profesjonalnej jakości? Czy też dużo pan eksperymentuje, a następnie przepisuje kod od podstaw w celu uzyskania pracy w ostatecznym kształcie?

Peter: Myślę, że nie można tego stwierdzić z góry. Kiedy zaczyna się pisać prototyp, czasami m ożna stwierdzić, jakiego rodzaju kom prom isy stosujemy. Niekiedy kompromisy te oznaczają, że prototypu nie można łatwo przekształcić na ostateczny program. Istnieje jednak wiele czynników, które mogą utrudnić taką transformację. W takim przypadku po prostu trzeba przepisać kod od podstaw . Przy odrobinie szczęścia m ożna przekształcić kod krok po kroku. Należy przygotować się na ewentualność konieczności wyrzucenia kodu i rozpoczęcia pracy od początku. Z jednej strony istnieje małe prawdopodobieństwo, że podejmiemy wystarczająco dużo prawidłowych decyzji. Piszemy prototyp, zaczynamy z nim eksperymentować i zmieniamy różne elementy. Po jakimś czasie, jeśli nie mamy dużo szczęścia, kod zaczyna wyglądać okropnie. Na pew no w ymaga co najmniej refaktoryzacji, a najprawdopodobniej napisania od początku. Tego właśnie należy oczekiwać. Zwykle należy napisać kod od początku. Pierwsza implementacja języka AWK była jedynie dowodem na popraw ność koncepcji. AWK generow ał bowiem kod w języku C. Oczywiście jest to sposób działania całkowicie niespójny z tym, czego oczekują użytkownicy. Tom Kurtz, twórca BASIC-a, powiedział, że pisanie kodu pozwala zrozumieć aspekty problemu, o których się nie myślało.

Peter: To prawda. Zdarza się, że pojawiają się rzeczy, o których program ista nie pomyślał, ponieważ nie miał odpowiedniej wiedzy na tem at problemu, zanim faktycznie nie stanął przed koniecznością jego rozwiązania. Myślę, że jednym z elementów, na które należy zwracać uwagę podczas zatrudniania ludzi, jest to, czy pisanie kodu stanowi dla nich n atu ralną formę w yrażania myśli. W ażne, czy właśnie w ten sposób wyrażają swoje pomysły na implementację algorytmu. Jakie różnice występują pomiędzy pisaniem oprogramowania a tworzeniem języka?

Peter: W pewien sposób pisanie języka jest prostsze od pisania uniwersalnego oprogramowania, choć nie jestem pewien, czy mam rację. Pisanie języka polega głównie na dokonywaniu wyborów. Poszczególne elementy muszą do siebie pasować, a istnieje stosunkowo niewielka liczba sposobów wykonania każdej operacji. Kiedy zdecydujemy już, jak będą wyglądały największe konstrukcje języka, mamy w głowie pewną ramę: jak będą działały funkcje, czy będziemy korzystali z mechanizmu odśmiecania, jakie są prymitywy języka itp. Implementację tworzy się warstwami. Myślę, że pisanie języka

190

ROZDZIAŁ

SZÓSTY

jest nieco łatwiejsze od pisania programów ogólnego przeznaczenia. Oczywiście jeśli zbyt późno odkryjemy, że podjęliśmy złą decyzję projektow ą, m usim y wszystko wyrzucić i zacząć od początku. Czy implementacja wpływa na projekt języka?

Peter: Och, z całą pewnością! Można to bardzo łatwo zaobserwować. Na przykład przez długi czas implementacja mechanizmu odśmiecania w języku była specjalną w łasnością. Pracował nad tym zespół języka Lisp oraz twórcy program ow ania funkcyjnego, a wiele innych osób tylko czekało, ponieważ nie było do końca pewne, jak ta własność powinna działać, na przykład w językach zbliżonych do C. Następnie członkowie zespołu Javy stwierdzili, że to jest własność, którą zamierzają zrealizować. W prowadzenie tego mechanizmu w taki sposób, aby w języku były wprowadzane stosunkowo niewielkie zmiany, wymagało kompromisu. Nie mówię, że to właśnie stało się w przypadku Javy, ale jeśli porzuciło się ideę dostępności rzeczywistego adresu pamięci dla programistów, można było zdecydować, czy należy zastosować mechanizm odśmiecania, kompaktowanie, czy też inne mechanizmy. Myślę, że języki bez mechanizmu odśmiecania są w pewnym sensie niedoskonałe, choć działanie tych m echanizm ów jest dalekie od doskonałości. Walka z alokow aniem pamięci to wielki problem , który nigdy nie będzie trywialny. Dziś wiemy jednak znacznie więcej na temat tego, jak należy implementować języki. Mamy też znacznie więcej operacji do wyboru, zwłaszcza jeśli tworzymy prosty język. Jeśli chcemy zrealizować w języku własność, której zaimplementowanie jest trudne, nie mamy pewności, czy warto podejmować ten trud i staramy się ją zastąpić czymś nieskomplikowanym. Jeśli próbujemy stworzyć język, który ma służyć do realizacji trudnych operacji, możemy stworzyć listę operacji koniecznych do w ykonania, a następnie rozwiązywać kolejne problemy. W jakim stopniu język wpływa na wydajność programistów? Czy zdolności programistów mają przy tym jakieś znaczenie?

Peter: Chciałbym znać odpowiedź na to pytanie. Kiedyś myślałem, że ją znam. Oczywiste jest, że programiści bardzo różnią się od siebie zdolnościami. Gdyby zmierzyć ich zdolności, różniłyby się dziesięciokrotnie, a może naw et znacznie bardziej. W ytwarzanie oprogramowania to rodzaj działalności inżynierskiej, a moja teza nie ma żadnego empirycznego potw ierdzenia. Uważam, że języki nie pow inny mieć znaczenia. Jeśli zatem mamy grupę osób i pewien projekt, to nie ma znaczenia, jakiego języka używa się w projekcie. Jednak dla indywidualnych programistów to już ważna kwestia. Myślę, że pewne osoby, ze względu na cechy osobowe, wcześniej opanowaną wiedzę lub jakieś inne czynniki, pewne rodzaje języków przyswajają łatwiej niż inne. Często zatem prowadzone są zabawne dyskusje na ten temat. Oczywiste jest, że istnieją aplikacje, na przykład w języku Lisp, w których zaim plem entow anie tych samych własności w języku C byłoby trudne. Są też takie

AWK

191

aplikacje w C, które trudno byłoby zaimplementować w języku Lisp. Dla bardzo wielu program ów m ożna zastosować różne języki. Przypuszczam jednak, że nie wszyscy program iści, naw et jeśli m ają doświadczenie, będą czuli się jednakowo dobrze w różnych językach, i nie rozumiem, dlaczego tak jest. Aby stać się ekspertem w języku, oczywiście potrzeba czasu. Z drugiej strony niektórym osobom poznanie pewnych języków zajmuje mniej czasu w porównaniu z innymi językami. Ludzie dyskutują na tem at języków oraz tego, które z wielu pożądanych własności będą implementowali, a których nie, także tego, jak złe jest to, że nie implementują tych własności itp. Nie m a jednak jasności, czy to rzeczywiście ma znaczenie. Podsumujmy: oprogram ow anie lądow nika na Marsie m ożna zaim plem entow ać za pomocą dowolnego języka. Każda implementacja będzie zależała od ludzi, którzy ją pisali, oraz od wewnętrznej organizacji w większym stopniu niż od użytego języka. Każdy będzie obstaw ał przy swoim wyborze, ale ja po prostu nie wierzę w takie argumenty. Język C nie obsługuje obiektów, ale jednocześnie pozwala budować niewielkie narzędzia, komponenty systemu Unix, które można wykorzystać wspólnie do tworzenia złożonych mechanizmów. Do jakiego stopnia budowanie obiektów wewnątrz języka jako składowych dużego programu jest lepsze od budowania komponentów będących częścią systemu?

Peter: Pańskie pytanie rodzi w mojej głowie dwa inne pytania. Jedno z nich dotyczy pow iązań bądź modularności. Pytanie dotyczy tego, co należy umieścić w języku, a co można zestawić z narzędzi. Jeśli komponenty są częścią składową języka, to można uzyskać bardziej złożone relacje pomiędzy nimi. Niektóre z nich dotyczą wydajności obliczeń, natomiast inne są związane ze spójnością koncepcyjną. Drugie pytanie wiąże się z programowaniem obiektowym jako ogólną ideą. Myślę, że jest wiele przesady w w ychw alaniu sukcesu program ow ania obiektowego. Pozostawimy tę dość kontrowersyjną tezę bez komentarza. Jeśli przyjrzeć się różnym językom, twórcy wielu z nich twierdzą: „Nasz język jest obiektowy”. Jeśli jednak bliżej przyjrzeć się tym językom, okazuje się, że w każdym z nich zastosow ano różne rozwiązania. Nie zawsze jest jasne, co oznacza termin „obiektowy” . Podejmowane są bardzo kontrowersyjne dyskusje na temat obiektowości, ponieważ każdy twórca języka odczuwa naturalną pokusę, by sądzić, że programowanie obiektowe oznacza właśnie to, co on stworzył w swoim języku. Myślę, że term in ten nie ma prostej i powszechnie akceptowanej definicji. Jaki wpływ na bezpieczeństwo kodu ma wybór języka programowania?

Peter: Z całą pewnością potrzebny jest mechanizm wspomagający realizację różnych aspektów bezpieczeństwa. Myślę, że ogólnie w programach występują dwa problemy związane z bezpieczeństwem. Jeden z nich to błędy logiczne: użytkow nik zleca program ow i wykonanie jakiejś operacji, a w programie powstaje błąd — w efekcie

1 92

ROZDZIAŁ

SZÓSTY

użytkownik uzyskuje uprawnienia do wykonywania działań, do których nie powinien mieć dostępu. Drugi problem to przepełnianie buforów, czyli różnego rodzaju błędy w im plem entacjach, które m ogą być wykorzystane przez krakerów. Są to błędy, o których rzadko się myśli. Sądzę, że większości z nich po prostu nie pow inno być. Stosowanie języków programowania niskiego poziomu sprzyja zaistnieniu błędów przepełnienia bufora — mogą one powstać w wyniku nieuważnego programowania, dlatego trudno im przeciwdziałać. Parę lat temu można było słyszeć pogłoski, że firma Microsoft przy okazji projektu 0 nazwie Vista planuje przepisać cały kod napisany w C i C++ na C# oraz że rozwiąże to problem przepełnień bufora, ponieważ w języku C# przepełnienia buforów nie m ogą wystąpić. Oczywiście zamiaru tego nie udało się zrealizować. Zamiast tego firma Microsoft stworzyła rozbudowane mechanizmy utrudniające wykorzystanie przepełnień bufora, bazujące na programach wykonywalnych w języku maszynowym. Istnieje również inny problem zabezpieczeń występujący na styku pomiędzy program am i. Jego przyczyną jest to, że wiele interfejsów nie zostało dobrze wyspecyfikowanych lub w ogóle jest pozbawionych specyfikacji innej niż nieformalna. Problemem takim mogą być na przykład skrypty krzyżowe HTTP i XML oraz podobne do nich mechanizmy. Trzeba coś zrobić z bezpieczeństwem, ale naprawdę nie wiem co. W jakim stopniu pomagają bariery zastosowane w języku, utrudniające powstawanie określonych problemów?

Peter: Pewnie trochę pom agają, ale tak jak już w spom inałem , nie wiem, w jakim stopniu. Kiedyś sporo pisałem w Pythonie. W moim kodzie zdarzało mi się wiele zabawnych błędów, które oczywiście były spowodowane nierozważnym myśleniem 1 złym stylem. M echanizm kontroli wcięć w Pythonie działał jednak tylko wtedy, gdy pętla nie była zbyt rozbudowana. Miałem w programie zagnieżdżoną pętlę — aby dostać się na koniec pętli, byłem zm uszony cofnąć się o dwie tabulacje w celu wykonania działań poza pętlą wewnętrzną. Nie dałem jednak dwóch tabulacji, tylko jedną. M yślałem, że to wystarczy, ponieważ tak to w yglądało na ekranie. Oznaczało to oczywiście tyle, że wykonywałem kosztowne obliczenia przy każdej iteracji pętli wewnętrznej, co było bardzo nierozsądne. Program co prawda działał poprawnie, ale bardzo wolno. Morał tej historii jest taki, że niezależnie od tego, jak dobrze zaprojektujemy język, programista zawsze może popełnić głupie błędy. A na pytanie o to, czy można naukowo podchodzić do zmniejszania lub zwiększania prawdopodobieństwa takiego działania, nie znam odpowiedzi. Inżynieria oprogram ow ania pod wieloma względami jest dziedziną niespełnioną, ponieważ duża jej część to anegdoty oparte na osobistych sądach pojedynczych osób. Nie jest dla m nie jasne, jakie kryteria oceny języków programowania są bezpośrednio i nierozerwalnie związane z pisaniem prawidłowych programów lub programów łatwych do utrzymania, czy też takich, które się łatwo modyfikuje.

AWK

193

Badania zwykle pom agają w implementacji, ale aspekty projektowe zazwyczaj odzwierciedlają osobiste preferencje projektanta.

Peter: Tak, to prawda. W języku programowania, który odniósł sukces, jest niewiele elementów niebędących przykładami tego, co pojawiło się w literaturze. Ktoś decyduje, że jakiś temat będzie interesujący. Wszystko, co znajduje się w języku programowania, przydaje się do myślenia na tem at języków i mówienia o językach. Nie jest jednak jasne, co należy zrobić w języku lub w jaki sposób go użyć, aby kod, proces program ow ania czy proces pielęgnacji stały się lepsze. Każdy ma na ten tem at odpowiedni pogląd, ale nie jest dla mnie jasne, dlaczego powinniśmy w to wierzyć. Nie wydaje się, aby poglądy te znajdowały potwierdzenie w nauce. Zastosowanie naukowego podejścia do projektowania języka jest trudne częściowo dlatego, że nie znamy naukowego sposobu mierzenia dobrych i złych stron języka.

Peter: Zgadzam się. Myślę, że dotyczy to ogólnie dobrych i złych cech programowania, a nie tylko języków. Jest wiele osób, które uważają, że znają rozwiązania, ale nie jest dla m nie jasne, dlaczego mielibyśmy im wierzyć, istnieje przecież wiele różnych skutecznych sposobów tworzenia programów. W ja k i sposób wybiera pan właściwą składnię dla języka? Czy koncentruje się pan w większym stopniu na warunkach brzegowych, czy też na potrzebach przeciętnego użytkownika?

Peter: Jak łatw o odgadnąć, m oja odpowiedź brzmi: „I na jednym, i na drugim ” . Składnia powinna być zrozumiała dla człowieka o przeciętnej inteligencji. Jednocześnie pow inno być jasne, jaka obowiązuje sem antyka dla wartości brzegowych. Język, który odnosi sukces, jest używany przez wiele osób. Większość z nich nie podziela pu nktu widzenia p rojektanta lub jego poczucia estetyki. Najlepiej, jeśli język nie zwodzi swoich użytkowników dziwnymi własnościami lub warunkami brzegowymi. Znajdą się również osoby, które będą pisały programy generujące programy w nowym języku. Może to być zaskakujące dla implementacji. Czy podczas projektowania języka pam ięta pan o zapewnieniu możliwości debugowania?

Peter: To podchw ytliwe pytanie. Programista oczekuje pom ocy od środowiska projektowego w zakresie działań łatwych do przeprowadzenia. Do mechanizmów, które potrafimy wykonać, należą podpowiedzi uzupełniania ciągów, listy parametrów, pokazywanie referencji oraz wyświetlanie definicji. Oczywiście trudniej jest znaleźć funkcję, która coś wykonuje, a której nazwy nie znamy. Na przykład mamy pewność, że gdzieś jest funkcja form atująca liczby na postać zrozum iałą dla kom putera, oddzielająca trójki zer spacjami lub wykonująca podobne działania. Nie potrafimy jednak zapam iętać nazwy. W jaki sposób m ożna znaleźć nazwy takich funkcji?

194

ROZDZIAŁ

SZÓSTY

Autorzy bibliotek starają się stosować konwencje nazewnictwa, nieformalne konwencje nazewnictwa i tym podobne rzeczy. Wiele jednak spośród tych mechanizmów nie skaluje się zbyt dobrze. A co pan sądzi o dobrych komunikatach o błędach?

Peter: Cóż, byłoby dobrze, gdyby były dostępne. Zwykle w komunikatach o błędach nie podoba mi się to, że wyglądają one jak notatki z program u do samego siebie. Powinny raczej sugerować użytkow nikowi, co należy zmienić. W niektórych przypadkach komunikaty o błędach są jeszcze gorsze. Jaki jest pożądany poziom szczegółowości komunikatów o błędach?

Peter: Powinny być na tyle pomocne, na ile się da, ale to w zasadzie nie jest odpowiedź na pytanie. Niektóre rodzaje błędów w językach programowania są znacznie trudniejsze do opisania niż pozostałe, choć z całą pewnością można zrozumieć heurystykę. A zatem w językach z rodziny C błędy w separatorach i naw iasach zazwyczaj sprawiają kompilatorom kłopoty. W takich przypadkach kompilator ma trudności w objaśnieniu, czego dotyczy błąd. Programiści jednak z czasem zaczynają rozpoznawać, co mówi kompilator w sytuacji, gdy zdarzy się nam opuścić średnik pomiędzy definicją klasy a następną funkcją. Łatwo rozpoznać, że bezsensowny kom unikat o błędzie nie ma nic wspólnego z tym, co się rzeczywiście stało. Zanim kom pilator dostrzegł pomyłkę, był już zbyt daleko w analizie następnej funkcji. W przypadku pominięcia zamykającego nawiasu klamrowego uzyskuje się podobnego rodzaju niezrozumiały komunikat o błędzie. Jedynym wyjściem jest nauczenie się rozpoznawania tego rodzaju problemów. Można by postarać się stworzyć lepszy mechanizm zgłaszania błędów, ale wydaje się, że to dużo pracy. Ponadto nie do końca wiadomo, czy warto. Powyższe pytanie m ożna sform ułow ać nieco inaczej: czy chcesz, aby program generował głupie kom unikaty o błędach, dające wskazówkę, co w programie jest niepraw idłow e, czy też wolisz otrzymywać przydatne kom unikaty o błędach przypominające wskazówki, które Microsoft umieścił kiedyś w Wordzie. Wskazówki te nigdy nie były zbyt przydatne i zawsze sprawiały wrażenie, że są złe. Myślę, że jeśli ktoś chciałby otrzymywać rozbudow ane kom unikaty o błędach, musiałby ciężko pracować nad tym, aby w większości były prawidłowe. Częściowo wynika to jednak z tego, że jesteśmy przyzwyczajeni do przeciętnych komunikatów o błędach, z których można wywnioskować, o co chodzi.

Oczekiwanie na przełom W ja k i sposób zmieniłby pan AWK w celu poprawienia możliwości tworzenia dużych programów?

Peter: Jeśli wziąć pod uwagę wszystko to, co zdarzyło się od m om entu stworzenia AWK, pytanie powinno raczej brzmieć tak: czy doszlibyśmy do języka w stylu Perlą,

AWK

1 95

czy też postać języka byłaby inna? Cóż, nie sądzę, abyśmy mieli odpowiednie umysły do tego, by wymyślić Perlą w całej jego doskonałości. Jeśli jednak przyjrzymy się charakterowi rozwoju języka AWK, to — gdyby przyjąć, że miałby on być używany do dużych programów — coś podobnego byłoby możliwe. Myślę, że inną odpowiedzią jest to, że zatrzymaliśmy się tam, gdzie się zatrzymaliśmy, ponieważ wydawało się, że jest to dobre miejsce do zatrzymania się. Nie pamiętam, czy tego już nie opowiadałem — w kilka miesięcy, a może w kilka lat po opublikowaniu AWK wewnątrz firmy otrzymałem telefon od kogoś z centrum obliczeniowego, kto miał pewne kłopoty z tym językiem. Przyjrzałem się jego programowi i powiedziałem, że przewidywaliśmy, że język AWK będzie używany do jednowierszowych, niewielkich programów. Człowiek, który do mnie dzwonił, napisał w AWK język asemblera dla pewnego ezoterycznego sprzętu i było to 55 stron kodu. Byliśmy zdumieni. Nie jest dziwne, że można to zrobić — ludzie piszą dłuższe programy w językach o uboższej strukturze — ale ta sytuacja nas zaskoczyła. Brian Kernighan powiedział, że prawie zawsze, kiedy zaprojektuje się niewielki język, ludzie zaczynają go używać, a następnie pytają o pętle i inne rzeczy. Zawsze trzeba się gdzieś zatrzymać, w innym przypadku język będzie stawał się...

Peter: ...coraz większy, a twórca języka musi zdecydować, czy chce podążać tą drogą, czy nie. Aby stworzyć język ogólnego przeznaczenia, lepiej zdefiniować ten cel na początku projektu. To lepsze rozwiązanie niż wyjście od niewielkiego języka i rozwijanie go, jeśli osiągnie sukces.

Peter: Myślę, że to także prawda. To inny aspekt, który przywodzi mi na myśl pewną historię. W większości przypadków ludzie piszą programy, na przykład języki, parsery lub kompilatory, i myślą o użytkownikach wprowadzających dane. Można jednak czasami spotkać osoby piszące programy, które generują dane wejściowe. Myślę, że jest to typowa sytuacja podczas implementacji kompilatorów, ponieważ w przypadku języków podobnych do C w innych programach raczej nie spotyka się instrukcji switch składającej się z 80 000 klauzul c a s e . Trudno sobie wyobrazić, aby człowiek był w stanie wpisać instrukcję s witch złożoną z 80 000 przypadków. Być może generator kodu nigdy nie widział instrukcji switch o tak dużej liczbie przypadków. Takie rzeczy zdarzają się na wszystkich poziomach, nawet w językach ogólnego przeznaczenia. A co powie pan o językach rozszerzalnych, które mogą być modyfikowane przez użytkowników?

Peter: Zależy, co to dokładnie znaczy. Ogólnie myślę, że to dobry pomysł. Istnieje jednak wiele ograniczeń, jakie można napotkać, chyba że chcemy stworzyć narzędzie w stylu Lisp, pozwalające dodawać elementy do języka za pomocą makr. Istnieje wiele

1 96

ROZDZIAŁ

SZÓSTY

powodów, dla których użytkownicy chcą dodawać elementy języka: na przykład popraw a ekspresywności lub potrzeba dołączenia bibliotek realizujących skom plikowane operacje, pisanych w innych językach. N asuwa się kolejne pytanie, na które w języku AWK nie musieliśmy udzielać odpowiedzi: jak trudno dołącza się procedury lub pakiety pisane w innych językach? Odpowiedź na to pytanie nie jest prosta. Ludzi można podzielić na matem atyków i niem atem atyków. Czy istnieje różnica pomiędzy matematyką a wytwarzaniem oprogramowania? Język C odniósł sukces, językom Scheme i Lisp się nie udało. Czy ponownie zwyciężyło podejście „gorsze jes t lepsze"?

Peter: Myślę, że istnieje różnica pom iędzy językami projektow anym i przez m atem atyków a językami, których projektantami nie byli matematycy, ale różnica pomiędzy językami Scheme i C to w większym stopniu różnica pomiędzy językiem, który próbuje opierać się na prostych podstaw ach, i takim , który tego nie robi. Nie ma w tym niczego oczywistego. Nie wiemy, co się dzieje. Oczywiście nie istnieje jednoznaczna odpowiedź na pytanie, co sprawia, że język odnosi sukces. Trzeba uwzględnić wiele czynników. Każdy m a ulubione dziedziny i obszary działania. Nikt nie wie, jak uwzględnić je wszystkie. Język Lisp w swojej początkowej formie m iał bardzo prosty m odel działania i z tego pow odu dawał zaskakująco duże możliwości. Można by zadać pytanie, dlaczego twórcy Lispa musieli skomplikować swój język. Czego nie zrobili na początku, a co wymagało dodatkowych komplikacji? Oczywiście we wszystkich pośrednich postaciach Lispa, następnie w języku Scheme i ostatecznie w Common Lisp są dodatkowe komplikacje. Prawdopodobnie występują tu dwie rzeczy. Nie myślałem o tym wcześniej, ale spróbuję wyjaśnić to na gorąco. Jedna rzecz to wygoda programisty. Inna to wydajność. Potem przejdziemy do innych języków, choć myślę, że języki z rodziny Lisp klarownie prezentują wiele mechanizmów. Jedną z podstawowych różnic pomiędzy pierwotnym Lispem a językiem Common Lisp są dodatkow e typy danych: tablice asocjacyjne i tym podobne konstrukcje. W prowadzono je głównie ze względu na wydajność. Z drugiej strony — moja wiedza jest naprawdę bardzo płytka w tym przypadku, niewiele wiem na tem at tego języka — w pewnym momencie twórcy Lispa zaczęli umieszczać w nim makra. Pod pewnym względem m akra są bardzo naturalnym m echanizm em . Istnieje dla nich inne środowisko obliczeniowe, ale — i to dotyczy wygody programisty — makra nie robią niczego takiego, czego programiści sami by nie mogli napisać. Działają ja k turbodoładowanie?

Peter: Takie są oczekiwania. Ale również wprowadzają dodatkową złożoność. Jeśli używa się ich zbyt często, kod staje się nieczytelny dla innych osób. Z powodu makr

AWK

197

o wiele trudniej powiedzieć, co kod robi — naw et nieformalnie. Istnieje wiele przypadków brzegowych. N awet w tym stosunkow o jednorodnym środowisku m ożna zaobserwować konflikt pomiędzy m atem atyczną czystością a sprawnością wykonywania działań, które są do wykonania. Taki sam problem dotyczy wszystkich języków z rodziny Lisp, z których wiele ma bardzo form alne definicje semantyczne. W co najmniej dw óch fazach życia program u biorą udział ludzie. Biorą też udział kom putery. Aby program był zadowalający dla komputera, potrzebna jest precyzyjna definicja tego, co oznacza kod pisany przez ludzi. Ludzie potrzebują czegoś, co nie jest trudne do pisania. Po jakimś czasie używania program u często się zdarza, że programiści, którzy go utrzymują, muszą mieć możliwość jego przeanalizowania i zmodyfikowania. W mojej opinii wiele własności ułatwiających pisanie kodu w niektórych językach w prow adza dodatkow ą trudność dla program istów pielęgnujących kod. Dość zaskakującym przykładem mogą tu być niemal wszystkie języki obiektowe. Języki praktyczne, w których precyzyjna semantyka jest klarowna tylko dla kom pilatora oraz jego twórcy, są bardzo cenne dla osób piszących nowy kod. Przydałyby się również języki pozwalające na wyrażanie intencji w sposób czytelny dla ludzi niedysponujących żadnym i innym i wskazówkami oprócz kodu. Nie przychodzą mi na myśl żadne przykłady. Oczywiście nie miałem okazji pisania we wszystkich językach. Jednak wśród znanych mi języków istnieją takie, które są nieco lepsze bądź nieco gorsze w wymienionych obszarach. M ów i pan o dwóch różnych biegunach.

Peter: Tak, to prawda. Pisaliśmy kod w starych dobrych czasach, kiedy po Ziemi chodziły smoki i inne giganty. Karły występowały tylko w wielkich skrzyniach. Nikt nie sądził, że kod, który pisaliśmy, przetrwa 30 lat. Gdyby ktoś powiedział, że za 30 lat w dalszym ciągu będzie w użyciu Unix lub FORTRAN, naturalną odpowiedzią byłoby: „Tak, i my go przepiszemy od podstaw ” . To był nasz sposób działania. Najpierw pisaliśmy kod, a potem go przepisywaliśmy. Za każdym razem był on trochę niezgodny z poprzednią wersją i w pewnym stopniu lepszy, aż do momentu osiągnięcia syndromu drugiego systemu (ang. second system effect), kiedy system stawał się drastycznie niezgodny z wcześniejszymi wersjami i znacznie gorszy. Uważaliśmy, że w pewnym sensie można wyróżnić tylko dwa rodzaje projektów oprogramowania: takie, które kończyły się niepow odzeniem , oraz takie, których finałem był horror związany z utrzymaniem poprzednich wersji. Nie dopuszczaliśmy do siebie myśli, że nie można w nieskończoność pisać nowego oprogramowania i jednocześnie przepisywać starego. Programy się rozrastają i albo dojdzie do sytuacji, kiedy będziemy przez cały czas przepisywać stare oprogramowanie, albo wymknie się nam ono spod kontroli. Nie m ożna robić i jednego, i drugiego. Problem pielęgnacji do pewnego stopnia ciągle się rozrasta. Mówię to z mojego doświadczenia w pracy w firmie Google, gdzie ten problem był bardzo poważny.

1 98

ROZDZIAŁ

SZÓSTY

Pozostawiam tę nierozwiązaną kwestię — moje wcześniejsze dywagacje na temat różnic pomiędzy językami matematycznymi i niematematycznymi — by dodać, że istnieją języki projektowane przez matematyków, eksmatematyków albo ludzi myślących jak matematycy oraz języki projektowane przez inne osoby. Myślę, że od tych pierwszych oczekiwałbym prawie kompletnej specyfikacji, przynajmniej nieformalnej. Powinien znaleźć się opis działania języka we wszystkich możliwych okolicznościach, jakie można sobie wyobrazić. Powinna to być historia leksykalna, a nie wskazówki na jej temat. Pomimo napisania całej reszty może się zdarzyć, że sukces nie zostanie osiągnięty. Simon Peyton Jones powiedział, że dla około 8 5 % konstrukcji Haskella udało się stworzyć formalne specyfikacje. Normalizowanie pozostałej części było zwykłą stratą czasu.

Peter: Czasami można być zbyt ostrożnym. Elementami, które nie działały tak dobrze, jak mogłyby, były w języku C typy short int lub 1ong. Do wyboru były dwie możliwości. A dodatkowo oznaczenia znaku.

Peter: Proszę, nie zaczynajmy nawet tematu znaków lub danych typu const. Można było powiedzieć: „Będą typy int8, int 16, int24, int36, int64, cokolwiek oznaczają, a język albo będzie zapew niał ich dokładną im plem entację, albo będzie dążył do tw orzenia jak najdokładniejszych przybliżeń” . To wszystko m ożna powiedzieć z perspektywy czasu. Nie jestem pewien, czy potrafiłbym zrobić to lepiej, gdybym zaczynał od początku. M ożna również powiedzieć: „Słuchajcie, m am y typy short int i long. Powiemy, co oznaczają, i na tym koniec. W innych przypadkach musicie sobie jakoś radzić sami” . Twórców kom pilatorów nie pow inno dziwić to, że chcą zapewnić swoim użytkow nikom jak największą wydajność. Zjawiska te m ożna zaobserwować na przykładzie kompilatora CCC, który hojnie obdarował użytkowników typami. W związku z tym są zwolennicy typów unsigned, a także orędownicy wskaźników oraz wszystkich innych rzeczy, o których nawet trudno pomyśleć. Interesującym przykładem są również ciągi znaków. W pewnym momencie dochodzimy do wniosku, że jeśli pozwolimy na występowanie w nich dowolnych znaków zamiast na przykład ciągów UTF-8 lub ASCII, to nie będzie można ich wyświetlić. Chociaż nie brzmi źle takie sformułowanie: „Tak, wszystkie struktury w moim języku są binarne, poza tymi, które zostaną jawnie przekształcone do form atu umożliwiającego wyśw ietlanie”, to jest z tym duży kłopot. Potrzebny jest prawdziwy pojęciowy przełom. Potrzebny jest od bardzo dawna. Ja jestem pesymistą.

AWK

1 99

Przy panu ja też staję się pesymistą.

Peter: Przepraszam. Zabawne jest to, że wszystkie te m echanizm y to w zasadzie rodzaj funkcji i właściwie można na nich polegać. Nie dają żadnych gwarancji, ale ostatecznie m ożna na nich polegać. Jak w iadom o, już daw no tem u sam ochody straciły możliwość funkcjonowania bez komputerów. W samochodach działa bardzo dużo kodu. W większości przypadków kod ten działa prawie bez przerw. Wiemy, że nie ma żadnej gwarancji ich poprawnego działania, i wiemy, że są sytuacje, w których komputery te muszą się zresetować w czasie, gdy jesteśmy na autostradzie, ale polegamy na nich. W pewnym sensie narzekam na niedoskonałości, a nie wady uniemożliwiające wykorzystywanie systemu. Owe niedoskonałości są jednak uciążliwe. Jakiego rodzaju przełomu potrzeba do tego, aby zacząć rozwiązywać te problemy?

Peter: Spróbuję zaprezentować kilka obserwacji. Z jednej strony m amy olbrzymią moc obliczeniową komputerów, na które piszemy oprogramowanie. Większość tej mocy obliczeniowej po prostu się marnuje. Zdumiewająco duża jej część jest zużywana na obsługę interfejsów użytkownika. Jeśli na przykład używamy języka C, to znaczna część mocy obliczeniowej zostaje zużyta na wczytywanie danych do pamięci oraz zapisywanie różnych wersji plików tymczasowych, tak aby mogły być ponow nie wczytane do pamięci. Zgadza się? W przypadku języków o przeciętnej strukturalnej integralności, w których można skorzystać z aliasów oraz wielu innych mechanizmów, można by powiedzieć, że kompilatory mogłyby wykonać znacznie lepszą pracę. Oznacza to, że programista musi mieć jakiś sposób na wyrażenie swoich zamiarów. Okazuje się, że cel ten jeszcze nie został osiągnięty. Rzeczywistość jest coraz gorsza. M ówienie o w ątkach i m echanizm ach obiektowych, że są proste, jest wyjątkową wspaniałomyślnością. Są one raczej obrzydliwie zagmatwane. Języki zawierają cały ten balast. Trzeba usunąć dużą część mechanizmów, która sprawia, że jest bardzo trudno powiedzieć, co program robi. Staje się to szczególnie uciążliwe w świecie procesorów wielordzeniowych. Być może jest wielu inteligentnych ludzi, którzy wykonują bardzo interesującą pracę. Być może nastąpi jakiś postęp. Jedynym narzędziem, które m am y do dyspozycji, jest poproszenie komputerów o to, by pomogły programom być bezpieczniejszymi i czytelniejszymi. Można to osiągnąć poprzez stworzenie lepszych języków. Trudno powiedzieć, jakie to mają być języki. To wielki kłębek nici. T rudno stwierdzić, co się dzieje z tymi wszystkimi pętelkami, które zaczynają się wzajemnie przenikać. Gdzie jest koniec? Nie mam pojęcia. Nie jestem bezwzględnym optymistą. Z drugiej strony nie jest tak źle. Niektóre elementy są tylko trochę denerwujące. W ja k i sposób definiuje pan sukces w swojej pracy?

Peter: Kiedy tworzyliśmy język AWK i podobne narzędzia, w ydawało nam się (choć może tak nie było), że jeśli będziemy mieli dobre pomysły i je dobrze

200

ROZDZIAŁ

SZÓSTY

zaim plem entujem y, to niewielkim nakładem pracy wywrzemy wielki wpływ na technikę kom puterow ą. Ten stan rzeczy niestety dziś już nie jest prawdziwy. W tedy było dość łatwo mieć istotny wpływ na znaczącą część świata komputerów. Dziś to już nie jest takie proste. Myślę, że m ałych grup osób, które mają duży w pływ na świat kom puterów , jest stosunkow o niewiele. Posiadanie dużego wpływ u nie jest łatwe. Osiągnięcie sukcesu nie jest niemożliwe — to oznacza, że wiele osób korzysta z program ów i uważają je za dobre, ale według mnie nie ma się już tak dużego wpływu na rozwój w ypadków w technice obliczeniowej. Bardzo trud no znaleźć przypadek — ekstremalnym przykładem tego jest Unix — w którym stosunkowo nieliczna grupa osób stworzyła system istotnie zmieniający świat. Być może któryś z Czytelników tej książki pokaże przykłady podobnych systemów powstałych w ciągu ostatnich 5 lub 10 lat, a które ja przeoczyłem, choć nie sądzę, aby takie istniały. Myślę, że dziś tworzeniem tego rodzaju systemów zajmują się większe grupy, a zadanie to jest znacznie trudniejsze. Myślę, że odpowiedź na pańskie pytanie pow inna brzmieć następująco: mieliśmy szczęście i wywarliśmy duży wpływ na technikę komputerową stosunkowo niewielkim nakładem pracy. Był to wspaniały m oment i doskonała miara sukcesu. Sądzę, że dziś trudno byłoby osiągnąć podobny sukces nawet znacznie bardziej utalentow anym osobom niż my. Oczywiście pod wieloma względami jest to dobre. Oznacza, że nastąpił znaczny postęp, ale oznacza również, że osoby indyw idualne muszą zadowolić się mniejszymi sukcesami.

Programowanie przez przykład Wspomniał pan, że językow i AWK udało się przetrwać dzięki programowaniu przez przykład.

Peter: Była to przemyślana decyzja projektowa. Istnieje wiele cech języka. Jedne są złe, inne dobre. W języku AWK występuje kolekcja interesujących (to bardzo uprzejmy sposób ich opisania) wyborów syntaktycznych, z których w mojej opinii było tylko kilka prawdziwych pomyłek. W większości przypadków, gdy tworzyliśmy konstrukcje składniowe, dążyliśmy do tego, by przypominały konstrukcje języka C, ponieważ wtedy nie musieliśmy wyjaśniać ich ludziom, z którymi pracowaliśmy. Powstało wtedy pytanie: co dalej? Uważaliśmy, że skoro wszystkie programy w AWK będą zawierały co najwyżej kilka wierszy kodu, to programowanie w AWK będzie polegało na szukaniu przykładów kodu realizującego podobne działania do tych, które chciał uzyskać program ista. W ystarczyło je tylko trochę zmodyfikować. Jeśli ktoś chciał stworzyć coś bardziej skomplikowanego, m ógł bez trudu robić to w sposób przyrostowy. W tym samym czasie, kiedy pracowaliśm y nad AWK, w Xerox PARC realizowano projekt — jego nazwa niestety wyleciała mi z głowy

AWK

201

— który w przybliżeniu odpowiadał projektowi AWK. System miał przetwarzać pliki. Systemy Xerox PARC nie postrzegały plików jako zbioru wierszy, ale tem at był podobny. Miał to być program dla sekretarek. Strona składała się z dwóch kolumn. Po lewej stronie pisało się program , a z prawej strony był działający przykład. Kompilator sprawdzał, czy program robił to, co pokazywał przykład. To sprytne.

Peter: Było sprytne, a twórcy projektu robili wszystko, aby składnia była zrozumiała dla sekretarek. Oczywiście projekt się nie powiódł. Nie odniósł sukcesu w tych obszarach, w których językowi AWK się powiodło z kilku powodów. System Unix był powszechny, a system, na który pisano tam ten program, nie itd. My przejęliśmy z tamtego projektu pomysł, aby znaleźć program, który w ykonuje w przybliżeniu to, co chcemy zrobić. Język AWK był celowo przeznaczony dla programistów. AWK nie działał tak samo jak system z Xerox PARC, ale to, że był stosunkowo prosty, oraz to, że dostępne były przykłady, na których można się było wzorować, a także książka z wieloma przykładami, bardzo pomogło. Poprzez kopiowanie, wklejanie i modyfikowanie programu w najlepszym wypadku można się nauczyć języka na zasadzie osmozy.

Peter: Muszę przy tej okazji dodać, że wyszła książka o AWK. Jej idea była taka, że pomiędzy nieformalnym wprowadzeniem a przykładami znalazł się stosunkowo kom pletny opis tego, czym był język i jakie operacje realizował. Myślę, że to był dobry opis. Książka o AWK spełniała swoją rolę. Czy użytkownicy ją czytali?

Peter: Niektórzy tak, a niektórzy nie. Spróbuję ująć to nieco inaczej. Niezależnie od tego, czy ktoś czytał książkę, czy nie, mógł w AWK programować przez przykład. To fakt empiryczny. A jak było w Adzie? Nigdy nie próbowałem.

Peter: Zgaduję, że w Adzie byłoby bardzo trudno tworzyć programy przez przykład. Jest wysoce prawdopodobne, że tworzenie przez przykład program ów w C++ także byłoby trudne. Mogłoby się powieść tylko w przypadku bardzo prostych programów. Jednak w języku C nie pisze się jednowierszowych programów.

Peter: To oczywiście czyni pewną różnicę. Oprócz programów wykonujących proste operacje na ekranie bardzo trudno jest pisać krótkie programy. Niewielkie przykłady są zadziwiająco obszerne.

202

ROZDZIAŁ

SZÓSTY

Podobnie ja k zakres problemów, które są rozwiązywane.

Peter: Zgadza się. To są języki ogólnego przeznaczenia, a język AWK taki nie jest, chociaż jednym z pierwszych program ów napisanych w AWK był asembler dla pewnego procesora. Byłem przerażony. Jego twórca nie potrafił wyjaśnić, dlaczego zdecydował się na taką implementację. Stało się jednak jasne, że znacznie łatwiej było zastosować język interpretowany, a powłoka nie dawała zbyt wielkich możliwości. Chciałbym, aby programowanie stało się bardziej dostępne dla zwykłych ludzi, ale także wolałbym, aby programy były bardziej niezawodne i by łatwiej komponowały się w większe metaprogramy. Trudno pogodzić te dwie idee.

Peter: Możliwości kompozycji ułatw ia projekt języka i idiomy. Niezawodność? Cóż, niełatwo ją osiągnąć. Podobnie jak przejrzysty projekt. W ja k i sposób rozpoznaje pan przejrzysty projekt?

Peter: To sprawa bardzo względna. Kiedyś byłem znacznie bardziej pewny swoich sądów niż dziś. Patrzysz na kod i próbujesz napisać krótkie przykłady. Myślisz, co ludzie o tym powiedzą. Jedna z reguł, która praktycznie zawsze się sprawdza, mówi, że czytelne przykłady prezentowane w podręcznikach są całkowicie nierealistyczne. Czasami może trafić się klasa, która wygląda tak jak ze w stępu do książki o programowaniu obiektowym, ale ja w to nie wierzę. Praktyczne klasy mają bardzo wiele składowych zdolnych do odpowiadania na różne komunikaty. Użyteczny obiekt, który można umieścić w programie, to zwykle bardzo obszerny kod. Myślę, że można pozwolić na pewną złożoność, jeśli korzyści uzyskane w zamian będą znaczące. Czy raczej postrzegane korzyści?

Peter: Oczywiście. Właśnie tak to działa. To jest inżynieria programowania. Niczego nie da się zmierzyć ilościowo. Korzyści postrzegane i realne oznaczają to samo, ponieważ nie możemy zmierzyć realnych korzyści lub nie mamy inklinacji do mierzenia realnych korzyści. Nie możemy nawet mierzyć wydajności, przez co powstają trudności w ocenie tego, co lepsze, a co gorsze.

Peter: Tak jest, ale nie sądzę, aby to m iało jakieś znaczenie. W dziedzinach inżynieryjnych mierzy się efekty. Jeśli budujemy mosty, interesuje nas to, ile kosztowały, czy trudno je było zbudować i czy będą wytrzymałe. W przypadku program ów możemy mierzyć to, jak trudno je było stworzyć czy ile pieniędzy wydano podczas tworzenia, ale reszta jest kompletną tajemnicą. Czy program dobrze robi to, co powinien? Skąd mamy to wiedzieć? W jaki sposób opisać, co program powinien robić?

AWK

2 03

Dla oprogramowania nie istnieje materiałoznawstwo.

Peter: Mam taką nadzieję, że kiedy w końcu twórcom sprzętu wyczerpią się pomysły, będzie istniało więcej zasad inżynieryjnych dla oprogramowania. Na razie wszystko zmienia się bardzo szybko. Może to niezbyt dobra analogia, ale gdyby właściwości betonu i stali co roku zmieniały się o 10%, to inżynieria budow lana wyglądałaby zupełnie inaczej. To są spekulacje, ponieważ nie ma powodu, dla którego musiałaby wyglądać inaczej. To tylko modele powiedzmy na lata 2007, 2008, 2009. Ponieważ nigdy nie dysponowaliśmy żadnymi modelami w oprogramowaniu, sądzę, że na razie nie m ożna stworzyć żadnych zasad. Mówimy o oprogramowaniu. Nie mamy atomów. Nie mamy właściwości fizycznych.

Peter: Rzeczywiście, to niezupełnie tak jak w matematyce. Nie mieści się w całości w ludzkich umysłach. Oprogramowanie jest niemal w całości tworzone przez ludzi, a występujące ograniczenia są pewnymi matematycznymi ograniczeniami mającymi związek z możliwościami obliczeniowymi, złożonością algorytmów oraz wszystkim tym, co otrzymamy od specjalistów w dziedzinie sprzętu. Ta część bardzo szybko się zmienia. Powiedział pan również, że istnieje różnica pomiędzy programistą komputerowym a matematykiem zajmującym się dowodzeniem twierdzeń. Twierdzenie można udowodnić i wtedy dowiadujem y się czegoś nowego. Po napisaniu programu komputerowego nagle okazuje się, że można zrobić coś, czego nie można było zrobić wcześniej.

Peter: W dalszym ciągu uważam, że w dużej części to jest aktualne. Oczywiście nowocześniejszym wersjom twierdzeń czasami towarzyszą algorytmy. Nic dziwnego, ponieważ technika kom puterow a okazała się tak przydatna, że nieco rozmyły się granice jej zastosowań. Zanim nastąpiła era kom puterów , ludzie myśleli o tym, jak tru dno wykonywać obliczenia. Były zagadnienia, które wymagały obliczeń. Autorzy książek naukowych często zamieszczają w nich swoje notatki. Są tam fascynujące, bardzo skomplikowane obliczenia wykonywane ręcznie, a na końcu jest odpowiedź. Nie uważam, aby różnica pomiędzy matematykiem a programistą była zasadnicza. Kiedyś mi się tak wydawało, ponieważ zaczynałem jako matematyk. Czy kiedy zaczniemy myśleć o komponentach ja k o twierdzeniach, nastąpi rewolucja komputerowa?

Peter: Nie nastąpi, aż nauczymy się właściwie opisywać kom ponenty. Najczęściej stosowane opisy są czysto funkcjonalne. Oto sposób, w jaki wejście przekształcono na wyjście. Nie jest pow iedziane nic lub prawie nic na tem at czasu, jaki zajmie to przetwarzanie. Nie ma prawie żadnych informacji o wym aganiach dla pamięci.

204

ROZDZIAŁ

SZÓSTY

Występują mgliste wzmianki na temat środowiska, w którym kom ponent ma działać. Twierdzenia są tworzone przez ludzi, a nie maszyny, ale znalazły się w nich hipotezy i wnioski. Twierdzenia tylko w nieznacznym stopniu podlegają interpretacji, podczas gdy w przypadku oprogramowania odgrywa ona zasadniczą rolę. Mogę podać wiele przykładów. Pomijam tu nawet takie rzeczy, jak uważna specyfikacja wejścia. Są programy, które przestały działać, gdy zostały przeniesione ze środowiska 16-bitowego do 32-bitowego, ponieważ pewne miejsca w tych program ach były wewnętrznie 16-bitowe, ale programista nie zdawał sobie z tego sprawy. Trudno opisać wszystkie te elementy. Ulubionym przykładem niedokładności opisu są programy, których poprawność udow odniono, a które zawierają błędy. Niestety, było w tych programach coś, co niedokładnie modelowało rzeczywistość. Ludzie to przeoczyli. Problem ten dotyczy specjalistów zajmujących się typami danych. Chcą zagwarantować możliwość wyznaczania typów danych metodą indukcji, a to bardzo obniża wydajność systemu. Na drugim krańcu tej wielowymiarowej przestrzeni są szablony C++, które pozwalają na obliczanie wszystkiego na etapie kompilacji z oczekiwaną szybkością. Ponieważ jednak możemy obliczać wszystko, sposób ten jest wolny. To bardziej interesująca kwestia od tego, co w informatyce nas martwi. Komputery nie stają się szybsze. Jedynie ich obszar zastosowań się rozszerza.

Peter: Wykładniczy postęp w dobroci to dobra rzecz. Doszedłem ostatnio do wniosku, że wzrost objętości danych, ja k i nastąpił od lat siedemdziesiątych przyćmił postęp w szybkości procesorów. Weźmy SQL, opracowany w tamtym okresie. Język ten w dalszym ciągu radzi sobie z olbrzymią eksplozją w rozmiarach danych. Inne języki nie wypadają tak dobrze.

Peter: Sądzę, że od lat siedemdziesiątych szybkość procesorów wzrosła w przybliżeniu tysiąckrotnie. Objętość danych nie zwiększyła się tak znacznie. Z doświadczenia w technice komputerowej wiemy, że z czasem osiąga się postęp rzędu 10n. Nie będzie złym przybliżeniem, jeśli powiemy, że 10n/2 z tego postępu to sprzęt, natom iast pozostałe 10n/2 to postęp w algorytmach. Myślę, że tak jest w przypadku SQL. W łożono wiele wysiłku w optymalizację zapytań oraz lepsze zrozumienie projektu bazy danych. Dzięki temu możliwa stała się obsługa terabajtowych baz danych. W ja k i sposób zasoby sprzętowe wpływają na sposób myślenia programistów?

Peter: Programiści za bardzo się różnią pomiędzy sobą, aby można było formułować ogólne twierdzenia na ich temat. Należy pamiętać o ograniczeniach. Na przykład prędkość światła jest taka, jaka jest, i nie poprawia się. Coś, co dobrze działa lokalnie, fatalnie w ypada przy pracy zdalnej. Wszystkie te warstwy abstrakcji i biblioteki pom ocnicze pozwalają szybko tworzyć program y, ale m ają ujem ny wpływ na wydajność i możliwości obliczeniowe.

AWK

205

Czy najpierw wybiera pan właściwe algorytmy, a potem dąży do poprawy szybkości ich działania, czy też koncentruje się pan na szybkości od początku?

Peter: Jeśli to problem, który jest dobrze znany, algorytmy buduje się w taki sposób, aby wydajność była wystarczająca. Następnie można starać się je dostroić, jeśli zachodzi taka potrzeba. Ogólnie rzecz biorąc, ważniejsze jest to, aby algorytm działał. Po w prow adzeniu wielu modyfikacji zm iana organizacji im plem entacji staje się znacznie trudniejsza. Często jednak okazuje się, że coś jest większe lub używane częściej, niż oczekiwano. Tak więc algorytmy o złożoności kwadratowej są niedopuszczalne. Czasami też zbyt dużo czasu zajmuje kopiowanie i sortowanie. Wiele programów działa dostatecznie wydajnie bez zbyt wielkiego wysiłku. Nowoczesne komputery są bardzo szybkie, a większość postrzeganych opóźnień wynika z pracy sieciowej lub obsługi wejścia-wyjścia. W ja k i sposób wyszukuje pan błędy w oprogramowaniu?

Peter: Sztuka polega na wyszukiwaniu problem ów , które m ożna rozwiązać. W oprogram ow aniu występuje wiele problem ów, które okazały się zbyt trudne do rozw iązania. Wiele m echanizm ów, które dobrze działały, przestało działać, kiedy skala problemu zwiększyła się o kilka rzędów wielkości. Co pan robi, kiedy ju ż znajdzie pan rozwiązanie, które działa dostatecznie dobrze?

Peter: Jeśli ma to służyć do mojego prywatnego użytku, po prostu się zatrzymuję, a jednocześnie staram się zapamiętać kontekst na tyle dokładnie, bym mógł później owo rozwiązanie zaktualizować. W przypadku programów na sprzedaż trzeba je dobrze udokumentować, zabezpieczyć przed różnego rodzaju złymi sytuacjami oraz dodać w ystarczająco dużo kom entarzy do kodu, aby ktoś inny nie m iał problem ów z pielęgnacją (by mógł robić to tak samo łatwo jak autor). Ten ostatni warunek bardzo trudno spełnić. Kod zawierający dobre komentarze należy do rzadkości. Jest taka zasada związana z systemem Unix, która mówi: Jeśli nie wiesz, ja k coś zrobić dobrze, nie rób tego wcale". Czy to podejście można rozszerzyć poza zakres ilniksa?

Peter: To jest kwestia, która znacznie wykracza poza oprogramowanie. Przez większość naszego życia musimy robić coś, na czym się dobrze nie znamy. To luksus, że powstał system Unix. Jego twórcy znaleźli sposoby na to, jak dobrze zrobić wiele rzeczy. Interesujące byłoby zbadanie takiej hipotezy — jeśli biznes nie jest realizowany zgodnie z tym podejściem, nie ma możliwości osiągnięcia sukcesu. W ątpię jednak, czy dane byłyby przekonujące. N aturalnym porów naniem są systemy Microsoft i Apple. Jak jednak porównać ogólne uznanie z łącznymi zyskami?

206

ROZDZIAŁ

SZÓSTY

ROZDZIAŁ

SIÓDMY

Lua

Lua to niew ielki, samodzielny, dynamiczny język stworzony w 1 993 roku przez Roberto lerusalimschy'ego, Luiza Henrique de Figueiredo i Waldemara Celesa. Dzięki kompaktowemu zbiorowi doskonałych własności oraz łatwym w użytkowaniu interfejsom API w stylu C język Lua jest ła tw y do osadzania. Jest również rozszerzalny o nowe pojęcia z różnych dziedzin. Język Lua zaznaczył swoje miejsce w świecie oprogramowania komercyjnego. Został wykorzystany w grach, na przykład World o fW a rc ra ft firm y Blizzard oraz Crysis firm y Crytek GmbH. W systemie Photoshop Lightroom firmy Adobe wykorzystano go do obsługi skryptów oraz interfejsów użytkownika. Język wywodzi się od języków Lisp, Schema i w pewnym sensie AWK. Pod względem projektu jest podobny do języków JavaScript, Icon i Tcl.

2 07

Siła skryptów W ja k i sposób zdefiniowaliby panowie język Lua?

Luiz H enriąue de Figueiredo: To szybki i rozbudowany język skryptowy nadający się do osadzania w układach scalonych (ang. embeddable). Roberto Ierusalimschy: Niestety coraz więcej osób używa terminu „język skryptowy” jako synonimu „języka dynamicznego” . Dziś nawet takie języki jak Erlang i Scheme są nazywane skryptowymi. To sm utne, poniew aż straciliśmy zdolność do dokładnego opisywania określonej klasy języków dynamicznych. Lua jest językiem skryptowym w pierw otnym znaczeniu tego wyrażenia. Językiem do zarządzania innym i komponentami, zwykle napisanymi w innym języku. 0 czym powinni pamiętać ludzie projektujący programy napisane w języku Lua?

Luiz: O tym, że istnieje sposób realizacji operacji typow y dla języka Lua. Próby emulowania wszystkich praktyk znanych z innych języków nie są zalecane. Należy używać własności wybranego języka, choć jak zgaduję, dotyczy to w równym stopniu dowolnego języka. W przypadku języka Lua owe własności to głównie tabele różnego przeznaczenia oraz m etam etody do tw orzenia eleganckich rozwiązań, a także podprocedury. Kto powinien używać języka Lua?

Roberto: Myślę, że z języka Lua może korzystać większość aplikacji, które nie zawierają obsługi skryptów. Luiz: Problem polega na tym, że wielu projektantów nie dostrzega takiej potrzeby na początku projektowania. Kiedy ją wreszcie zauważa, jest już za późno — ponieważ większość kodu została już napisana na przykład w C lub C++. W tedy projektanci wiedzą, że nie mogą już nic zrobić. Projektanci aplikacji powinni rozważać możliwość wykorzystania skryptów od samego początku. Skrypty zapewniają znacznie większą elastyczność. Dają również lepszą perspektywę wydajności, ponieważ zmuszają projektantów do myślenia o tym, gdzie aplikacja potrzebuje dobrej wydajności, a gdzie nie ma to znaczenia i m ożna pozostawić możliwość szybkiego tw orzenia skryptów. Co oferuje język Lua programistom z punktu widzenia bezpieczeństwa?

Roberto: Rdzeń interpretera języka Lua został zbudow any jako „aplikacja wolno stojąca” . Jest to termin pochodzący ze standardu ISO dla języka C. Ogólnie oznacza, że program nie wykorzystuje niczego ze środowiska (nie korzysta z m odułu stdio, instrukcji mai loc itp.). Wszystkie te własności są dostarczane przez biblioteki zewnętrzne. Przy takiej architekturze bardzo łatwo jest stworzyć programy o ograniczonym dostępie

208

ROZDZIAŁ

SIÓDMY

do zasobów zewnętrznych. Na przykład w samym języku Lua możemy tworzyć zamknięte środowiska — tzw. piaskownice. Wystarczy usunąć ze środowiska wszystko to, co uznamy za niebezpieczne (na przykład instrukcję fi leopen). Luiz: Lua oferuje również definiowane przez użytkownika haki dla debugera, które można wykorzystać do monitorowania wykonywania programów w Lua. Dzięki tym mechanizmom program może być na przykład przerwany, jeżeli wykonuje się zbyt długo lub zużywa za dużo pamięci. Jakie są ograniczenia języka Lua?

Roberto: Myślę, że główne ograniczenia języka Lua są takie same jak ograniczenia wszystkich innych języków dynam icznych. Po pierwsze, naw et przy najbardziej zaawansowanej technologii JIT (a język Lua posiada jeden z najlepszych debugerów JIT wśród języków dynamicznych) nie da się uzyskać wydajności dobrych języków statycznych. Po drugie, niektóre złożone program y mogą korzystać ze statycznej analizy (głównie ze statycznego definiowania typów). Dlaczego zdecydowali się panowie na wykorzystanie mechanizmu odśmiecania?

Roberto: W języku Lua m echanizm odśmiecania był wykorzystywany od samego początku. Uważam, że w przypadku języka interpretowanego mechanizm odśmiecania może być znacznie bardziej kom paktow y i rozbudow any niż zliczanie referencji, nie mówiąc już o tym, że pozwala na pozbycie się śmieci. Ze względu na to, że język interpretowany zazwyczaj zawiera samoopisujące się dane (wartości ze znacznikami i tym podobne elementy), prosty mechanizm zbierania typu „oznacz i posprzątaj” można zrealizować bardzo prosto i nie ma on wpływu na pozostałą część interpretera. Dla każdego języka bez typów danych zliczanie referencji może być bardzo trudne. Bez statycznego określania typów każde przypisanie może zmieniać liczniki, a zatem wymaga dynamicznego sprawdzania zarówno w starej, jak i nowej wartości zmiennej. Późniejsze doświadczenia ze zliczaniem referencji w języku Lua w ogóle nie poprawiły wydajności. Czy są panowie zadowoleni ze sposobu obsługi liczb w języku Lua?

Roberto: Z mojego doświadczenia wynika, że liczby w kom puterach zawsze były źródłem różnych niespodzianek (podobnie zresztą jak poza komputerami). Uważam wykorzystanie typu double jako jedynego typu liczbowego w języku Lua za rozsądny kompromis. Rozważaliśmy wiele innych opcji, ale większość z nich była zbyt wolna, zbyt złożona lub wymagała zbyt dużo pamięci. Nawet zastosowanie samego typu doubl e nie jest rozsądnym wyborem dla systemów wbudowanych. Dlatego właśnie można skompilować interpreter z alternatywnym typem liczbowym — na przykład long.

LUA

209

Dlaczego wybrali panowie tabele jako jednorodne konstruktory danych w języku Lua?

Roberto: Moją osobistą inspiracją był VDM (język formalnego opisu głównie specyfikacji oprogramowania). Był to projekt, w którego realizację byłem zaangażowany, kiedy rozpoczęliśmy prace nad językiem Lua. W języku VDM występują trzy rodzaje kolekcji: zbiory, sekwencje i mapy. Ale zarówno zbiory, jak i sekwencje można łatwo wyrazić za pomocą map. Z tego powodu miałem na myśli ideę map jako jednolitego konstruktora. Luiz również miał swoje powody. Luiz: Tak. Bardzo podobał mi się język AWK. Zwłaszcza jego obsługa tablic asocjacyjnych. Co zyskują programiści, którzy wykorzystują funkcje pierwszej klasy stosowane w języku Lua?

Roberto: Co prawda pod różnymi nazwami — od podprogramów do metod — funkcje spinają wszystkie języki program owania od ponad 50 lat. W związku z tym dobra obsługa funkcji jest zaletą każdego języka. Obsługa funkcji w języku Lua pozwala programistom stosować kilka dobrych technik ze świata programowania funkcyjnego — na przykład reprezentowania danych jako funkcji. I tak figurę można zaprezentować za pomocą funkcji, która na podstawie współrzędnych x i y zwróci informację o tym, czy wybrany punkt należy do tej figury. Dzięki takiej reprezentacji wykonanie operacji, jak wyznaczanie unii zbiorów czy też części wspólnych, staje się trywialne. Język Lua wykorzystuje funkcje także w sposób niekonwencjonalny. Fakt, że są to funkcje pierwszej klasy (ang. first-class function), upraszcza takie zastosowania. Na przykład każdy fragment kodu wprowadzony do interpretera jest kompilowany jako treść funkcji. Dzięki temu każda konwencjonalna definicja funkcji w języku Lua zawsze jest zagnieżdżona wewnątrz funkcji. Oznacza to, że nawet proste programy w języku Lua wymagają funkcji pierwszej klasy. Po co zaimplementowali panowie domknięcia?

Roberto: Domknięcia (ang. closure) to konstrukcje, które od początku chcieliśmy mieć w języku Lua: są proste, uniwersalne i dają duże możliwości. Od pierwszej wersji funkcje w języku Lua były wartościami pierwszej klasy. Okazało się, że jest to bardzo przydatne nawet dla standardowych programistów bez wcześniejszych doświadczeń w programowaniu funkcyjnym. Bez domknięć możliwości używania funkcji pierwszej klasy są w pewnym sensie ograniczone. Tak na marginesie: pojęcie „dom knięcie” dotyczy techniki implementacji, a nie samej własności. Sama własność oznacza funkcję pierwszej klasy z zasięgiem leksykalnym, ale oczywiście term in „dom knięcie” jest krótszy. ©

210

ROZDZIAŁ

SIÓDMY

W ja k i sposób planują panowie obsługiwać współbieżność?

Roberto: Nie jesteśmy zwolennikami wielowątkowości, czyli współdzielonej pamięci z wywłaszczaniem. W referacie przygotowanym na konferencję poświęconą historii języków programowania (HOPL)1 napisaliśmy: „W dalszym ciągu uważamy, że nikt nie jest w stanie pisać poprawnych programów w języku, w którym wyrażenie a - a+1 nie jest deterministyczne” . Problemów tych m ożna uniknąć, jeśli zrezygnuje się albo z wywłaszczania, albo ze współdzielonej pamięci, a język Lua oferuje oba te podejścia. W przypadku podprogram ów m am y w spółdzieloną pamięć bez wywłaszczania, ale m echanizm ten nie nadaje się do zastosow ania na m aszynach z procesorami wielordzeniowymi. Mechanizm wielu procesów potrafi jednak eksplorować takie maszyny dość skutecznie. Przez „proces” rozumiem wątek języka C z własnym stanem języka Lua. Tak więc na poziomie języka Lua nie występuje współdzielenie pamięci. W drugim wydaniu książki Programming in Lua [Lua.org] zaprezentowałem prototyp takiej implementacji. O statnio spotkałem się z bibliotekami obsługującymi takie podejście (na przykład Lua Lanes i luaproc). W języku Lua nie ma obsługi współbieżności, ale jest implementacja interesującego rozwiązania wielozadaniowości — konkretnie podprogramów asymetrycznych. W ja k i sposób one działają?

Roberto: Miałem pewne doświadczenia z językiem M odula 2 (moja żona napisała kompletny interpreter Mkodu w ramach swojej pracy magisterskiej). Zawsze podobał mi się pom ysł używania podprogram ów jako podstaw y dla współbieżności kooperatyw nej (ang. co-operative concurrency) oraz innych struktur sterujących. Jednak podprogramy symetryczne w postaci znanej z języka Modula 2 nie sprawdziłyby się w języku Lua. Luiz: W naszym referacie na konferencję HOPL szczegółowo opisaliśmy te decyzje projektowe. Roberto: W końcu stworzyliśmy taki asymetryczny model. Idea tego mechanizmu jest napraw dę prosta. Tworzymy podprogram z jawnym w yw ołaniem funkcji

coroutine.create i przekazujemy do niej funkcję, która ma być uruchomiona jako treść prodprogram u. Po w znow ieniu podprogram u zaczyna działać kod w jego treści i wykonuje się tak długo, aż podprogram się zakończy lub nastąpi jawne wywołanie funkcji yield zwracające wartość. N astępnie m ożna wznowić podprogram . Jego wykonywanie rozpocznie się od miejsca, w którym podprogram się zatrzymał. Ogólna idea jest bardzo podobna do generatorów Pythona, ale z pewną kluczową różnicą: podprogram y Lua m ają możliwość zwracania wartości wew nątrz zagnieżdżonych wywołań, podczas gdy w Pythonie generator może zwracać wartość 1 Ierusalimschy R., de Figueiredo L.H. i Celes W., The evolution of Lua, materiały z konferencji ACM HOPL III, 2007.

LUA

211

tylko z głównej funkcji. Jeśli chodzi o im plementację, to oznacza, że podprogram może mieć niezależny stos, podobnie jak wątek. Zaskakujące jest to, o ile większe możliwości oferują podprogram y z obsługą stosu w porów naniu z płaskimi generatoram i. Na przykład na ich bazie m ożna zaim plem entować jednorazowe kontynuacje.

Doświadczenie W ja k i sposób definiują panowie sukces w swojej pracy?

Luiz: Sukces języka program ow ania zależy od liczby program istów używających języka oraz od sukcesu aplikacji, które wykorzystują język. Nie wiemy dokładnie, ile osób programuje w języku Lua, ale z całą pewnością jest wiele aplikacji pomyślnie używających języka Lua, włącznie z kilkoma bardzo udanymi grami. Zakres aplikacji, w jakich jest używany język Lua — począwszy od przetw arzania obrazów w kom puterach typu desktop, a skończywszy na systemach w budow anych do sterowania robotów — pokazuje, że istnieje zapotrzebowanie na język tego typu. Na koniec spośród języków stworzonych w krajach rozwijających się Lua jest jedynym, który osiągnął globalne znaczenie na taką skalę. Jest to jedyny język w tej grupie, o którym mówiono na konferencji ACM HOPL. Roberto: To trudne. Pracuję w kilku dziedzinach i w każdej z nich w inny sposób postrzegam sukces. Ogólnie rzecz biorąc, powiedziałbym, że wspólną częścią definicji sukcesu w różnych dziedzinach jest bycie znanym. Zawsze olbrzymią przyjemność sprawia sytuacja, w której ktoś przedstawia cię innej osobie, a ty jesteś rozpoznawany. Czy żałują panowie czegoś w związku z językiem?

Luiz: Właściwie nie żałuję niczego. Z perspektywy można powiedzieć, że gdybyśmy wiedzieli tyle, ile wiemy teraz, to pewne operacje mogliśmy przeprowadzić wcześniej. Roberto: Nie potrafię wskazać jakiejś jednej konkretnej rzeczy, której żałuję, ale projekt języka obejmuje kilka w ażnych decyzji. Dla m nie najtrudniejsze decyzje dotyczą łatwości użytkowania. Jednym z celów języka Lua było zapewnienie łatwości użytkowania go przez programistów niebędących profesjonalistami. Nie należę do tej kategorii użytkowników. W związku z tym niektóre decyzje dotyczące języka nie są idealne z mojej perspektywy jako użytkownika. Typowym przykładem jest składnia języka Lua: w wielu zastosow aniach sprawdza się opisowa składnia języka Lua, ale osobiście wolałbym, gdybyśmy zastosowali bardziej kompaktową notację. Czy popełnili panowie jakieś błędy w projekcie lub implementacji?

Luiz: Nie uważam, abyśmy popełnili jakieś istotne błędy w projekcie lub implementacji języka Lua. Po prostu uczyliśmy się, w jaki sposób rozwijać język, a to znacznie więcej od zdefiniowania składni, semantyki i opracowania implementacji. Są również istotne

212

ROZDZIAŁ

SIÓDMY

względy społeczne, na przykład stworzenie i wsparcie dla społeczności, opracowanie podręczników, książek, list mailingowych, kanałów chat itp. Z pewnością poznaliśmy wartość wspierania społeczności, a także ciężkiej pracy, jaką trzeba w to włożyć. Wiemy również, ile wysiłku wymaga projektowanie i kodowanie. Roberto: Na szczęście nie popełniliśmy wielkich błędów. Co najwyżej po drodze popełniliśm y wiele drobnych pomyłek. Mieliśmy jednak szansę ich popraw ienia wtedy, gdy język Lua się rozwijał. Oczywiście to denerwowało niektórych użytkowników ze względu na niezgodności pomiędzy wersjami, ale teraz język Lua jest stabilny. Co według panów należy robić, aby zostać lepszym programistą?

Luiz: Nigdy nie należy bać się zaczynania projektu od początku, co oczywiście łatwiej powiedzieć, niż zrobić. Nigdy nie należy zaniedbywać potrzeby zajm ow ania się szczegółami. Nie należy tworzyć zestawu własności, które w naszej opinii będą potrzebne za jakiś czas: dodanie ich zawczasu może uniemożliwić dodanie znacznie lepszych własności później, kiedy będą bardziej potrzebne. Na koniec — zawsze należy dążyć do prostszych rozwiązań. Jak powiedział Einstein: „Najprościej, jak się da, ale nie prościej” . Roberto: Trzeba się uczyć now ych języków program ow ania, ale tylko z dobrych książek. Haskell jest językiem, który pow inni znać wszyscy programiści. Należy studiować informatykę: nowe algorytmy, nowe formalizmy (rachunek lambda, jeśli ktoś go jeszcze nie zna, rachunek pi, CSP itp.). Zawsze należy dążyć do poprawiania swojego kodu. Jakie zagadnienie w informatyce jest najtrudniejsze i jak należy go nauczać? Roberto: Myślę, że nie ma czegoś takiego jak informatyka jako dziedzina nauki o ściśle określonym zakresie wiedzy. Nie chcę przez to powiedzieć, że informatyka nie jest nauką, ale wciąż trudno określić, co jest informatyką, a co nią nie jest (tak jak i to, co jest ważne, a co nie jest ważne). Wiele osób zajmujących się informatyką nie ma formalnego wykształcenia informatycznego. Luiz: Uważam siebie za matematyka zainteresowanego rolą komputerów w matematyce, ale oczywiście bardzo lubię komputery. © Roberto: Nawet wśród osób mających formalne wykształcenie informatyczne nie ma jednomyślności. Brakuje wspólnych podstaw. Wiele osób sądzi, że to Java dała początek monitorom, maszynom wirtualnym, interfejsom (w odróżnieniu od klas) itp. Czy programy nauczania informatyki można zaliczyć do trochę bardziej zaawansowanych kursów zawodowych?

Roberto: Tak. A wielu programistów nawet nie skończyło studiów informatycznych. Luiz: Ja tak nie uważam, ale nie jestem zatrudniony jako program ista. Z drugiej strony sądzę, że byłoby błędem wymaganie od program istów posiadania tytułów

LUA

2 13

naukowych z informatyki, certyfikatów lub tym podobnych dokumentów. Stopień naukowy z informatyki nie daje gwarancji, że jego posiadacz będzie potrafił dobrze programować, a wielu dobrych programistów nie posiada stopni naukowych (może to było prawdą, kiedy zaczynałem karierę, być może dziś jestem już za stary). Uważam, że stopień naukowy z informatyki nie daje gwarancji, że ktoś będzie potrafił dobrze programować. Roberto: Błędem jest wymaganie od większości profesjonalistów posiadania tytułów. Miałem jednak na myśli to, że kultura w tym obszarze jest zbyt słabo rozwinięta. Istnieje bardzo mało tematów, które można wskazać jako obowiązkowe zagadnienia, które powinny być znane. Oczywiście pracodawca może wymagać, czego tylko chce. Nie powinno być jednak wymagań dotyczących tytułów naukowych. Jaka jest rola matematyki w informatyce, a w szczególności w programowaniu?

Luiz: Cóż, ja jestem matematykiem. Widzę matematykę wszędzie. Programowanie spodobało mi się prawdopodobnie ze względu na to, że posiada cechy matematyczne: dokładność, abstrakcję, elegancję. Program jest dow odem skom plikow anego twierdzenia, które m ożna stopniowo udoskonalać i poprawiać. Program wykonuje też określone operacje. Oczywiście w ogóle nie myślę w tych kategoriach, kiedy programuję, ale sądzę, że nauka matematyki jest — ogólnie rzecz biorąc — bardzo ważna dla opanowania umiejętności programowania. Matematyka pomaga nauczyć się odpowiedniego sposobu myślenia. Znacznie łatwiej się programuje, jeśli jest się przyzwyczajonym do myślenia o rzeczach abstrakcyjnych, które rządzą się własnymi regułami. Roberto: Christos H. Papadimitriou powiedział: „Informatyka jest nową matematyką” . Bez matematyki programista nie może zbyt wiele osiągnąć. W szerszym ujęciu zarówno matematyka, jak i programowanie współdzielą tę samą kluczową dziedzinę mentalną: abstrakcję. Współdzielą również kluczowe narzędzie: logikę formalną. Dobry programista wykorzystuje matematykę przez cały czas: wyznaczając niezmienniki w kodzie, modele interfejsów itp. Wiele języków programowania stworzyli matematycy. Być może dlatego programowanie jest tak trudne!

Roberto: Pozostawię tę kwestię naszemu matematykowi. © Luiz: Powiedziałem wcześniej, że programowanie ewidentnie ma cechy matematyczne: dokładność, abstrakcja, elegancja. W projektow aniu języka program ow ania dostrzegam podobieństw o do tw orzenia matematycznej teorii: dostarczam y dobrych narzędzi, które pozwalają innym wykonać dobrą pracę. Zawsze pociągały mnie języki programowania, które są niewielkie, ale dają duże możliwości. Ich siła polega na istnieniu m ocnych prym ityw ów i konstrukcji. Podobnym pięknem jest dysponowanie dobrymi definicjami i podstawowymi twierdzeniami.

214

ROZDZIAŁ

SIÓDMY

W ja k i sposób rozpoznaje pan dobrego programistę?

Luiz: To się po prostu wie. Dziś o wiele łatwiej jest rozpoznać złych programistów — nie dlatego, że ich programy są złe (choć coraz częściej są one skomplikowanym i niestabilnym zlepkiem kodu), ale dlatego, że można wyczuć, że osoby te nie czują się dobrze w program ow aniu — tak jakby ich w łasne program y były dla nich ciężarem i niewiadomą. W ja k i sposób powinno się nauczać debugowania?

Luiz: Nie sądzę, aby debugow ania m ożna było nauczać — przynajmniej tru dno to robić formalnie. Można jednak się go nauczyć poprzez sesje debugowania z inną osobą — najlepiej bardziej doświadczoną. Od takich osób m ożna się nauczyć strategii debugowania: w jaki sposób zawęzić problem, jak przewidywać sytuacje i oceniać wyniki, co jest bezużyteczne i tylko zaciemnia obraz itp. Roberto: Debugowanie to w zasadzie sedno rozwiązywania problem ów . Jest to działanie, w którym czasami trzeba wykorzystać wszystkie narzędzia intelektualne, jakie kiedykolwiek poznaliśmy. Oczywiście istnieją pewne przydatne triki (na przykład unikanie korzystania z debugera, jeśli to możliwe, używanie testerów pamięci podczas programowania w językach niskopoziomowych, takich jak C). Te sztuczki to jednak tylko niewielki fragm ent debugowania. Debugowania pow inniśm y się uczyć tak samo, jak uczymy się programowania. W ja k i sposób panowie testują i debugują swój kod?

Luiz: Ja staram się konstruować go i testować kawałek po kawałku. Rzadko używam debugera. Jeśli już, robię to dla kodu w C, nigdy dla kodu w języku Lua. W przypadku języka Lua zazwyczaj wystarczają instrukcje print umieszczone w odpow iednich miejscach. Roberto: Ja stosuję podobne podejście. Jeśli używam debugera, to tylko po to, aby określić miejsce, w którym następuje awaria programu. Dla kodu w języku C niezbędne są takie narzędzia, jak Valgrind lub Purify. Jaka jest rola komentarzy w kodzie źródłowym?

Roberto: Bardzo niewielka. Uważam, że jeśli kod wymaga komentarzy, to zazwyczaj nie jest dobrze napisany. Komentarz traktuję prawie tak jak notatkę o treści: „Pow inienem później napisać to od początku” . Uważam, że przejrzysty kod jest znacznie bardziej czytelny od kodu z komentarzami. Luiz: Zgadzam się. Stosuję kom entarze, które m ówią coś, co nie w ynika z kodu w oczywisty sposób.

LUA

215

W ja k i sposób należy dokumentować projekt?

Roberto: Metodą siłową. Żadne narzędzia nie są w stanie zastąpić dobrze napisanej i dobrze przemyślanej dokumentacji. Luiz: Tworzenie dobrej dokumentacji na temat ewolucji projektu jest jednak możliwe tylko wtedy, gdy mamy ją w głowie od samego początku. Nie dotyczy to języka Lua. Nigdy nie przewidywaliśmy, że język Lua tak bardzo się rozwinie i będzie tak powszechnie używany, jak jest dziś. Kiedy pisaliśmy referat na konferencję HOPL (co zajęło nam prawie dwa lata!), trudno nam było sobie przypomnieć sposób, w jaki podejmowaliśmy pewne decyzje projektowe. Z drugiej strony, gdybyśmy na początku projektu prowadzili formalne, rejestrowane spotkania, prawdopodobnie stracilibyśmy spontaniczność i trochę przyjemności z pracy nad projektem. Jakie czynniki mierzą panowie podczas ewolucji kodu?

Luiz: W edług m nie pow inna to być prostota implementacji. Obok niej szybkość działania oraz poprawność implementacji. Ważnym czynnikiem jest także elastyczność — dzięki niej można zmodyfikować implementację, jeśli zachodzi taka potrzeba. W ja k i sposób dostępne zasoby sprzętowe wpływają na sposób myślenia programistów?

Luiz: Jestem już stary. © Uczyłem się programowania na komputerze IBM 370. Wtedy wiele godzin zajmowało dziurkowanie kart, zanoszenie ich do kolejki oraz odbieranie wyników. Miałem do czynienia z wieloma wolnymi maszynami. Myślę, że programiści powinni próbować je wykorzystywać, ponieważ nie wszyscy na świecie dysponują najszybszymi komputerami. Osoby programujące aplikacje dla masowych odbiorców powinny testować je na wolnych komputerach. Dzięki temu można dokładniej poznać odczucia użytkowników. Oczywiście do tworzenia kodu mogą używać najlepszych dostępnych kom puterów : nie m a niczego zabawnego w konieczności długiego oczekiwania na zakończenie kompilacji. We współczesnym świecie globalnego internetu projektanci aplikacji internetowych powinni korzystać z wolnych połączeń, a nie tylko z hiperszybkich połączeń używanych w pracy. Celowanie w przeciętną platformę sprawi, że nasze produkty będą szybsze, prostsze i lepsze. W przypadku języka Lua sprzętem jest kompilator języka C. Podczas implementowania języka Lua nauczyliśmy się, że dbanie o łatwość przenośności kodu się opłaca. Prawie od samego początku im plem entowania języka Lua pamiętaliśmy o bardzo ścisłych ograniczeniach standardu ANSI/ISO C (C89). To umożliwiło działanie języka Lua na specjalistycznym sprzęcie, takim jak roboty, oprogramowanie firmware drukarek, ruterów sieciowych itp. Żadna z tych platform nie była dla nas celem. Roberto: Zgodnie z jedną ze złotych zasad, zawsze należy traktować zasoby sprzętowe jako ograniczone. Oczywiście zasoby sprzętowe zawsze są ograniczone. „Natura nie znosi próżni” — każdy program zwykle rozrasta się tak długo, aż w końcu zużyje

216

ROZDZIAŁ

SIÓDMY

wszystkie dostępne zasoby. Co więcej, wraz ze spadkiem cen zasobów dla platform o ustabilizowanej pozycji pojawiają się nowe platformy o poważnych ograniczeniach. Stało się tak w przypadku m ikrokom puterów, następnie telefonów komórkowych. Dzieje się to przez cały czas. Jeśli ktoś chce osiągnąć pierwszeństwo na rynku, powinien bardzo uważnie ocenić zasoby, jakich potrzebują programy. Jakie wnioski z lekcji na tem at powstania, rozwoju i przystosowania się języka Lua do warunków współczesnych mogą wyciągnąć programiści, którzy tworzą systemy komputerowe dziś oraz będą je tworzyć w najbliższej przyszłości?

Luiz: Powinno się pamiętać o tym, że nie wszystkie aplikacje będą działały na mocnych kom puterach typu desktop lub laptopach. Wiele aplikacji działa na urządzeniach o ograniczonych zasobach, na przykład telefonach kom órkow ych lub naw et mniejszych urządzeniach. Osoby, które projektują i im plem entują narzędzia do programowania, powinny zwracać szczególną uwagę na ten problem, ponieważ nigdy nie m ożna mieć pewności co do tego, gdzie i w jaki sposób nasze narzędzie zostanie wykorzystane. Należy więc projektować aplikacje z myślą o wykorzystaniu jak najmniejszej ilości zasobów. Dzięki stosowaniu tej zasady można być przyjemnie zaskoczonym, że nasze narzędzie jest używane w wielu kontekstach, o których wcześniej nie myśleliśmy, a naw et w takich, o których istnieniu nie wiedzieliśmy. To właśnie stało się w przypadku języka Lua! Nie bez powodu lubimy pewien żart, który w rzeczywistości wcale nie jest żartem: kiedy dyskutujemy o włączeniu nowej własności do języka Lua, zadajemy sobie pytanie: „Dobrze, a czy to będzie działać w kuchence mikrofalowej?” .

Projekt języka Język Lua jest łatw y do osadzania i potrzebuje bardzo mało zasobów. W ja k i sposób projektuje się języ k i dla środowisk o ograniczonych zasobach sprzętowych, pamięciowych i programowych?

Roberto: Kiedy zaczynaliśmy pracę nad językiem Lua, nie precyzowaliśmy takich celów zbyt wyraźnie. Po prostu musieliśmy je spełnić, by zrealizować nasz projekt. W miarę postępów w projekcie jego cele staw ały się dla nas bardziej czytelne. Uważam, że teraz najważniejszym celem jest zapewnienie ekonomii we wszystkich aspektach i przez cały czas. Kiedy na przykład ktoś sugeruje uwzględnienie jakiejś nowej własności, pierwsze pytanie brzmi: „Ile to będzie kosztowało?” . Czy zdarzało się panom odrzucać nowe funkcje dlatego, że były zbyt kosztowne?

Roberto: Niemal wszystkie własności są zbyt kosztowne w porów naniu z tym, co wnoszą do języka. Dla przykładu nawet prosta instrukcja continue nie spełniła naszych kryteriów.

LUA

2 17

Jakie korzyści powinna przynosić nowa własność, aby była warta ponoszonych kosztów?

Roberto: Nie m a tu ustalonych reguł, ale dobrą regułą są własności, które nas zaskakują, czyli takie, które przydają się do wykonywania działań innych, niż pierwotnie planowaliśmy. To przywodzi mi na myśl inną złotą regułę opartą na ustaleniu, jak wielu użytkowników skorzysta z nowej własności. Niektóre własności okazują się przydatne tylko niewielkiej grupie użytkowników, natom iast inne przydają się prawie wszystkim. Czy potrafią panowie podać jakiś przykład dodanej własności, która okazała się przydatna dużej grupie użytkowników?

Roberto: Pętla for. Broniliśmy się nawet przed nią, ale kiedy się pojawiła, zmieniły się wszystkie przykłady w książce! Słabe tabele są również zaskakująco przydatne. Niewiele osób z nich korzysta, ale powinny to robić. Czekali panowie siedem la t po opublikowaniu wersji 1.0, zanim dodali pętlę for. Co panów wstrzymywało? Co ostatecznie skłoniło do je j uwzględnienia?

Roberto: W strzymywało nas to, że nie mogliśmy znaleźć form atu dla pętli for, który byłby jednocześnie ogólny i prosty. W łączyliśmy now ą instrukcję, kiedy znaleźliśmy dobry format — wykorzystujący funkcje-generatory. Domknięcia stały się kluczowym czynnikiem, dzięki którem u generatory okazały się wystarczająco ogólne, by je stosować. Dzięki dom knięciom sama funkcja generatora może przechowywać wewnętrzny stan podczas działania pętli. Czy to nowy obszar kosztów: aktualizowanie kodu w celu skorzystania z nowych własności oraz nowo odkrytych dobrych praktyk?

Roberto: Nikt nikogo nie zmusza do korzystania z nowych własności. Czy użytkownicy wybierają jedną wersję języka Lua i pozostają przy niej przez cały czas życia projektu. Czy nigdy je j nie aktualizują?

Roberto: Myślę, że wielu producentów gier postępuje właśnie w taki sposób, natomiast w innych dziedzinach — sądzę — zdarzała się zmiana wykorzystywanej wersji języka Lua. Dla przykładu w grze World ofWarcraft wprowadzono zmianę — z wersji języka Lua 5.0 do wersji Lua 5.1! Trzeba jednak pamiętać, że język Lua jest dziś znacznie bardziej stabilny, niż był kiedyś. W ja k i sposób dzielą się panowie obowiązkami podczas realizacji projektu —

w szczególności jeśli chodzi o pisanie kodu?

Luiz: Pierwsze wersje języka Lua zakodow ał W aldem ar w 1993 roku. Od około 1995 roku Roberto pisze i utrzymuje większą część kodu. Ja jestem odpowiedzialny za niewielki fragment kodu: m oduły zrzucania i ładow ania kodu bajtowego oraz

218

ROZDZIAŁ

SIÓDMY

niezależny kom pilator luac. Zawsze przeglądaliśmy kod i wysyłaliśmy e-mailem do innych uczestników projektu sugestie dotyczące proponowanych zmian w kodzie. Organizowaliśmy także długie spotkania na tem at now ych własności i ich implementacji. Czy otrzymywali panowie dużo komentarzy na temat języka lub jego implementacji od użytkowników? Czy stosują panowie jakiś formalny mechanizm uwzględniania uwag użytkowników w języku oraz jego poprawkach?

Roberto: Żartujemy sobie, że jeśli czegoś nie pamiętaliśmy, to nie było to takie ważne. Lista dyskusyjna dotycząca języka Lua jest dość aktywna, niektórzy jednak stawiają znak równości pomiędzy otwartym oprogramowaniem a projektem społecznościowym. Kiedyś wysłałem na listę dyskusyjną języka Lua wiadomość, która podsum ow uje nasze stanowisko: „Język Lua jest otw artym oprogram ow aniem , ale nigdy nie był tw orzony w sposób otwarty. Nie oznacza to, że nie słucham y innych ludzi. Czytamy praktycznie każdą w iadom ość na liście mailingowej. Kilka istotnych własności w języku Lua było zainspirowanych lub powstało na bazie zewnętrznych pomysłów (na przykład metatabele, podprogramy czy też implementacja domknięć to tylko kilka większych). Ostatecznie jednak to my decydujemy. Nie robimy tego dlatego, że uznajemy nasze opinie za lepsze od opinii innych. Postępujemy tak tylko dlatego, że chcemy, aby język Lua był taki, jakim chcemy go widzieć, a nie żeby był najpopularniejszym językiem na świecie. Ze względu na ten styl rozwoju oprogram ow ania wolimy, aby nie było publicznego repozytorium dla języka Lua. Nie chcemy być zmuszani do wyjaśniania wszystkich zmian, które wprowadzamy w kodzie. Nie chcemy być zmuszani do ciągłego aktualizowania dokumentacji. Chcemy mieć swobodę rozwijania dziwnych pomysłów oraz rezygnowania z nich bez konieczności wyjaśniania każdego posunięcia” . Dlaczego chętnie wykorzystują panowie sugestie i pomysły, ale nie kod? Jedno, co przychodzi mi do głowy, to fakt, że być może samodzielne pisanie kodu pozwala nauczyć się czegoś więcej na temat problemów oraz ich rozwiązań.

Roberto: Coś w tym jest. Chcemy w pełni kontrolować to, co się dzieje z językiem Lua, dlatego fragment kodu nie jest wielkim udziałem w projekcie. Fragment kodu nie wyjaśnia nam, dlaczego jest taki, jaki jest. Kiedy jednak zrozumiemy ideę, pisanie kodu staje się przyjemnością, której nie chcemy tracić. Luiz: Myślę również, że obawiamy się dołączać kod z zewnątrz, ponieważ nie mamy pewności, do kogo należą prawa własności do tego kodu. Nie chcemy być pozwani do sądu za to, że wykorzystaliśmy kod, do którego nie mieliśmy prawa.

LUA

219

Czy język Lua osiągnie punkt, w którym dodadząjuż panowie wszystkie własności, które chcieli panowie dodać, a jedynymi wprowadzanymi zmianami będą ulepszenia implementacji (na przykład LuaJIT)?

Roberto: Sądzę, że teraz jesteśmy w takim punkcie. Dodaliśmy jeśli nie wszystkie, to większość własności, które chcieliśmy dodać. W ja k i sposób realizują panowie testy dymne (ang. smoke testingj oraz testy regresji? Zauważyłem, że jed n ą z większych korzyści z otwartego repozytorium je s t to, że użytkownicy mogą wykonywać zautomatyzowane testy dla prawie każdej poprawki.

Luiz: Wydania języka Lua nie są tak częste, zatem kiedy pojawia się nowe wydanie, jest one poddawane intensywnym testom. Wydajemy wersje robocze (poprzedzające wersję alfa) tylko wtedy, gdy są już dość stabilne, tak by użytkownicy mogli zobaczyć, jakie nowe własności zostały wprowadzone. Roberto: Przeprowadzamy rozbudow ane testy regresji. Rzecz w tym, że piszemy kod w ANSI C, zazwyczaj mamy więc bardzo niewiele problemów z przenośnością. Nie musimy testować zm ian na kilku różnych maszynach. W ykonuję wszystkie testy regresji zawsze, kiedy zmienię coś w kodzie. Testy te są jednak w całości zautomatyzowane. Muszę jedynie wpisać test al 1. Kiedy w kodzie znajdują panowie powtarzający się problem, to w ja k i sposób panowie rozpoznają, co jest najlepszym rozwiązaniem: lokalne obejście czy globalna poprawka?

Luiz: Zawsze staramy się wprowadzać poprawki natychmiast po znalezieniu błędów. Ponieważ jednak niezbyt często wydajemy nowe wersje języka Lua, czasami czekamy, aż zbierze się wystarczająco dużo poprawek, aby było uzasadnione wydanie wersji pomocniczej. Ulepszenia, które nie są poprawkami błędów, pozostawiamy do wersji głównych. Jeśli problemy są złożone (co zdarza się nieczęsto), wprowadzamy lokalne obejście w wersji pomocniczej oraz globalną poprawkę w następnej wersji głównej. Roberto: Zazwyczaj lokalne obejście zawodzi bardzo szybko. Na takie rozwiązanie można się zdecydować tylko wtedy, kiedy jest naprawdę niemożliwe wprowadzenie globalnej popraw ki — na przykład jeśli globalna popraw ka w ymaga stworzenia nowego, niezgodnego z poprzednią wersją interfejsu. Czy teraz, w kilka lat od rozpoczęcia projektu, w dalszym ciągu będą panowie tworzyć projekt z przeznaczeniem do działania w ograniczonych zasobach sprzętowych?

Roberto: Oczywiście, zawsze jesteśmy na tym skoncentrowani. Dla zaoszczędzenia kilku bajtów analizujemy nawet kolejność pól w strukturach języka C. © Luiz: A język Lua wykorzystuje dziś w m ałych urządzeniach więcej osób niż kiedykolwiek wcześniej.

220

ROZDZIAŁ

SIÓDMY

W ja k i sposób dążenie do prostoty wpływa na projekt języka z perspektywy użytkownika? Myślę o wsparciu dla klas w języku Lua. W pewien sposób przypomina mi to programowanie obiektowe w języku C (choć jest znacznie mniej denerwujące).

Roberto: Obecnie stosujemy zasadę „m echanizmy zamiast strategii” . Dzięki temu możemy zapewnić prostotę języka, ale tak jak pan powiedział, użytkow nik musi określić w łasną strategię. Tak właśnie jest w przypadku klas. Jest wiele sposobów ich implementowania. Niektórzy użytkownicy je uwielbiają, inni nienawidzą. Luiz: Dzięki temu Lua staje się w pewnym sensie językiem dla majsterkowiczów. W języku Tcl przyjęto podobne podejście, ale doprowadziło to do fragmentacji, ponieważ w każdej bibliotece stosowano inne podejście. Czy fragmentacja jes t mniejszym problemem ze względu na zamierzone przeznaczenie języka Lua?

Roberto: Tak. Czasami jest to problem, ale w wielu zastosowaniach (na przykład w grach) nie ma z tym problemu. Lua jest w większości wykorzystywana w systemach w budow anych, w pewnych aplikacjach. Aplikacja zapewnia bardziej solidny framework dla unifikacji stylów programowania. Istnieje Lua/Lightroom, Lua/WoW, Lua/Wireshark — każdy m a własną wewnętrzną kulturę. Czy w związku z tym uważają panowie styl elastyczności przyjęty w języku Lua i wyrażający się hasłem „M y dostarczamy mechanizmów" za dużą zaletę?

Roberto: Niekoniecznie. Tak jak w przypadku innych obszarów, to jest pewien kom prom is. Czasami jest bardzo wygodnie, jeśli istnieją gotowe strategie do wykorzystania. Hasło „My dostarczam y m echanizm ów ” jest dość elastyczne, ale wymaga więcej pracy oraz fragmentacji stylów. Jest również dość ekonomiczne. Luiz: Z drugiej strony czasami trudn o wyjaśnić to użytkow nikom . Myślę tu o wyjaśnieniu im, jakie są mechanizmy i na czym polega ich zastosowanie. Czy to przeszkadza współdzieleniu kodu pomiędzy projektami?

Roberto: Często tak jest. Przeszkadza również rozwojowi niezależnych bibliotek. Na przykład aplikacja WoW korzysta z bardzo wielu bibliotek (jest nawet implementacja problemu komiwojażera z wykorzystaniem programowania genetycznego). Biblioteki te nie są jednak używane nigdzie poza WoW. Czy nie obawiają się panowie zatem , że z tego powodu język Lua rozszczepi się na odmiany Lua/WoW, Lua/Wireshark itd.?

Luiz: Nie boimy się tego: język pozostanie taki sam. Różnią się dostępne funkcje. Sądzę, że te aplikacje w pewien sposób na tym zyskują.

LUA

221

Czy poważni użytkownicy języka Lua piszą swoje własne dialekty na bazie języka Lua?

Roberto: Być może. Co prawda język Lua nie obsługuje makr i myślę, że gdyby były makra, można by było stworzyć prawdziwy nowy dialekt. Luiz: Nie byłby to dialekt języka w ścisłym tego słowa znaczeniu, ale dialekt w rozumieniu języka specyficznego dla danej dziedziny. Taki był właśnie cel powstania języka Lua. Kiedy używa się go do obsługi plików danych, może wyglądać jak dialekt, ale oczywiście są to tylko tabele języka Lua. Istnieją pewne projekty, w których w pewnym stopniu wykorzystuje się makra. Przypominam sobie na przykład projekt metalua. Problem dialektów dotyczy bardziej języka Lisp. Dlaczego zdecydowali się panowie na udostępnienie rozszerzalnej semantyki?

Roberto: Rozpoczęło się od pom ysłu udostępnienia właściwości obiektowych. Nie chcieliśmy dodawać mechanizmów obiektowych w języku Lua, ale użytkownicy ich chcieli, dlatego wpadliśmy na pomysł stworzenia mechanizmów wystarczających do tego, by użytkownicy sami mogli zaimplementować swoją obsługę obiektów. W dalszym ciągu uważamy, że była to dobra decyzja. Chociaż w takiej konfiguracji programowanie obiektowe jest trudniejsze dla początkujących, język staje się znacznie bardziej elastyczny. W szczególności kiedy używamy języka Lua razem z innymi językami (co jest charakterystyczne dla języka Lua), elastyczność ta pozwala programistom dopasować model obiektowy z modelem obiektowym zewnętrznego języka. W ja k i sposób bieżące środowisko sprzętu, oprogramowania, usług i sieci różni się od środowiska, w którym system Lua był projektowany pierwotnie? W ja k i sposób zmiany te wpłynęły na system oraz jakich adaptacji wymagają?

Roberto: Ponieważ celem języka Lua było zapewnienie wysokiego stopnia przenośności, powiedziałbym, że współczesne środowiska nie różnią się zbytnio od wcześniejszych. Kiedy na przykład zaczynaliśmy prace projektowe nad językiem Lua, funkcjonowały 16-bitowe kom putery z systemami DO/W indows, a niektóre starsze kom putery w dalszym ciągu były 8-bitowe. Obecnie nie ma 16-bitowych komputerów desktop, choć niektóre platform y, na których jest wykorzystywany język Lua (systemy wbudowane), w dalszym ciągu są 16-bitowe lub nawet 8-bitowe. Istotna zmiana nastąpiła w języku C. Kiedy w 1993 roku rozpoczynaliśmy projekt Lua, standard ISO (ANSI) C nie miał jeszcze tak utrwalonej pozycji, jaką ma dziś. Na wielu platform ach w dalszym ciągu korzystano ze standardu K&R języka C, a w wielu aplikacjach wykorzystywano złożony mechanizm m akr pozwalających na kompilację w standardach K&R C oraz ANSI C. Najważniejsza różnica polegała na deklaracji nagłówków funkcji. W tam tym okresie wybór standardu ANSI C był śmiałą decyzją.

222

ROZDZIAŁ

SIÓDMY

Luiz: Nie czuliśmy potrzeby implementacji w standardzie C99. Język Lua został zaimplementowany w standardzie C89. Być może będziemy zmuszeni do wykorzystania pewnych fragmentów standardu C99 (zwłaszcza nowych typów specyficznych dla rozmiaru) podczas przechodzenia na maszyny 64-bitowe, ale nie sądzę, aby to było konieczne. Gdyby mogli panowie stworzyć maszynę wirtualną języka Lua od nowa, to czy w dalszym ciągu ściśle przestrzegaliby panowie standardu ANSI C, czy też woleliby zastosować lepszy język niskopoziomowego programowania międzyplatformowego?

Roberto: ANSI C to najbardziej przenośny język, jaki obecnie znam. Luiz: D ostępne są doskonałe kom pilatory ANSI C, ale naw et wykorzystanie ich rozszerzeń nie gwarantuje nam lepszej wydajności. Roberto: Nie jest łatw o ulepszyć ANSI C, a przy tym zachować jego przenośność i wydajność. Tak na marginesie. Zastosowali panowie standard C 89/90. Czy tak?

Roberto: Tak. Standard C99 nie ma jeszcze dobrze ugruntowanej pozycji. Luiz: Nie jestem do tego pewien, czy standard C99 wniesie dużo dodatkow ych własności. W szczególności myślę o instrukcjach goto z wykorzystaniem etykiet, które są dostępne w gcc. Byłaby to alternatywa dla instrukcji switch (dla głównej instrukcji switch maszyny wirtualnej). Roberto: Taka własność poprawiłaby wydajność w wielu maszynach. Luiz: Testowaliśmy ją niedawno i inne osoby również ostatnio ją testowały. Zysk wydajnościowy nie jest spektakularny. Roberto: Po części wynika to z naszej architektury bazującej na rejestrach. Jej cechą charakterystyczną jest mniej opkodu i więcej operacji wykonywanych przez każdą z instrukcji. To zmniejsza koszty związane z działaniem m odułu dyspozytora (ang. dispatcher). Dlaczego zdecydowali się panowie zbudować maszynę wirtualną bazującą na rejestrach?

Roberto: W celu uniknięcia stosowania instrukcji get 1ocal/set 1ocal . Chcieliśmy też sprawdzić ten pomysł. Pomyśleliśmy, że jeśli nawet okaże się, że nie działa on zbyt dobrze, będziemy mogli napisać na ten tem at kilka artykułów. Ostatecznie okazało się, że pomysł był dość dobry i napisaliśmy tylko jeden artykuł. © Czy uruchamianie kodu w środowisku maszyny wirtualnej pomaga przy debugowaniu?

Roberto: To nie pom aga. To zmienia całą koncepcję debugow ania. Każdy, kto kiedykolwiek debugował programy zarówno w kompilowanych, jak i interpretowanych językach (na przykład C i Java), wie, że są one od siebie bardzo daleko. Dobra maszyna

LUA

2 23

w irtualna sprawia, że język staje się bezpieczny w tym sensie, że błędy (na przykład błędy segmentacji) mogą być zawsze interpretowane w kategoriach samego języka, a nie w kategoriach maszyny, na której on działa. Jaki wpływ na debugowanie ma fakt, że język jest niezależny od platformy?

Roberto: Zazwyczaj ułatwia to debugowanie, ponieważ im bardziej niezależny jest język od platformy, tym bardziej solidnego opisu abstrakcji i działania wymaga. Wiadomo, jesteśmy ludźmi i z całą pewnością będziemy popełniali błędy podczas pisania oprogramowania. Czy w związku z tym kiedykolwiek myśleli panowie o tym, ja k ie własności trzeba będzie dodać lub usunąć z języka w celu ułatwienia fazy debugowania?

Roberto: Oczywiście. Pierwszy krok do ułatwienia debugowania to dobre komunikaty o błędach. Luiz: Kom unikaty o błędach w języku Lua popraw iły się w porów naniu z wcześniejszymi wersjami. Przeszliśmy od zatrważającego komunikatu „wywołanie wyrażenia, a nie funkcji” , który występował aż do wersji 3.2 języka Lua do znacznie dokładniejszych kom unikatów o błędach, na przykład „próba wywołania globalnej funkcji ‘F (wartość nil)” . Począwszy od Lua 5.0, używamy symbolicznego uruchamiania kodu bajtowego. Dzięki temu możemy uzyskać użyteczne komunikaty o błędach. Roberto: W samym projekcie języka zawsze staram y się unikać konstrukcji w ymagających złożonych objaśnień. Jeśli coś trudniej zrozumieć, trudniej się to debuguje. Jakie je st powiązanie pomiędzy projektem języka a projektem oprogramowania napisanego w tym języku?

Roberto: Dla m nie głównym kom ponentem projektu języka są przypadki użycia — czyli analiza sposobu używania własności języka przez użytkow ników oraz kombinacja własności języka. Oczywiście programiści zawsze znajdą nowe sposoby używania języka. Dobry język pow inien umożliwiać wykorzystywanie w sposób nieprzewidziany, jednak normalne wykorzystanie języka jest zgodne z tym, co planowali projektanci podczas tworzenia języka. Jak bardzo implementacja języka wpływa na projekt języka?

Roberto: To dwukierunkowa droga. Implementacja ma olbrzymi wpływ na język: nie pow inno się projektow ać tego, czego nie da się w ydajnie zaim plem entow ać. Niektórzy o tym zapom inają, ale wydajność zawsze jest głównym ograniczeniem projektu każdego oprogram ow ania. Projekt może również mieć olbrzymi wpływ na implementację. Na pierwszy rzut oka niektóre aspekty języka Lua wywodzą się z jego implementacji (niewielki rozmiar, dobry interfejs API dla języka C, przenośność), ale kluczową rolę w umożliwieniu takiej implementacji odgrywa projekt języka Lua.

224

ROZDZIAŁ

SIÓDMY

W jednym z napisanych przez panów artykułów przeczytałem: „ W języku Lua wykorzystano napisany samodzielnie skaner oraz samodzielnie stworzony rekurencyjny parser zstępujący". Co panów skłoniło do rozważania pomysłu tworzenia parsera od podstaw? Czy od początku było wiadomo, że będzie on znacznie lepszy od parsera wygenerowanego za pomocą programu yacc?

Roberto: W pierwszej wersji języka Lua korzystaliśmy zarów no z program u lex, jak i yacc. Jednak jednym z pierwszych celów języka Lua było wykorzystanie go w roli języka opisu danych, podobnego do XML. Luiz: Ale to było dużo wcześniej. Roberto: Wkrótce użytkownicy zaczęli używać języka Lua do przetwarzania plików danych o rozmiarach kilku megabajtów. Wtedy okazało się, że skaner wygenerowany za pomocą programu lex szybko stał się wąskim gardłem. Ręczne napisanie dobrego skanera jest stosunkowo łatwe. Ta prosta zmiana poprawiła wydajność języka Lua prawie o 30%. Decyzja przejścia z parsera generowanego za pomocą narzędzia yacc na parser pisany ręcznie została podjęta znacznie później i nie była łatwa. Rozpoczęła się od problemów z kodem szkieletowym używanym w większości implementacji yacc/bison. Kod ten nie był wtedy dostatecznie przenośny (w niektórych m odułach używano pliku nagłówkowego mai 1oc. h, który nie jest zgodny z ANSI C). Nie mieliśmy też dobrej kontroli nad ogólną jakością (na przykład sposobem obsługi przepełnienia stosu lub błędów alokacji pamięci). Kod ten nie był również wielobieżny (na przykład nie pozwalał na wywołanie parsera podczas parsowania). Poza tym parser typu dół-góra nie jest tak dobry jak parser typu góra-dół, w przypadku gdy chcemy generować kod w locie, tak jak w języku Lua. Powodem są trudności w obsłudze dziedziczonych atrybutów. Po wprowadzeniu zmiany zauważyliśmy, że parser, który samodzielnie stworzyliśmy, był nieco szybszy i mniejszy od parsera wygenerowanego przez narzędzie yacc, ale to nie był główny powód zmiany. Luiz: Parser typu góra-dół pozwala również na lepsze komunikaty o błędach. Roberto: Nigdy jednak nie polecałbym samodzielnego pisania parsera dla żadnego języka, który nie ma dojrzałej składni. Wiadomo również, że gramatyka LR(1) (albo LALR lub nawet SRL) ma znacznie większe możliwości od gramatyki LL(1). Nawet w przypadku tak prostej składni, jaką ma język Lua, musieliśmy zastosować pewne sztuczki w celu stworzenia dobrego parsera. Na przykład procedury dla wyrażeń binarnych w ogóle nie są zgodne z oryginalną gramatyką. Zamiast tego używamy inteligentnego rekurencyjnego podejścia bazującego na priorytetach. Na wykładach z tworzenia kompilatorów zawsze polecam studentom korzystanie z narzędzia yacc.

LUA

225

Czy ma pan jakąś interesującą anegdotę związaną z pańskimi doświadczeniami w roli wykładowcy?

Roberto: Kiedy zaczynałem uczyć program ow ania, studenci korzystali przede wszystkim z kom puterów m ainfram e. Raz zdarzyło się, że program stworzony przez bardzo dobrą grupę studentów nie chciał się nawet skompilować. Kiedy im to powiedziałem, przysięgali, że uważnie testowali swój program z wykorzystaniem wielu przypadków testowych i program działał bez zarzutu. Oczywiście używali dokładnie takiego samego środowiska — komputera mainframe. Tajemnica wyjaśniła się za kilka tygodni, kiedy dowiedziałem się, że zaktualizowano kompilator Pascala. Aktualizacja została wykonana po zakończeniu zadania przez studentów, ale zanim zacząłem je sprawdzać. W program ie był bardzo drobny błąd składniow y (o ile pamiętam, był to nadmiarowy średnik). Tego błędu stary kompilator nie wykrył!

226

ROZDZIAŁ

SIÓDMY

ROZDZIAŁ

ÓSMY

Haskell

Haskell jest czysto funkcyjnym, leniwym językiem programowania zaprojektowanym jako otw arty standard dla nowoczesnych języków funkcyjnych. Pierwszy raport na temat Haskella pojawił się w 1990 roku, a standard został przyjęty w roku 1998. Przez lata język mocno się rozwinął, w szczególności w zakresie systemu typów, który ma wiele nowych własności. Popularność Haskella ostatnio wzrosła — powstało wiele dobrych bibliotek i praktycznych aplikacji. Wprowadzono znaczące poprawki w implementacji (najbardziej znaczące w słynnym kompilatorze GHC — Glasgow Haskell Compiler), zaczęła się także rozwijać społeczność użytkowników. Haskell jest szczególnie interesujący jako narzędzie do badań nad językami dziedzinowymi, współbieżnością oraz kontrolą stanów. Wysokiego poziomu abstrakcji do rozwiązywania problemów, jaki zapewnia ten język, nie da się porównać z żadnym innym językiem, zwłaszcza jeśli ktoś zrozumie podejście Haskella do projektowania oprogramowania. Nota redakcyjna: ten wywiad powstał na podstawie wymiany e-m aili z Paulem Hudakiem, Johnem Hughesem, Simonem Peytonem Jonesem oraz Philipem Wadlerem. Następnie został uzgodniony w rozmowie telefonicznej z Simonem Peytonem Jonesem.

2 27

Zespół języka funkcyjnego W ja k i sposób tworzy się język, gdy pracuje nad nim zespół?

Sim on P eyton Jones: Mieliśmy szczęście posiadania wspólnego celu (było nim tworzenie leniwego funkcyjnego języka programowania) oraz wspólnych technicznych obszarów zainteresowań. W naszym artykule na tem at historii Haskella1 opisaliśmy różne taktyki, które stosowaliśmy (spotkania twarzą w twarz, w ym iana e-maili, stanowisko redaktora i cara składni). Pracę ułatwiało nam również to, że mieliśmy rzeszę użytkowników, którzy dążyli do zapewnienia wstecznej zgodności z poprzednimi wersjami języka. W projekcie nie brały udziału firmy, dlatego nie musieliśmy przejmować się (sprzecznymi) celami korporacyjnymi. John Hughes: Mieliśmy wspólną wizję. Wszyscy byliśmy pasjonatami programowania funkcyjnego — w tam tych czasach panow ało olbrzymie ożywienie w tej branży. Wszyscy chcieliśmy mieć udział w tym, aby marzenie o programowaniu funkcyjnym stało się rzeczywistością. Poza tym mieliśmy dla siebie olbrzymi szacunek. Myślę, że pasja i szacunek m iały kluczowe znaczenie przy podejm ow aniu koniecznych, ewidentnie dziwacznych decyzji. Paul Hudak: Trzeba mieć wspólną wizję. Wątpię, aby bez tego udało się daleko zajść. Pierwsi członkowie Komitetu Haskella mieli bardzo spójną wizję. Należy do tego dodać m nóstw o energii. W Komitecie Haskella ilość energii była olbrzymia. Byli jak stado dzikich zwierząt. Potrzebna jest także pokora. Większa liczba pracow ników wcale nie gw arantuje szybszego w ykonania prac (zgodnie z zasadą opisaną w książce The Mythical Man-Month), ponieważ oprócz wspólnej wizji pomiędzy ludźmi występują różnice. Było między nam i bardzo dużo różnic, ale mieliśmy wystarczająco dużo pokory, aby osiągać kompromisy. Na koniec — potrzebne jest kierownictwo. Mieliśmy szczęście, ponieważ mogliśmy współdzielić kierownictwo — myślę, że to dość niezwykłe. Zawsze była jedna osoba, która kierowała operacją. Zawsze wiedzieliśmy, kim była ta osoba, i ufaliśmy, że zrobi wszystko, by wykonać zadanie. W ja k i sposób łączyli panowie pomysły w spójną całość?

Simon: Dużo się kłóciliśmy, głównie za pośrednictwem e-maili. Pisaliśmy techniczne rozprawy popierające nasz punkt widzenia i rozprowadzaliśmy je pomiędzy sobą.

1 Being Lazy w ith Class: the history of Haskell, referat wygłoszony na III Konferencji ACM na temat języków programowania (HOPL III), http://research.microsoft.com/~simonpj/papers/ his tory-of-haskell/index.htm .

228

ROZDZIAŁ

ÓSMY

Byliśmy skłonni do kompromisów, ponieważ opracowanie języka było dla nas ważne. Istotne było również uznanie, że po drugiej stronie kom prom isu także są ważne argumenty. John: Czasami uwzględnialiśmy w języku różne podejścia — na przykład styl równań w zestawieniu ze stylem wyrażeń. Haskell obsługuje obydwa. Jednak w większości przypadków prowadziliśmy długie, techniczne dyskusje na tem at rywalizujących ze sobą pomysłów, ostatecznie kończone kom prom isem . Myślę, że w ażną rolę odgrywała semantyka — chociaż nigdy nie opracowaliśmy kompletnej, formalnej semantyki dla wszystkich elem entów Haskella, to regularnie formalizowaliśmy fragmenty projektu. Jeśli coś było nieeleganckie semantycznie, zawsze był to silny argument przeciwko takiej propozycji. Zwracanie bacznej uwagi na formalną semantykę pomogło nam w opracowaniu przejrzystego projektu. Paul: Łączyliśmy nasze koncepcje poprzez debatę — w większości na poziomie technicznym, na którym określenia „dobrze” i „źle” często były oczywiste, ale również na poziomie subiektywnym, estetycznym, często głęboko osobistym, kiedy nie można było jasno stwierdzić, że coś jest „dobre”, a coś „złe” . Debaty często nie miały końca (niektóre trw ają do dziś), ale jakoś przez to przeszliśmy. W przypadku pozornie nieistotnych kwestii często polegaliśmy na naszych kierownikach, którzy podejmowali ostateczne decyzje. Na przykład mieliśmy cara składni, który podejmował ostateczne decyzje dotyczące szczegółów syn taktycznych. W ja k i sposób rozpoznają panowie najlepsze pomysły oraz w ja k i sposób postępują z własnościami, które się panom nie podobają?

Paul: Najlepsze pomysły są oczywiste — podobnie zresztą jak najgorsze. Najtrudniejsze problemy to takie, dla których nie istnieje czytelne, najlepsze rozwiązanie. Simon: Dla własności, które nam się nie podobały, po prostu szukaliśmy argumentów przeciw. Jeśli zrobiło to wystarczająco dużo osób, przebić się z takim pom ysłem było bardzo trudno. W rzeczywistości nie przypom inam sobie pomysłu, który był m ocno popierany przez jedną osobę lub m ałą grupkę, a ostatecznie został przegłosowany. Być może jest to m iara naszej wspólnej technicznej wiedzy, którą wnieśliśmy do projektu. Brak zgody do pewnego stopnia nie był problem em . Trudne tem aty znajdow ały ochotników chętnych do opracowania szczegółów. W językach występuje mnóstwo detali. Co się dzieje w takim czy innym niejasnym przypadku? W bibliotekach również występuje mnóstwo szczegółów. Są to bardzo istotne szczegóły. John: Najlepsze pomysły akceptuje się przez aklamację. Jednym z takich pomysłów był system klas: kiedy go zobaczyliśmy, wszyscy po prostu go połknęliśmy. Nie oznacza to, że nie było potem dużo pracy — na przykład przy obsłudze takich detali, jak domyślne egzemplarze oraz interakcje z modułami.

HASKELL

229

Jeśli chodzi o własności, które się nam nie podobają, prawie z definicji to są te tematy, które najintensywniej się dyskutuje. Użytkownicy narzekają na nie przez cały czas. Każdorazowo przy przeglądaniu języka ktoś stwierdza: „Czy w końcu nie możemy się pozbyć X?”, niezależnie od tego, czym jest X, i dyskusja rozpoczyna się od nowa. Oznacza to co najmniej tyle, że dokładnie wiemy, dlaczego w języku są własności, które nam się nie podobają i dlaczego się ich nie pozbędziemy. W niektórych przypadkach wiemy to od samego początku — na przykład niepopularne od zawsze ograniczenie monomorfizmu pozostało w tym samym kształcie od początku. Stało się tak dlatego, że rozwiązuje ono rzeczywisty problem i nikt jak dotąd nie znalazł lepszego sposobu na jego rozwiązanie. Innym razem poddawaliśmy decyzje projektowe pod dyskusję ze względu na zebrane doświadczenia. Zmieniliśmy interpretację jawnej ścisłości (ang. explicit strictness), kiedy okazało się, że pierwotny projekt utrudniał ewolucję programu. Zrobiliśmy to kosztem semantycznej elegancji — była to jedna z nielicznych okazji, kiedy zdecydowaliśmy się na coś takiego. Usunęliśmy przeciążanie list składanych (ang. list comprehensions), kiedy mechanizm ten okazał się mylący dla początkujących programistów. To, że udało nam się powrócić do projektu i poprawić błędy po zdobyciu nowych doświadczeń, nawet jeśli zmiany w języku były niezgodne z poprzednimi wersjami, ostatecznie poprawiło projekt języka. Czy istniały zalety tego, że byli panowie grupą? Czy to zobowiązywało do kompromisów?

Simon: Bycie grupą było najważniejszą rzeczą! Wszyscy mieliśmy w łasne języki i wierzyliśmy, że posiadanie wspólnego języka zapobiegnie dublow aniu wysiłków i pom oże naszym użytkow nikom uwierzyć w nas, ponieważ wszyscy pracujem y nad jednym językiem. Ta nadzieja spełniła się — Haskell odniósł olbrzymi sukces pod każdym względem. Był to sukces szczególnie spektakularny, jeśli wziąć pod uwagę nasze pierwotne oczekiwania. Paul: Z bycia grupą wynikały same korzyści. Oprócz wspólnej wizji (raz jeszcze) każdy z nas wnosił inną umiejętność. Ufaliśmy sobie wzajemnie i dużo się od siebie uczyliśmy. Były to fantastyczne interakcje pomiędzy inteligentnymi, energicznymi i ciężko pracującymi indywidualistam i. Haskell nie mógłby być zaprojektow any przez jedną osobę. John: Kompromis może być czymś bardzo dobrym! Praca w grupie z całą pewnością była wielką zaletą. Mieliśmy uzupełniające się doświadczenia i umiejętności. Myślę, że projektowanie języka w szerszej grupie z całą pewnością prow adziło do uzyskania bardziej użytecznych własności, niż m ożna by uzyskać, gdyby każdy z nas pracował w pojedynkę. Jedna osoba może zaprojektować mniejszy, prostszy, praw dopodobnie nawet bardziej elegancki język, ale nie sądzę, aby mógł on być równie użyteczny. Poza tym każda tru d n a decyzja m ogła być (i była) analizow ana z wielu różnych perspektyw. Co kilka głów, to nie jedna. Decyzja, która wygląda sensownie dla jednej

230

ROZDZIAŁ

ÓSMY

osoby, dla innej może być w oczywisty sposób wadliwa — często zdarzało się, że odrzucaliśmy jakiś pomysł, kiedy w wyniku dyskusji okazywało się, że ma on tego typu wady, a później znajdowaliśmy lepsze rozwiązanie. Myślę, że jakość projektu odzwierciedla uwagę, z jaką nad nim pracowaliśmy. Brzmi to trochę dialektycznie, nieprawdaż: teza+antyteza = synteza.

Trajektoria programowania funkcyjnego Co sprawia, że języki programowania funkcyjnego różnią się od innych języków?

Simon: To proste: zarządzanie efektami ubocznymi. John: Oczywiście uważne zarządzanie efektami ubocznymi. Funkcje pierwszej klasy (choć te znajdują swoje miejsce także w coraz większej liczbie języków imperatywnych). Spójne notacje dla czysto funkcyjnych operacji — począwszy od tworzenia struktur danych, a skończywszy na listach składanych. Sądzę, że lekkie systemy typów również są bardzo ważne — niezależnie od tego, czy to czysto dynamiczne systemy typów znane z języków Scheme i Erlang, czy też polim orficzne systemy bazujące na w nioskow aniu, typow e dla języków Haskell i ML. Oba systemy typów są lekkie w tym sensie, że typy nie przeszkadzają użytkownikowi naw et wtedy, gdy często korzysta z funkcji wyższego rzędu — to rzeczywiście stanowi sedno programowania funkcyjnego. Sądzę, że leniwe wartościowanie (ang. lazy evaluation) również jest bardzo ważne, choć oczywiście nie występuje w każdym języku funkcyjnym. Paul: Abstrakcja, abstrakcja i jeszcze raz abstrakcja, która dla mnie obejmuje funkcje wyższego rzędu, monady (abstrakcja sterowania), różne abstrakcje typów itd. Jakie są zalety pisania w języku bez efektów ubocznych?

Simon: Trzeba martwić się tylko o wartości, a nie o stan. Jeśli do funkcji przekażemy te same dane wejściowe, to za każdym razem uzyskamy te same wyniki. W ynikają z tego pewne implikacje dla wnioskowania, kompilacji czy współbieżności. Jak powiedział David Balaban (z firmy Amgen, Inc.): „Programowanie funkcyjne zmniejsza lukę pomiędzy myślą a kodem. To ważniejsze niż cokolwiek innego” . John: Cóż, dziś prawie nie ma takich języków. Programy w Haskellu m ogą mieć efekty uboczne, jeśli mają odpowiedni typ lub wykorzystują niebezpieczne operacje. Programy w językach ML i Erlang mogą mieć efekty uboczne. Efekty uboczne nie są podstawą dla całego programowania, ale raczej stanowią wyjątek — nie są zalecane i są uważnie kontrolow ane. W związku z tym trochę zmodyfikuję pana pytanie: jakie są zalety programowania w języku, który w większości nie ma efektów ubocznych? Wiele osób w tym miejscu zaczęłoby mówić o wnioskowaniu. Ja również tak bym zrobił, ale z czysto praktycznej perspektywy. Spróbujmy pomyśleć o testowaniu funkcji

HASKELL

231

w języku imperatywnym. Kiedy testujemy kod, musimy dostarczyć wielu różnych danych wejściowych i sprawdzić, czy wyniki są spójne ze specyfikacją. Jeśli występują efekty uboczne, te dane wejściowe zawierają nie tylko param etry funkcji, ale także fragmenty globalnego stanu czytane przez funkcję. Na podobnej zasadzie wyjście składa się nie tylko z wyników funkcji, ale także z tych fragm entów stanu, które funkcja modyfikuje. Aby móc skutecznie przetestować funkcję, trzeba mieć możliwość umieszczenia testowych danych wejściowych w tych fragmentach stanu, które funkcja czyta, oraz czytać te części stanu, które ona modyfikuje... Może się jednak zdarzyć, że do tych fragm entów naw et nie m a bezpośredniego dostępu. W związku z tym trzeba konstruować potrzebny stan testowy pośrednio — za pomocą sekwencji innych w yw ołań funkcji — a następnie obserwować efekty działania funkcji za pom ocą kolejnych wywołań (czytających informacje, które miały być zmienione przez funkcję) występujących za wywołaniem testowanej funkcji. Czasami nawet się nie wie dokładnie, które fragmenty stanu są odczytywane bądź zapisywane! Aby sprawdzić warunki po wywołaniu funkcji, trzeba mieć dostęp zarówno do stanu sprzed jej uruchomienia, jak i do stanu po uruchomieniu! Trzeba więc skopiować stan sprzed testu. Dzięki temu m ożna uzyskać dostęp do wszystkich potrzebnych informacji po przeprowadzeniu testu. Porównajmy tę operację do testowania klasycznej funkcji, która zależy tylko od swoich argumentów, a jedynym efektem jej działania jest zwracanie wyniku. Życie jest o wiele prostsze. Nawet dla takich programów, w których występuje wiele efektów ubocznych, sensowne jest wydzielenie maksymalnie obszernej części funkcjonalności do łatwo testowalnego, wolnego od efektów ubocznych kodu i zastosow anie cienkiego w rappera obsługującego efekty uboczne. W jednym z ostatnich postów na swoim blogu Don Stewartz zamieścił doskonały opis takiego podejścia. Zastosował je dla menedżera okien XM onad2. Przekazywanie w postaci argumentów wszystkiego, od czego funkcja zależy, powoduje, że zależności stają się czytelniejsze. Nawet w Haskellu można pisać programy, które przetwarzają jeden złożony stan. Jest on następnie przekazywany jako argum ent do wszystkich funkcji i zwracany jako wynik przez wszystkie funkcje, które go modyfikują. Zwykle jednak się tego nie robi: przekazujemy do funkcji tylko te informacje, których ona potrzebuje, i pozwalamy funkcji zwracać tylko te informacje, które ona sama generuje. Dzięki tem u zależności stają się znacznie czytelniejsze w porów naniu z kodem imperatywnym, w którym dowolna funkcja może z zasady zależeć od dowolnego fragmentu stanu. Zapominanie o takich zależnościach może spowodować najbardziej kłopotliwe błędy. Na koniec, natychm iast po rozpoczęciu program ow ania z efektami ubocznymi, ważna staje się kolejność wartościowania. Na przykład przed wykonaniem dowolnej operacji na plikach trzeba pamiętać o otwarciu uchwytu do pliku. Trzeba również

2 http://cgi.cse.unsw.edu.au/~dons/blog/2007/06/02#xmonad-0.2 .

232

ROZDZIAŁ

ÓSMY

pam iętać o tym, by zam knąć plik dokładnie jeden raz. Nie w olno go używać po zamknięciu. Każdy obiekt z obsługą stanów nakłada ograniczenia na kolejność, w jakiej m ożna używać jego API. Ograniczenia te są następnie dziedziczone przez większe fragm enty kodu — na przykład jakaś funkcja musi być w yw ołana przed zamknięciem pliku dziennika, poniew aż czasami zapisuje ona informacje w dzienniku. Jeśli program ista zapom ni o którymś z tych ograniczeń i wywoła funkcje w nieprawidłowej kolejności, dojdzie do awarii programu. Jest to bardzo istotne źródło błędów. Na przykład program Static Driver Verifier firmy Microsoft sprawdza, czy przestrzegamy ograniczeń nakładanych przez obiekty z obsługą stanów znajdujące się w jądrze systemu Windows. W przypadku programowania bez efektów ubocznych nie trzeba się tym martwić. Najtrudniejsze błędy, z jakimi m iałem ostatnio do czynienia, występowały w bibliotekach Erlang ze stanowymi interfejsami API. Używałem ich w kodzie o bardzo złożonej i zdeterminowanej dynamicznie kolejności uruchamiania. Ostatecznie jedynym sposobem na to, by mój kod zaczął działać, okazało się zbudowanie interfejsu API bez efektów ubocznych na bazie interfejsu standardowego. Obawiam się, że po prostu nie jestem wystarczająco inteligentny na to, by skutecznie tworzyć kod z efektami ubocznymi. Być może programowanie imperatywne byłoby łatwiejsze bez tych lat doświadczeń w programowaniu funkcyjnym... © Czy mówiłem coś o łatwości przetwarzania równoległego? Paul: Największe zalety to logika i przyjemność z rozwiązywania łamigłówek! © John Hughes wspominał o przetwarzaniu równoległym. Czy są jakieś inne zmiany w branży komputerowej, które powodują, że programowanie funkcyjne stało się jeszcze bardziej pożądane i potrzebne, niż było wcześniej?

Simon: Według mnie długoterminowy trend jest taki, że programowanie funkcyjne będzie nabierało coraz większego znaczenia jako narzędzie kontroli efektów ubocznych w językach programowania wszystkich typów. Pięciominutowy klip wideo znajdujący się pod poniższym adresem wyjaśnia, co mam na myśli: http://channel9.msdn.com/ShowPost.aspx7PostID-326762 Czy dążenie do wyeliminowania efektów ubocznych wynika z naturalnego rozwoju programowania strukturalnego — przypomina przejście do języków wyższego poziomu z bardziej wysokopoziomowymi strukturami sterującymi i pętlam i zamiast skoków i

instrukcji goto. Czy programowanie bez efektów ubocznych jest kolejnym etapem

rozwoju?

Simon: Jest to jeden ze sposobów, w jaki m ożna patrzeć na ten proces. Jestem tu ostrożny, ponieważ mówiąc o program owaniu strukturalnym, ludzie mają bardzo różne rzeczy na myśli. Zawsze uważam, że trzeba ostrożnie dobierać słowa, aby to, co staramy się powiedzieć zwięźle, nie zostało odebrane jako zgryźliwe.

HASKELL

233

W klasycznym liście na temat szkodliwości instrukcji goto „Goto Considered Harmful” Dijkstra napisał: „Usuńcie instrukcje goto ze swoich programów, aby stały się one bardziej zrozumiałe, łatwiejsze do skompilowania itd.” . Następnie można by uznać, że po to, aby programy stały się czytelniejsze, należy usunąć instrukcje przypisania. Uważam jednak za błąd uznawanie programowania funkcyjnego jedynie za narzędzie poprawy estetyki („Usuniemy wszystkie grzeszne i złe rzeczy, a pozostawimy nudne i trudne życie”). Zamiast powiedzieć: „Usuwamy to czy tam to”, mówimy: „Usuwamy niektóre rzeczy, ale w zam ian dajemy leniwe wartościow anie, funkcje wyższego rzędu, niezwykle bogaty system typów i m onady” . Ta zamiana nie zmusza programistów do innego myślenia na tem at programowania, zatem nie jest to bezbolesne przejście, ale takie, które daje dużo w zamian. W ja k i sposób w programowaniu funkcyjnym zmieniła się obsługa błędów?

Simon: O błędach można myśleć w nowy sposób — bardziej jak o błędnych wartościach niż o propagacji wyjątków. Błędna wartość jest jak liczba niew ym ierna (NaN) w zbiorze liczb zmiennoprzecinkowych. Dzięki tem u możemy spojrzeć na obsługę błędów w sposób bardziej zorientowany na wartości, a mniej na przepływ sterowania. Ogólnie rzecz biorąc, to bardzo dobra zmiana. W konsekwencji znacznie łatwiej wyrazić błędne działanie funkcji, jeśli m ożna posłużyć się jej typem. Na przykład zamiast: item lookup( key ) /* może zgłosić wyjątek nieznaleziony */ mamy: lookup :: Map -> Key -> Maybe Item przy czym typ danych Maybe wyraża możliwość wystąpienia awarii i robi to za pomocą wartości. W ja k i sposób w programowaniu funkcyjnym zmienia się debugowanie?

Paul: Po pierwsze, zawsze miałem wrażenie, że m etoda debugow ania polegająca na śledzeniu wykonyw ania, znana z języków im peratywnych, została odrzucona — nawet dla programów imperatywnych! Rzeczywiście niektórzy znani programiści imperatywni unikali tej metody. Zamiast niej stosowali metody bardziej rygorystyczne bazujące na testowaniu lub weryfikacji. Obecnie interesującą rzeczą dla języków funkcyjnych, a zwłaszcza leniwych języków funkcyjnych, jest to, że nie występuje w nich pojęcie „śladu wykonywania”. W związku z tym ta m etoda debugow ania nie jest najlepszą opcją. Kompilator GHC zawiera mechanizm śledzenia dla silnika redukcji grafów. Mechanizm ten podkreśla sposób wartościowania w tym kompilatorze. Jednak w mojej opinii ów mechanizm ujawnia o wiele za dużo na temat procesu wartościowania. Zamiast tego opracowano debuggery, takie jak Buddha, które bazują na zależnościach pomiędzy danymi. Debuggery te są

234

ROZDZIAŁ

ÓSMY

o wiele bardziej zgodne z deklaracyjnymi zasadami program ow ania funkcyjnego. Dość zaskakujące jest jednak to, że przez wszystkie lata mojego program ow ania w Haskellu nigdy nie korzystałem z debuggera Buddha ani debuggera kom pilatora GHC, ani też z żadnego innego debuggera. W mojej opinii testow anie dobrze się sprawdza. W ystarczy testować niewielkie fragm enty kodu za pom ocą narzędzia QuickCheck bądź innego podobnego narzędzia, a następnie — co m a kluczowe znaczenie — po prostu analizować kod, aby zobaczyć, dlaczego coś nie działa w taki sposób, w jaki oczekiwaliśmy. Przypuszczam, że wiele osób programuje podobnie. W przeciwnym wypadku byłoby znacznie więcej badań na temat debuggerów Haskella. Co prawda przez pewien czas był to popularny temat, ale już taki nie jest. Interesujące byłoby przeprow adzenie ankiety na tem at sposobów debugow ania program ów w Haskellu. Trzeba jednak powiedzieć, że istnieje inny rodzaj debugowania, którem u poświęca się znacznie więcej uwagi. Mam na myśli profilowanie czasu i zajętości pamięci, ale w szczególności zajętości pamięci. Wycieki pamięci to ukryta zmora programowania funkcyjnego, a profilowanie pamięci to ważne narzędzie walki z nią. Czy języki programowania funkcyjnego będą łatwiejsze do nauczenia się, jeśli podejdziemy do nich bez bagażu wielu lat doświadczeń w językach programowania imperatywnego?

Simon: Nie jestem pewien. Zdolność do nauki program owania funkcyjnego ściśle wiąże się ze zdolnościami do programowania w ogóle. Oczywiście trzeba trochę zmienić sposób myślenia, ale bystrzy programiści potrafią to robić. Myślę, że zrzucanie winy za niszowy status program ow ania funkcyjnego na to, że większość program istów najpierw zdobywała doświadczenia w technikach imperatywnych, jest niewłaściwe. Ważniejszym powodem jest występowanie efektu zamknięcia. Wielu programistów korzysta z C++. W związku z tym dla języka C++ istnieje bardzo dobre wsparcie w kompilatorach, narzędziach itp. Ale nawet to nie jest najważniejszym powodem: spójrzmy na błyskawiczny sukces języków Python lub Ruby. John: Nie, to jest mit. Większa część doświadczeń przydaje się — na przykład zrozumienie roli abstrakcji w program ow aniu, znajom ość algorytm ów i struktur danych czy też zrozumienie, że języki program ow ania m ają charakter formalny. Dobrzy programiści w C i C++, których uczyłem Haskella, osiągali znacznie szybsze postępy od całkowitych nowicjuszy. Doświadczony programista wie, czym jest błąd syntaktyczny. Może nie rozumieć systemu typów, ale wie, czym jest błąd systemu typów. Wie, że nadanie zm iennym opisowych nazw nie pom oże kom puterow i w zrozumieniu programów i poprawieniu błędów! Myślę, że mit powstaje dlatego, że programowanie funkcyjne wydaje się programistom im peratywnym trudniejsze, niż się tego spodziewają. Doświadczeni programiści są przyzwyczajeni do tego, że łatw o uczą się now ych języków, ponieważ mogą

HASKELL

235

bezpośrednio przenosić podstawowe pojęcia, takie jak zmienne, instrukcje przypisania czy pętle. W przypadku języków funkcyjnych to się nie sprawdza: nawet doświadczeni programiści muszą nauczyć się pewnych pojęć, zanim cokolwiek uda im się zrobić. W związku z tym uznają, że programowanie funkcyjne jest trudne. Pomimo to uczą się go znacznie szybciej i łatwiej od całkowitych nowicjuszy. Paul: Zwykłem mówić, że ugruntowane przyzwyczajenia utrudniają uczenie się czegoś nowego, ale nie jestem już tego pewien. Myślę, że dla najlepszych, najbystrzejszych i najbardziej doświadczonych programistów (dowolnego rodzaju) nauczenie się Haskella nie jest trudne. Co więcej, znajdują oni w tym dużą przyjemność. Ich doświadczenie pom aga im w zrozumieniu abstrakcji, rygorystycznej kontroli wyników, systemu silnych typów itp. Mniej doświadczonym programistom często sprawia to trudności. Czy zdaniem panów powodem jest to, że żaden z języków programowania funkcyjnego nie zyskał większego znaczenia?

John: Słaby marketing! Nie mam tu na myśli propagandy. Tego jest aż za dużo. Myślę o uważnym szukaniu w rynku docelowym takiej niszy, w której program ow anie funkcyjne m ogłoby dominować, a następnie o podejmowaniu wysiłków, by stało się ono najskuteczniejszym sposobem rozwiązywania problem ów. W szczęśliwych latach osiemdziesiątych uważaliśmy, że programowanie funkcyjne jest dobre do wszystkiego. Jednak określać technologię jako dobrą do wszystkiego to jak mówić, że jest dobra do niczego. Jaka powinna być marka? Problem ten bardzo dokładnie opisał John Launchbury w swoim referacie pow italnym na konferencji ICFP. Firma Galois C onnections prawie zbankrutowała w efekcie posługiwania się sloganem „oprogramowanie w językach funkcyjnych” . W ystarczyła zm iana na „oprogram ow anie o wysokim stopniu bezpieczeństwa”, by firma powoli zaczęła wzmacniać swoją pozycję. Wiele osób nie ma pojęcia, jaki jest m echanizm unow ocześniania technologii. Spodziewają się, że lepsza technologia stanie się dominująca sama z siebie (efekt lepszej pułapki na myszy), ale świat taki nie jest. Mój sposób myślenia na ten tem at bardzo się zmienił, kiedy przeczytałem książkę M oore’a Crossing the Chasm [HarperBusiness] oraz Christensena The Innovator’s Dilemma [Collins Business]. Gdyby w latach osiemdziesiątych istniała docelowa nisza... Co prawda było nią programowanie równoległe, ale okazało się ono niezbyt istotne aż do niedawna (do pojawienia się procesorów wielordzeniowych powstałych dzięki pomysłowości konstruktorów komputerowych). Myślę, że marketing był ważniejszy niż problemy techniczne. Choć te, takie jak na przykład niska wydajność, także były ważne. Paul: Ponieważ zbyt radykalnie różni się od program ow ania konwencjonalnego. Ta różnica sprawia, że jest ono trudne do zaakceptowania, trudne do nauki i trudne do obsługi (ze względu na biblioteki, implementacje itp.).

236

ROZDZIAŁ

ÓSMY

Czy ta sytuacja się zmienia?

Simon: Programowanie funkcyjne to inwestycja długofalowa. To radykalnie odmienny sposób myślenia o całym program ow aniu. Ta odm ienność sprawia, że jest ono trudniejsze do nauczenia się. A naw et jeśli ktoś się go nauczy, tru d n o jest mu przystosować się do nowego stylu, ponieważ jest to raczej zmiana rewolucyjna niż ewolucyjna. W dalszym ciągu nie wiadomo, czy programowanie funkcyjne stanie się głównym nurtem programowania. Natomiast wiadomo, że programowanie funkcyjne wywarło wpływ na najważniejsze języki. Co więcej, ten wpływ jest coraz większy. Oto kilka przykładów: odśmiecanie (ang. garbage collection), typy polimorficzne (generyczne), iteratory, technologia LIN, funkcje anonimowe i wiele innych mechanizmów. Są dwa powody, dla których wpływ programowania funkcyjnego jest coraz większy Po pierwsze, wraz ze zwiększaniem się skali programów oraz coraz większą dbałością 0 poprawność koszty nieograniczonych efektów ubocznych oraz korzyści ze stosowania bardziej funkcyjnego stylu programowania stają się coraz bardziej wyraźne. Po drugie (choć ten wpływ być może m a charakter przejściowy), procesory wielordzeniowe 1 program ow anie współbieżne odnow iły zainteresow anie czystymi obliczeniami lub przynajmniej obliczeniami, w których efekty uboczne są uważnie kontrolowane. Dobrym przykładem może być pamięć STM (ang. Software Transactional Memory). Trzeba pamiętać, że ostatnio nastąpił znaczący rozwój społeczności Haskella. Nie jest pozbawione sensu przypuszczenie, że jakiś uznany język funkcyjny wkrótce dołączy do głównego nurtu programowania (choć zgaduję, że nawet jeśli coś podobnego się zdarzy, to taki język będzie nosił nazwę Java3, a jego składnia będzie przypominała klasyczny język obiektowy). John: Oczywiście. Spójrzmy na Erlang — język skoncentrowany na bardzo specyficznej niszy — rozbudowanych systemach rozproszonych wykorzystywanych w systemach telekomunikacyjnych z olbrzymią kolekcją bibliotek dla każdego zadania związanego z telekom unikacją. Język ten m a to szczęście, że serwery internetow e wymagają praktycznie tej samej charakterystyki. Erlang być może nie jest jeszcze głów nym językiem programowania nawet w branży telekomunikacyjnej, ale ma bardzo wielu użytkowników i rozwija się w tempie wykładniczym. Wybór języka Erlang do tworzenia aplikacji telekomunikacyjnej nie musi być dziś kontrowersyjnym wyborem — dziś jest to sprawdzona technologia. Haskell nie jest tak mocno zaawansowany, ale zainteresowanie nim bardzo szybko wzrasta. Powstaje w nim wiele różnych aplikacji. Podobnie dzieje się z językiem OCaml. Systemy wielordzeniowe stwarzają unikatową okazję dla programowania funkcyjnego. Powszechnie uważa się, że programiści nie wiedzą, jak je programować, a coraz więcej osób zaczyna zastanawiać się nad alternatywnymi sposobami programowania systemów równoległych. Pod uwagę brane jest między innym i program ow anie funkcyjne.

HASKELL

237

Zabawne, że w dalszym ciągu można usłyszeć opinię o automatycznym zrównoleglaniu klasycznego kodu jako o rozwiązaniu krótkoterminowym, podczas gdy programowanie funkcyjne jest opisywane jako atrakcyjne podejście długofalowe. Faktem jest jednak, że gdyby ktoś zaczął tworzyć dziś produkt, który w m omencie jego planow anego wydania za rok ma wykorzystywać osiem rdzeni, to pisanie sekwencyjnego kodu w C, który ma realizować automatyczne zrównoleglanie kodu, byłoby skrajnie ryzykowną strategią. Decyzja o wyborze języka Concurrent Haskell lub SMP Erlang nie wiąże się żadnym ryzykiem, ponieważ są to technologie, które dziś już działają. Na rynku są już produkty w języku Erlang wykorzystujące procesory dwurdzeniowe. Produkty te, dzięki dodatkowem u rdzeniowi, działają dwukrotnie szybciej. Za kilka krótkich lat łatwe zrównoleglanie kodu stanie się kluczową zaletą języków programowania. W związku z tą zmianą języki funkcyjne mają więc okazję na wzmocnienie swojej pozycji. Paul: Zgadza się, środowisko do potencjalnego przyjęcia języków funkcyjnych zmienia się z kilku powodów: •

Inne języki zaadaptowały niektóre dobre idee programowania funkcyjnego, zatem zmiana w kierunku programowania funkcyjnego nie jest już tak radykalna.



Nowi programiści przychodzący do branży w ciągu ostatnich 15 lat mieli okazję lepszego poznania zagadnień program ow ania równoległego, m atem atyki oraz m etod formalnych. W związku z tym idee program ow ania funkcyjnego nie są już postrzegane jako radykalne.



Istnieje więcej bibliotek, implementacji oraz narzędzi, które sprawiają, że używanie języka staje się łatwiejsze i bardziej praktyczne.



Istnieje spory zbiór pomyślnie zrealizowanych aplikacji napisanych w Haskellu (lub innych językach programowania funkcyjnego). Dzięki temu wiele osób zaczyna ufać, że programy napisane w językach funkcyjnych mogą działać.

Czy fakt, że w 5 0 lat od momentu powstania programowania funkcyjnego w dalszym ciągu uważamy je za przydatne, mówi nam coś o stanie techniki komputerowej?

Simon: Myślę, że to mówi nam coś o programowaniu funkcyjnym. Jestem zwolennikiem program ow ania funkcyjnego, ponieważ jest ono zarówno ugruntow ane formalnie, jak i możliwe do praktycznego zastosowania. Przez „ugruntow ane form alnie” rozum iem to, że języki oraz ich im plem entacje (zwłaszcza takie języki jak Haskell) bazują na bardzo prostych regułach matematycznych — inaczej niż mocne, ale bardziej improwizowane języki, takie jak Python czy Java. Oznacza to, że programowanie funkcyjne nie przestanie być modne — programowanie funkcyjne reprezentuje zasadniczy sposób myślenia o przetwarzaniu danych, w żadnym razie nie jest to kwestia mody.

238

ROZDZIAŁ

ÓSMY

Przez „możliwe do praktycznego zastosowania” rozumiem to, że technika ta, z uwagi na znacznie poprawioną implementację i biblioteki, jest znacznie bardziej użyteczna teraz, niż była jeszcze 10 lat temu. Dzięki temu korzyści z ugruntowanego formalnie podejścia stają się dostępne dla znacznie szerszej grupy odbiorców. Wraz ze wzrostem zainteresowania takimi zagadnieniami, jak: •

bezpieczeństwo,



współbieżność,



błędy spowodowane efektami ubocznymi,

programowanie funkcjonalne będzie coraz bardziej widoczne i użyteczne. Technika kom puterowa zbliża się do punktu, w którym koszty program owania funkcyjnego będą miały mniejsze znaczenie niż wcześniej, a korzyści staną się bardziej cenne.

Język Haskell Wróćmy na chwilę do wcześniejszej wypowiedzi Johna Hughesa. Co tak bardzo spodobało się panom podczas projektowania systemu klas?

Simon: Wiedzieliśmy, że jest wiele problem ów związanych z porów nyw aniem dowolnych typów, sposobami wyświetlania dowolnych typów oraz wykonywania obliczeń. Wiedzieliśmy, że interesują nas liczby całkowite, zmiennoprzecinkowe oraz liczby podwójnej precyzji, a także liczby całkowite o dowolnej liczbie pozycji. Nie chcieliśmy, aby programista musiał pisać pi us int, plus float czy też plus arbitrary

precision integer. W iedzieliśmy, że chcemy znaleźć sposób, by m ożna było po prostu napisać A+B i uzyskać poprawny wynik. Rozwiązanie tego problemu przyjęte w języku ML polegało na um ożliw ieniu wpisania wyrażenia A+B, natom iast typ dodaw ania m usiał być rozwiązany w sposób lokalny. Jeśli ktoś napisał f (x.y) = x+y+l, system komunikował: „Musisz powiedzieć mi więcej. Podaj sygnaturę typu dla f ( ), tak abym wiedział, czy ten plus dotyczy dodaw ania liczb całkowitych, liczb zmiennoprzecinkowych, czy też liczb podwójnej precyzji” . Powodowało to konieczność przekazywania informacji o typach w wielu miejscach programu.

Simon: Jeszcze gorzej. Potrzebna była funkcja, którą można wywołać w odniesieniu do liczb zmiennoprzecinkowych, całkowitych lub podwójnej precyzji. Poprawienie jej tak, by zwracała dane jednego typu, jest bardzo kłopotliwe. W ten sposób funkcja traci uniwersalny charakter. Zamiast jednej trzeba napisać trzy funkcje: f float, f double oraz f integer — wszystkie o tej samej treści, ale różnych

HASKELL

239

sygnaturach typu. Do której z nich pow inien sięgnąć kom pilator po napotkaniu funkcji w treści programu? Problem funkcji pi us int pojawiał się na nowo, z tym że o jeden poziom wyżej. To nas bardzo m artw iło. Nie wyglądało ani pięknie, ani popraw nie. System klas rozwiązał ten problem. Dzięki niemu zapis f (x.y) = x+y+l był prawidłowy dla wszystkich przypadków. Wyrażenie przyjmowało typ Num a => a -> a -> a i działało dla dowolnego typu numerycznego, włącznie z takimi, o których jeszcze nie pomyśleliśmy! Do wykonania działania potrzebne są egzemplarze klasy Num. Doskonałe jest jednak to, że typ można zdefiniować później — w 10 lat po opracowaniu standardu Haskell, zdefiniow aniu klasy Num oraz napisaniu funkcji f. W ystarczy, że zdefiniujemy go jako egzemplarz klasy Num, a stara funkcja będzie z nim działać. Dlatego właśnie system klas tak bardzo przypadł nam do gustu. Mieliśmy problem, który wydawał się trudny do rozwiązania, a udało się go rozwiązać bez większych kłopotów. Większość początkowych prac wykonali Philip Wadler i Stephen Blott. System klas rozwiązał również problem równości. W języku ML zastosowano inne rozwiązanie dla równości. Jeśli zdefiniujemy składową typu member:: [a] -> Bool, która pyta, czy wartość jest elementem listy, to operacja ta wymaga sprawdzenia, czy wartości typu a są równe. Jednym z m ożliwych rozwiązań jest stwierdzenie, że każda wartość obsługuje równość, ale takie rozwiązanie nam się nie podobało. Nie pozwalało w sensowny sposób sprawdzić, czy funkcje są równe. Twórcy języka ML powiedzieli: „Dajemy w am specjalny rodzaj zmiennej typu o nazwie 'a. Składowa jest typu member :: a -> ['a] -> Bool. Zmienna 'a nosi nazwę zmiennej typu porównawczego (ang. equality-type variable). Jest ona dostępna tylko dla tych typów, które m ożna porównywać. M ożna zatem zastosować składową do zmiennej całkowitej lub znakowej, ale nie można zastosować składowej do funkcji, ponieważ w takim przypadku egzemplarz zmiennej ' a zostałby utw orzony przez funkcję, a to nie jest prawidłowe” . W języku ML dostępne było rozwiązanie inne od tego bazującego na przeciążonych liczbach, ale także związane z systemem typów, dlatego zmienne 'a pojawiały się w wielu miejscach. Problem sprawdzania równości został rozwiązany w całkowicie inny, specyficzny sposób. Niestety, pozostał problem porządkow ania. Co zrobić, aby posortować listę? Nie wystarczy samo porównywanie, trzeba także porządkować elementy. Klasy także rozwiązały ten problem. W Haskellu piszemy:

member :: Eq a => a -> [a] -> Bool sort :: Ord a => [a] -> [a] a zatem mówimy dokładnie, jakie właściwości musi mieć typ (odpowiednio równość lub porządkowanie). Dlatego właśnie system klas tak bardzo nam się spodobał. Pojedynczy mechanizm typów na poziomie systemu, który rozwiązywał wiele problemów — takich, dla których

240

ROZDZIAŁ

ÓSMY

wcześniej istniały tylko rozwiązania improwizowane lub niejednorodne. Był to jeden młotek, który rozłupał całą garść orzechów. Dla żadnego z tych „orzechów” nie istniało dobre rozwiązanie. Philip Wadler: Interesującą własnością klas definiujących typy było to, że wpłynęły one na sposób działania typów generycznych w Javie. Metoda Javy postaci:

public static T min (T x. T y ) { if (x.compare(y) < 0) x; else y: } jest bardzo podobna do metody Haskella:

min :: Ord a => a -> a -> a m i n x y = i f x < y then x else y nie m ówiąc o tym, że ta druga jest krótsza. Ogólnie rzecz biorąc, stwierdzenie, że zmienna typowa rozszerza interfejs (który zwykle jest parametryzowany w obrębie tej samej zmiennej typowej), spełnia w Javie tę samą rolę, co w Haskellu stwierdzenie, że zmienna typowa należy do klasy typu. Jestem pewien, że istniał bezpośredni związek pomiędzy tymi mechanizmami, ponieważ byłem członkiem zespołu (należeli do niego także M artin Ordersky, Gilad Bracha i wielu innych) pracującego nad typami generycznymi w Javie. Myślę, że z kolei na typy generyczne w języku C# miały wpływ typy generyczne z Javy. Nie uczestniczyłem jednak w pracach nad językiem C#, dlatego nie jestem pewien. N owa idea pojęć (ang. concepts) w języku C++ jest również bardzo podobna. W dokumentacji tego mechanizmu użyto klas typów z Haskella w celach porównawczych. Kiedy programiści Haskella doceniają cechę silnej typizacji?

Simon: System typów Haskella jest na tyle silny, że za jego pomocą można wyrazić istotną część projektu. Sprawdzanie typów to nie tylko sposób uniknięcia głupich pomyłek w rodzaju wyrażeń 5+True. Daje on programistom nowy poziom abstrakcji pozwalający na opisywanie programu. Pozwala mówić o jego projekcie i architekturze. Tam, gdzie programiści obiektow i rysują diagram y UML, program iści Haskella piszą definicje typów (a program iści ML piszą sygnatury modułów). To znacznie lepsze rozwiązanie — dokładne i możliwe do sprawdzenia za pomocą komputerów. Philip: Oto stara anegdota3. Firma Software AG opublikowała komercyjny produkt bazodanowy pod nazwą Natural Expert, w którym zapytania o dane oraz operacje manipulowania danymi były wykonywane za pomocą opracowanego przez tę firmę

3 H utchison N. et al., N atural Expert: a commercial functional program m ing environm ent, Journal of Functional Programming 7(2), marzec 1997.

HASKELL

241

języka funkcyjnego podobnego do Haskella. Firma ta prowadziła tygodniowe szkolenie na temat przygotowanego przez siebie języka. Na początku kursu programiści narzekali, że mechanizm sprawdzania typów generuje mnóstwo błędów. Na koniec szkolenia dochodzili do wniosku, że większość napisanych przez nich program ów działała doskonale, jeśli tylko udało im się przejść przez mechanizm kontroli typów. A zatem typy zastępowały mechanizm debugowania. Krótko mówiąc, osoby, które na początku tygodnia uznaw ały typy za swoich wrogów, na koniec postrzegały je jako swoich przyjaciół. Nie próbuję powiedzieć, że dowolny program , który napiszemy, będzie działał, kiedy tylko uda m u się przejść przez mechanizm kontroli typów. Mechanizm ten wychwytuje jednak olbrzymią liczbę błędów i sprawia, że debugowanie staje się o wiele łatwiejsze. Typy wydają się szczególnie ważne, gdy programista zaczyna wykorzystywać bardziej zaawansowane własności. Na przykład używanie funkcji wyższego rzędu jest znacznie łatwiejsze wtedy, kiedy istnieją typy upraszczające wykonywanie niektórych operacji. Funkcje polimorficzne w swoich typach odkrywają olbrzymią ilość informacji. Jeśli na przykład wiemy, że jakaś funkcja ma następujący typ: 1 :: (Int -> Int) -> [Int] -> [Int]

(funkcja przekształcająca dane typu integer i zwracająca listę liczb typu integer), to może ona wykonywać niemal dowolne operacje. Jeżeli natomiast ma typ:

m :: forall a b. (a -> b) -> [a] -> [b] (dla dowolnych typów a i b skorzystaj z funkcji przekształcającej dane typu a na typ b oraz listy danych typu a i zwróć listę danych typu b), to można na jej temat powiedzieć bardzo dużo. W rzeczywistości typ określa tw ierdzenie4, które funkcja spełnia. Na podstawie typu można udowodnić, że:

m f xs = map f (m id xs) = m id (map f xs) gdzie map stosuje funkcję do każdego elementu listy w celu uzyskania nowej listy, a id jest funkcją tożsamościową. Najczęściej samo m oznacza po prostu map, zatem (m id) to funkcja tożsamościowa. Może się jednak zdarzyć, że mzmieni kolejność elem entów — na przykład może odwrócić listę danych wejściowych, a następnie zastosować funkcję, albo najpierw zastosować funkcję, a następnie pobrać wszystkie pozostałe elementy wyniku. Na tym jednak koniec. Typy gwarantują, że wyrażenie musi zastosować funkcję do elementu listy wejściowej w celu uzyskania elementu listy wyjściowej oraz że nie może analizować wartości elementu po to, by zdecydować, co ma z nią zrobić. Analizuje tylko to, czy element znajduje się na liście. Dla mnie najbardziej niezwykłą cechą systemu typów jest to, że typy mają bardzo ścisłe powiązanie z logiką. Istnieje doskonała właściwość znana jako propozycje w roli 4 Wadler Ph., Theorems for Free, IV Międzynarodowa konferencja na tem at programowania funkcyjnego i architektury komputerów, Londyn 1989.

242

ROZDZIAŁ

ÓSMY

typów, czyli izomorfizm Curry’ego-Howarda. Zgodnie z nim każdy program jest w pew nym sensie dow odem propozycji. Typ program u odpow iada propozycji, którą program udowadnia. Z kolei modyfikowanie programu jest jak upraszczanie dowodu. Najbardziej podstawowe sposoby tworzenia struktury danych — rekordy, warianty i funkcje — odpowiadają trzem najbardziej podstawowym konstrukcjom w logice — koniunkcji, alternatywie i implikacji5. Okazuje się, że zasada ta sprawdza się dla wszystkich typów systemów logicznych i programów. Nie jest to zatem tylko kruchy zbieg okoliczności, ale głęboka i cenna zasada projektowania języków programowania bazujących na typach. Rzeczywiście daje ona przepis na projekt: pomyśl o typie, dodaj konstruktory w języku w celu stworzenia wartości tego typu oraz destruktory, które demontują wartości tego typu. Stosuj przy tym zasadę, że jeśli zbudujesz coś, a potem to zdemontujesz, otrzymasz to, od czego wychodziłeś (to się nazywa prawem beta), a jeśli coś zdemontujesz i zbudujesz ponownie, także otrzymasz to, od czego wyszedłeś (to jest prawo eta). Jest to niezwykle silne i doskonałe narzędzie. Wiele razy, kiedy coś projektujemy, czujemy, że istnieje pięć różnych sposobów w ykonania tej samej operacji, przy czym nie w iadom o, który z nich jest najlepszy. Powyższe prawo informuje nas jednak, że istnieje rdzeń języków funkcyjnych, który nie jest dowolny. Obecnie dochodzim y do punktu, w którym inform atycy coraz częściej wpisują przeprow adzone dowody do kom putera, tak aby to on sprawdził, czy m ają rację. Ta procedura bazuje na tych samych zasadach oraz tym samym systemie typów, na których bazują języki funkcyjne. Dzieje się tak ze względu na głębokie powiązanie pomiędzy programami a dowodami oraz pomiędzy typami a propozycjami. Widzimy zatem, że wszystko zaczyna być coraz bardziej spójne. Typy pozwalają na opisywanie coraz większej części działania programu, a kompilator jest w stanie zapewnić coraz więcej właściwości program u. Coraz częstszą praktyką będzie przeprow adzanie dowodów właściwości programów podczas ich pisania. Rząd Stanów Zjednoczonych czasami wymaga dowodów poprawności funkcji bezpieczeństwa w oprogramowaniu stosowanym w armii. Z pewnością ten trend będzie się umacniał. Obecnie systemy operacyjne nie dają zbyt silnych gwarancji bezpieczeństwa. Myślę jednak, że w tym zakresie nastąpi zmiana, a systemy typów odegrają w tym istotną rolę. Czy cechę leniwego wartościowania można przenieść do innych języków programowania, czy też pasuje ona do Haskella ze względu na jego inne własności?

John: Efektem leniwego wartościowania jest złożony i nieprzewidywalny przepływ sterowania. Nie jest to problem w Haskellu, ponieważ porządek wartościowania może wpływać na wynik — przepływ sterowania może być dowolnie złożony, co nie ma

5 Wadler Ph., New Languages, Old Logic, Dr. Dobb’s Journal, grudzień 2000.

HASKELL

2 43

wpływu na to, jak łatwo bądź jak trudno uruchamia się kod. Leniwe wartościowanie może być dodaw ane do innych języków (i było dodawane). Nie jest ono również specjalnie trudne do zasymulowania. Kiedy jednak leniwe wartościowanie powiążemy z efektami ubocznymi, otwierają się wszystkie bramy piekieł. Uruchomienie takiego kodu jest praktycznie niemożliwe. Nie m ożna bowiem zrozumieć, dlaczego efekty uboczne występują w takiej kolejności, w jakiej występują. Doświadczyłem tego w języku Erlang, kiedy symulowałem leniwe wartościowanie w kodzie wykorzystującym bibliotekę z interfejsem bazującym na efektach ubocznych. Ostatecznie jedynym sposobem na to, by mój kod zaczął działać tak, jak chciałem, było stworzenie czysto funkcyjnego interfejsu biblioteki na bazie interfejsu z efektami ubocznymi. Tak udało mi się sprawić, by mój kod z leniwym w artościow aniem nie pow odow ał efektów ubocznych. Myślę zatem, że odpowiedź na pańskie pytanie pow inna brzmieć następująco: tak, leniwe wartościowanie można wyeksportować do innych języków, ale programiści, którzy skorzystają z tego mechanizmu, będą musieli unikać efektów ubocznych w tej części kodu. Oczywiście mechanizm LINQ jest tu dobrym przykładem. Czy istnieją inne własności Haskella możliwe do zapożyczenia w innych językach oraz czy owo zapożyczenie sprawi, że język i te staną się bardziej użyteczne bądź bezpieczniejsze?

Philip: Istnieje kilka pochodzących z Haskella własności, które zostały w łączone lub są włączane do głównych języków. Domknięcia funkcyjne (wyrażenia lambda) pojawiły się w wielu różnych językach. Między innymi w językach Perl, JavaScript, Python, C#, Visual Basic i Scala. W Javie wprowadzono klasy wewnętrzne jako sposób symulacji domknięć. Jest też szeroko dyskutowana propozycja na temat dodania do Javy właściwych domknięć (podobnych do tych, które występują w języku Scala). Trend wprowadzania domknięć w innych językach nie pochodzi tylko z Haskella, ale także z innych języków funkcyjnych, łącznie z językami z rodziny Scheme i ML. Listy składane występują w Pythonie, C#, Visual Basicu (razem z mechanizmem LINQ) oraz języku Scala. Natom iast dla Perlą i JavaScript planuje się wprowadzenie takich własności. Język Haskell nie wprowadził list składanych, ale istotnie przyczynił się do ich popularyzacji. W łaściwości składania w językach C#, Visual Basic i Scala dotyczą również struktur innych niż listy. W związku z tym konstrukcje te przypominają bardziej m onady składane lub notację „ d o ” . Oba te m echanizm y w prow adzono w Haskellu. Typy generyczne w Javie zostały stworzone pod silnym wpływem typów polimorficznych oraz klas typów w Haskellu. Sam pomagałem w zaprojektowaniu typów generycznych w Javie. Byłem także współautorem książki na ten temat, wydanej przez wydawnictwo

244

ROZDZIAŁ

ÓSMY

O’Reilly6. Z kolei m echanizm y występujące w Javie zainspirowały wprowadzenie podobnych konstrukcji do języków C# i Visual Basic. Klasy typów występują także w języku Scala. Obecnie w języku C++ są prowadzone prace nad wprowadzeniem własności znanej jako pojęcie (ang. concept), która także jest blisko związana z klasami typów. Haskell wywarł również wpływ na szereg mniej powszechnie używanych języków, takich jak Cayenne, Clean, Mercury, Curry, Escher, Hal oraz Isabelle. John: Warto też wspomnieć o anonimowych delegatach w C# oraz listach składanych w Pythonie. Idee programowania funkcyjnego można spotkać w bardzo wielu miejscach. Paul: Czytałem wiele wypowiedzi osób, które uczyły się Haskella, ale rzadko z niego korzystały w swojej pracy. Twierdziły one, że Haskel zmienił (na lepsze) ich sposób myślenia oraz programowania w językach imperatywnych. Wpływ Haskella na główne języki oraz nowo powstające języki jest ogromny. Zatem chyba wykonujemy dobrą robotę. Wydaje się, że m am y wpływ na głów ny nurt, mim o że sami nie jesteśmy w głównym nurcie. Jakie jest powiązanie pomiędzy projektem języka a projektem oprogramowania napisanego w tym języku?

Simon: Język, w jakim piszemy oprogramowanie, ma olbrzymi wpływ na projekt program ów napisanych w tym języku. Na przykład wielu program istów wykorzystujących techniki program ow ania obiektowego używa diagramów UML do szkicowania projektu. W Haskellu lub ML zamiast nich pisze się sygnatury typów. Duża część wstępnej fazy tworzenia programu funkcyjnego składa się z pisania definicji typów. Jednak w odróżnieniu od diagram ów UML cały projekt jest w łączony w ostateczny produkt i dokładnie sprawdzony komputerowo. Definicje typów doskonale spraw dzają się przy pisaniu niezm ienników typu — na przykład „ta lista nigdy nie jest p usta” . Obecnie tego rodzaju twierdzenia nie są sprawdzane komputerowo, ale spodziewam się, że coraz częściej tak będzie. Rozbudowane typy zmieniają obraz pielęgnacji programów. Po zmianie typu danych można mieć pewność, że kompilator wskaże wszystkie miejsca, które należy zmienić w konsekwencji tego faktu. Dla mnie jest to jeden z najpoważniejszych powodów, dla których w arto wykorzystywać typy ekspresyjne. Nie potrafię sobie wyobrazić wprowadzania istotnych zmian w dużych programach z dynamiczną obsługą typów, jeśli nie ma się do dyspozycji podobnych mechanizmów. Korzystanie z języka funkcyjnego dramatycznie zmienia podejście do testowania, o czym doskonale opowiedział wcześniej John.

6 Naftalin M. i Wadler Ph., Java Generics and Collections, O ’Reilly, 2000.

HASKELL

245

W ykorzystanie języka funkcyjnego pcha programistę w stronę czysto funkcyjnych struktur danych zamiast struktur danych, które się ciągle zmieniają. Może to mieć olbrzymi wpływ na projektowanie programów. W Haskellu można pisać programy imperatywne, ale wyglądają one niezgrabnie. Programiści, tam, gdzie to możliwe, powinni kierować się czystością projektu. Paul: Podoba mi się odpowiedź Simona, chociaż skupił się on na tym, w jaki sposób Haskell (oraz inne języki program owania funkcyjnego) w płynął na projektowanie oprogram owania. M ożna by postawić odw rotne pytanie: w jaki sposób aplikacje w pływają na projekt języka? Haskell i większość innych języków funkcyjnych opracow ano jako języki ogólnego przeznaczenia. Jedną z doskonałych własności aplikacji napisanych w Haskellu w ostatnich latach jest jednak to, że wiele z nich napisano w językach tematycznych (Domain Specific Languages — DSL), które są osadzone w Haskellu. Jest wiele przykładów takich aplikacji — dotyczą one takich dziedzin, jak grafika, animacja, komputerowe przetwarzanie muzyki, przetwarzanie sygnałów, analiza składniowa, drukowanie, kontrakty finansowe oraz wiele innych. Powstało również mnóstwo bibliotek, których projekt bazuje na tej koncepcji. Tak jak agent nieruchomości ciągle powtarza, że lokalizacja, lokalizacja i jeszcze raz lokalizacja to trzy najważniejsze cechy nieruchomości, tak ja uważam, że abstrakcja, abstrakcja i jeszcze raz abstrakcja to trzy najważniejsze rzeczy w programowaniu. Dla m nie dobrze zaprojektow any język DLS to ostateczna abstrakcja dziedziny. Pobiera tylko tyle informacji, ile potrzeba — ani więcej, ani mniej. W przypadku Haskella doskonałe jest to, że dostarcza on frameworku do tworzenia takich języków DSL w sposób łatwy i efektywny. Nie jest to doskonała metodologia, ale jest ona bardzo dobra. Philip: Języki funkcyjne pozwalają na łatwe rozszerzanie języka w obrębie języka. Doskonałym tego przykładem są języki Lisp i Scheme. W książce Paula G raham a7 m ożna znaleźć informacje o tym, jak Lisp stał się tajną bronią podczas tworzenia jednej z pierwszych aplikacji webowych (która później stała się produktem firmy Yahoo!), a w szczególności makr w języku Lisp, które stały się kluczem do budowy tego oprogramowania. Haskell dostarcza także różnych własności, które ułatwiają rozszerzanie możliwości języka. Należą do nich wyrażenia lam bda, leniwe wartościowanie, notacja monadów, a także (w kompilatorze GHC) szablon Haskella do metaprogramowania. Paul wspominał już, że dzięki temu Haskell staje się ulubionym językiem do osadzania języków tematycznych. Staje się to także przydatne w mniej wzniosłych aplikacjach, na przykład do budowania niewielkich bibliotek wykorzystywanych przez kombinatory parserów (ang. parser combinators), a także do dokładnego drukowania. Jeśli ktoś naprawdę chce zrozumieć zalety programowania funkcyjnego, te dwa przykłady są doskonałym punktem wyjścia. 7 Graham P., Hakerzy i malarze. Wielkie idee ery komputerów, Helion, 2004.

246

ROZDZIAŁ

ÓSMY

Leniwe w artościow anie w Haskellu także ma wielki w pływ na m etodę pisania programów, ponieważ pozwala na dekompozycję problemów w sposób, który w innym przypadku byłby trudny do osiągnięcia. Jedną z moich ulubionych cech interpretacji leniwego wartościowania jest to, że pozwala ono na przeobrażanie czasu na przestrzeń. Na przykład zamiast myśleć o tym, w jaki sposób dostarczać wartości po kolei (czas), mogę zwrócić listę zawierającą wszystkie wartości (przestrzeń). Leniwe wartościowanie gwarantuje, że wartości na liście będą obliczane po kolei, tak jak trzeba. Myślenie o przestrzeni często jest łatwiejsze od myślenia o czasie: przestrzeń może być bezpośrednio wizualizowana, podczas gdy wizualizacja czasu wymaga animacji. Spróbujmy porów nać przeglądanie harm onogram u w ydarzeń w danym dniu z oglądaniem wideo z wypadków, jakie się w tym dniu wydarzyły. W ykorzystanie leniwego wartościowania może zatem bardzo zmienić sposób podejścia do problemu. Jednym z przykładów są wspomniane wcześniej kombinatory parserów, które zwracają listę wszystkich możliwych parserów. Leniwe wartościowanie gwarantuje, że lista ta zostanie przetworzona, kiedy stanie się potrzebna. W szczególności jeśli pierwsze parsowanie jest zadowalające, inne nie zostaną nigdy wygenerowane.

Nauczanie programowania (funkcyjnego) Co panowie zyskali z doświadczenia nauczania programowania studentów college'u?

Paul: Przez wiele lat języki funkcjonalne były (a może nawet są i dziś) wykładane głównie na początkowych latach studiów. Są one bowiem łatwe do nauki oraz wydzielenia z różnych szczegółów przetwarzania imperatywnego. Z perspektywy czasu uważam, że na dłuższą metę to błąd! Studenci szybko dochodzą do wniosku, że języki program owania funkcyjnego to zabawki. Wyciągają taki wniosek z tego, że są one używane na początkowych latach studiów, głównie na nierzeczywistych przykładach. Kiedy natom iast odkryją prawdziwą siłę efektów ubocznych, wielu studentów nigdy nie powraca do języków funkcyjnych. Wielka szkoda. Uważam, że najlepsze cechy program ow ania funkcyjnego często nie są doceniane przez początkujących. Dopiero po zdobyciu pewnych doświadczeń w programowaniu korzyści stają się widoczne. Na Uniwersytecie w Yale prowadziliśmy kurs programowania funkcyjnego, w którym uczestniczyli głównie studenci dalszych lat nauki. Nie miałem wtedy żadnych problemów z wykładaniem trudnych i złożonych problemów, a także zaawansowanych zagadnień m atem atycznych potrzebnych do pokazania prawdziwej siły program ow ania funkcyjnego. Co ważniejsze, mogłem powiedzieć: „Nabijcie tym swoją imperatywną fajkę i spalcie to”. Często tak właśnie postępowaliśmy — porównywaliśmy kod Haskella z kodem w C. To bardzo pouczające, a niestety niemożliwe do przeprow adzenia ze studentami, dla których Haskell jest pierwszym językiem.

HASKELL

2 47

Co złego jest w sposobie nauczania informatyki? Jakie zmiany w procesie nauczania panowie by zaproponowali?

Paul: Chciałem kiedyś napisać artykuł na tem at mojego celu w edukacji. M iałem nadzieję, że będzie to interesujące dla czytelników, a być może naw et inspirujące. Istnieją setki książek, które uczą sposobów programowania lub pisania programów w określonym języku. W książkach tych zazwyczaj wykorzystywane są przykłady pochodzące z różnych źródeł. Zwykle są one jednak dość banalne. Ich zakres obejmuje zagadnienia od ciągów Fibonacciego, obliczania silni, przetwarzania ciągów znaków i tekstu do prostych łamigłówek i gier. Zastanawiam się, czy możliwe jest napisanie książki, której głównym tematem jest coś innego niż programowanie i w której język programowania jest używany jako podstawowe narzędzie do nauczania najważniejszych pojęć. Przypuszczam, że wielu powie, że taką książką jest pozycja dotycząca systemów operacyjnych, pracy w sieci, grafiki lub kom pilatorów , o ile intensyw nie używa języka do objaśniania materiału. Mnie interesują jednak tematy, które nie dotyczą informatyki. Myślę na przykład o określonych naukach — fizyce, chemii, astronomii — czy naw et naukach społecznych, w szczególności ekonomii. Zastanaw iam się, czy jest możliwość pójścia o jeden krok dalej i nauczania elementów różnych dziedzin sztuki — zwłaszcza muzyki. Uważam, że język funkcyjny, szczególnie taki jak Haskell, który oferuje doskonałe wsparcie dla dziedzinowych osadzonych języków, mógłby być znakomitym narzędziem do nauczania pojęć innych niż programowanie. Doskonałą cechą programowania jest to, że zmusza ono programistę do dokładności, a świetną cechą programowania funkcyjnego jest to, że pozwala ono na zapewnienie spójności i dokładności. Obie te cechy można wykorzystać do nauczania wielu wspomnianych przeze mnie dyscyplin. Sim on: To pytanie dotyka tem atu, w który jestem dość m ocno zaangażow any w Wielkiej Brytanii. Jestem gubernatorem w szkole. W każdej szkole jest rada gubernatorów. Obecny sposób nauczania informatyki w szkole jest fatalny. Właściwie nie naucza się informatyki. Naucza się tylko technik informatycznych. Kiedy mówię o technikach informatycznych, m am na myśli arkusze kalkulacyjne, bazy danych i tym podobne programy. To tak, jakby powiedzieć: „To jest samochód, a tak się nim jeździ” . Tak właśnie naucza się używ ania arkuszy kalkulacyjnych. „A teraz, kiedy już umiesz jeździć, porozmawiamy o tym, dokąd mógłbyś jechać. Chcesz jechać do Birmingham? Jeśli chcesz jechać do Birmingham, to myślę, że możesz zaplanować swoją podróż w taki sposób. Mógłbyś też zabrać ze sobą te osoby” . Uczeń bierze udział w planowaniu projektu, analizie wymagań, integracji systemów i tym podobnych przedsięwzięciach. Na razie, kiedy jest w szkole, nikt go nie uczy, co znajduje się pod m aską sam ochodu. W pewnym sensie m ożna bronić takiego sposobu nauczania. Przecież każdy powinien umieć jeździć samochodem, prawda?

248

ROZDZIAŁ

ÓSMY

Co więcej, powinien wiedzieć coś o tym, gdzie może jeździć oraz jak uniknąć potrącenia pieszych. Nie każdy musi być zainteresowany sposobem działania samochodów. Nie ma nic złego w tym, że większość ludzi umie nimi tylko jeździć. Niektórzy pow inni być jednak zainteresowani tym, jak działają samochody. Istnieją dziedziny informatyki, czy też techniki obliczeniowej, które powinny być nauczane w szkole. Przynajmniej powinno się ich uczyć dzieci, które są tym zainteresowane. Na razie mówi się im: „Tak działają komputery” . Na tym jednak sprawa się kończy. Niewiele osób można w ten sposób zainteresować komputerami, ponieważ to jest nudne. W Wielkiej Brytanii pracuję w grupie roboczej, która próbuje wspomagać nauczycieli w nauczaniu techniki obliczeniowej lub inform atyki na poziomie szkolnym. Oczywiście chodzi o gimnazjum, do którego w Wielkiej Brytanii chodzą dzieci w wieku od 11 do 16 lat. Na poziom ie A naucza się techniki obliczeniowej na poziomie podstaw ow ym — jest to fragm ent prawdziwej inform atyki. W kursie poziom u A uczestniczy młodzież w wieku od 16 do 18 lat. Ale zanim to nastąpi, młodzi ludzie nie uczą się informatyki. Coraz mniej uczniów uczy się inform atyki w szkołach średnich, a jeszcze mniej decyduje się na jej studiowanie. Trend ten zaczyna przypominać sytuację ze Stanów Zjednoczonych. Częściowo powodem tej sytuacji jest to, że każde dziecko ma dziś komputer. W związku z tym dzieci wiedzą bardzo dużo na temat komputerów. Kiedy w szkole uczy się ich zagadnień informatycznych w różnych kontekstach, myślą sobie: „To jest po prostu nudne. Dlaczego miałbym się tym interesować?” . Myślę, że na tym polega najważniejszy błąd nauczania techniki obliczeniowej w szkołach. Tych, którzy nie są naprawdę zainteresowani techniką, wystarczy nauczyć, jak należy prowadzić sam ochód. Pow inna to być wiedza niezbyt rozległa oraz powiązana z innymi tematami. Należy skupić się na tym, że komputery są przydatnym narzędziem. Nie m a w nich nic wielkiego. Jednak niektóre dzieci pow inno się nauczać czegoś więcej. W końcu uczymy ich fizyki, a ona jest interesująca tylko dla mniejszości. Większość dzieci nie pamięta, czym są współczynniki, rozwinięcia i nic ich to nie obchodzi. Na tej samej zasadzie pow inno się uczyć informatyki. Chciałbym, aby niektóre dzieci miały o niej pojęcie i aby udało się je zainteresować komputerami dlatego, że praca z nimi może być ekscytująca.

Formalizm i ewolucja Jaką wartość dostrzegają panowie w definiowaniu formalnej semantyki dla języka?

Simon: Semantyka form alna poświadcza wszystko to, co zrobiliśmy w Haskellu. Na przykład w większości moich artykułów jest trochę podstaw formalnych. Wyjaśniają one, o co chodzi w artykule. Nawet dla tak imperatywnego zagadnienia jak pamięć transakcyjna w artykule znajduje się formalny opis semantyki transakcji.

HASKELL

249

Semantyka formalna to fantastyczny sposób na zrozumienie idei. Pozwala zrozumieć pewne detale, natom iast inne kłopotliwe szczegóły mogą pozostać otwarte. Jednak w realnym języku, w którym wszystko odgrywa istotną rolę, sform alizowanie wszystkiego jest dość kłopotliwe. Chylę czoła przed definicją standardu języka ML. Uważam ją za wyczyn. Myślę, że ML jest jedynym językiem, który posiada stosunkowo pełny opis formalny. Przypuszczam, że potrzebny zakres opisu formalnego zależy od korzyści, jaki ten opis przynosi. Koszt przekształcenia ostatnich 10% kolekcji form alnych fragm entów opisujących elementy języka na kompletny opis formalny jest bardzo wysoki. Wymaga olbrzymiej pracy. Może to być nawet 70% wszystkich nakładów pracy. Jakie korzyści uzyskuje się z tych 70% nakładów pracy? Być może jakieś 20%. Nie znam dokładnych liczb. W ydaje mi się, że współczynnik koszty/korzyści gw ałtow nie rośnie, kiedy próbujemy formalizować całość języka zamiast jego fragmenty. Jest to prawda nawet przy pierwszej wersji. Następnie próbujemy sobie zadać pytanie: „Ale co się stanie, kiedy język się rozwinie?” . Będziemy zmieniać Haskella. Jeśli będę musiał formalizować każdy aspekt tej zmiany, będzie to spory hamulec dla wprowadzania zmian w języku. To właśnie zdarzyło się w przypadku języka ML. Modyfikowanie języka ML jest dosyć trudne właśnie dlatego, że język ten ma formalny opis. Formalizm może być hamulcem dla innowacji. Jest bodźcem do innowacji, ponieważ pozwala zrozumieć, czym jest innowacja, ale jest hamulcem dla innowacji, jeśli istnieje rodzaj środowiska, które dyktuje konieczność form alizow ania wszystkiego na przestrzeni całego języka. Czy istnieje jakiś złoty środek, na przykład półformalizm — ubranie dżinsów i koszuli z kołnierzykiem?

Simon: Myślę, że to jest miejsce, które zajmuje Haskell. Definicja języka jest prawie w całości opisowa. Jeśli jednak przyjrzymy się towarzyszącym jej dokum entom badaw czym , to znajdziem y w nich wiele form alizm ów we fragm entach języka. Formalizmy te nie zostały zawarte w raporcie języka, przynajmniej nie w pełnym opisie. Dla języka, który nie ma opisu formalnego, można znaleźć znacznie więcej sformalizowanych m ateriałów niż dla języka C++. Język C++ jest całkowicie nieformalny, choć w stworzenie tego nieformalnego opisu włożono wiele wysiłku. To zabawny kom promis. Uważam, że formalizm przyczynił się w dużym stopniu do zapewnienia czystości Haskella. Nie wprowadzaliśmy w nim nowych elementów bez zastanow ienia. W szystko m usiało jakoś pasować w uporządkow any sposób. Daje to fantastyczną okazję, by powiedzieć: „To wygląda na bałagan. Czy na pewno to ma być właśnie tak?” . Jeśli coś wygląda na bałagan, są szanse, że będzie to trudno zaim plem entow ać, a programiście tru d n o będzie zrozumieć, co zostało zaimplementowane.

250

ROZDZIAŁ

ÓSMY

Philip: W stępny dokum ent na tem at klas typów napisałem wspólnie ze Stephenem Blottem. Dokument ten znalazł się w materiałach z sympozjum poświęconego zasadom tw orzenia języków program ow ania (Symposium on Principles of Programming Languages), zorganizow anego w 1989 ro k u 8. Formalizował rdzeń klas typów. Staraliśmy się, aby dokum ent był jak najprostszy i jak najbardziej zwięzły. Później wraz z Cordym Hallem, Kevinem H am m ondem i Simonem Peytonem Jonesem staraliśmy się stworzyć znacznie bardziej kom pletny model9. Referat na ten tem at pojawił się na konferencji ESOP w 1994 roku. Jak m ożna zauważyć, dopracowanie szczegółów zajęło pięć lat! Nie staraliśm y się formalizować całego Haskella, ale próbowaliśmy sformalizować wszystkie szczegóły klas typów. A zatem dla różnych celów istnieją różne właściwe poziomy modelowania. Referat na konferencji ESOP służył za bezpośredni model implementacji w kompilatorze GHC. Zwłaszcza wykorzystanie rachunku lam bda wyższego rzędu w roli języka pośredniego, co obecnie stanowi centralną część kom pilatora GHC. Jest to jedna z ciekawych własności formalizacji. Sformalizowanie elementu języka to dużo pracy. Kiedy jednak się tego dokona, jest to doskonały przewodnik do implementacji. Często się zdarza, że coś wydaje się trudne do zaimplementowania. Kiedy jednak zostaną podjęte wysiłkim, by to sformalizować, implementacja okazuje się znacznie prostsza. Innym przykładem formalizacji jest język Featherweight Java, który opracowałem razem z Atsushi Igarashi i Benjaminem Piercem. Dokum ent ten opublikowaliśmy na konferencji OOPSLA w 1999 roku (oraz powtórzyliśmy na konferencji TOPLAS w 2001 roku)10. W tamtych czasach wielu ludzi publikowało formalne modele Javy. Robiąc to, starali się oni, aby były one maksymalnie kompletne. Z kolei naszym celem formalizacji języka Featherweight Java było stworzenie modelu jak najprostszego. Staraliśmy się sprowadzić wszystko do niewielkiej składni, w której reguły zajmowałyby najwyżej jedną stronę. Okazało się, że to dobry pomysł. Model był tak prosty, że stanow ił dobrą podstawę, kiedy ktoś chciał dodać jedną now ą własność i ją zamodelować. Dokument ten wielokrotnie cytowano. Z drugiej strony okazało się, że w ystąpił błąd w pierw otnym projekcie typów generycznych, związany z operacjami przypisania i tablicami. Nie wykryliśmy go, ponieważ w języku Featherweight Java nie uwzględniliśmy ani operacji przypisania, ani tablic. Trzeba więc zdecydować się na kompromis pomiędzy prostym modelem, który daje wgląd w język, a bardziej kom pletnym m odelem pozwalającym na wychwycenie większej liczby błędów. Obie rzeczy są ważne! 8 Wadler Ph. i Blott S., How to make ad-hoc polymorphism less ad hoc, 16. Sympozjum na temat zasad języków programowania, Austin, Texas: ACM Press, styczeń 1989. 9 Hall C. et al., Type classes in Haskell, Europejskie sympozjum na tem at program ow ania, LNCS 788, Springer Verlag: 241 - 256, kwiecień 1994. 10Hutchison N. et al., Featherweight Java: A minimal core calculus for Java and GJ, TOPLAS, 23(3): 3 9 6 - 4 5 0 , maj 2001.

HASKELL

251

Byłem również zaangażowany w formalizowanie części definicji XQuery — języka zapytań dla XML — standardu W3C11. Oczywiście rozgorzały dyskusje wśród członków komisji standaryzacyjnych. W naszym przypadku wiele osób pytało: „O co chodzi z tą formalizacją? W jaki sposób m am to czytać?” . Osoby te nie chciały stworzyć z formalizacji obowiązującego standardu. Wolały, aby standardy były bardziej opisowe, ponieważ uważały, że dzięki temu będą bardziej zrozumiałe dla programistów. Jednak niektóre fragmenty systemu typów były łatwe do zapisania w sposób formalny i bardzo trudne do zapisania w sposób opisowy. Z tego pow odu zdecydowano, że dla tych fragmentów obowiązująca będzie specyfikacja formalna. W pewnym momencie ktoś zaproponował zmianę w projekcie. Interesujące było to, że pom im o w spom nianych wyżej uwag komisja standaryzacyjna poprosiła naszą grupę — osoby pracujące nad formalizacją — abyśmy opracowali formalny opis tej zmiany. Zrobiliśmy to i odkryliśmy, że choć propozycja zmiany zapisana w sposób nieformalny powinna być dokładna, było w niej 10 miejsc, w których nie wiedzieliśmy, jak sformalizować opis, ponieważ proza m ogła być zinterpretow ana na więcej niż jeden sposób. W związku z tym rozwiązaliśmy te problemy i zaprezentowaliśmy specyfikację formalną. Kiedy przy następnym spotkaniu zaprezentowaliśmy formalną specyfikację, zm iana została przyjęta jednogłośnie — nie było żadnej dyskusji. Na spotkaniach komisji standaryzacyjnej takie sytuacje prawie się nie zdarzają. Zatem w tym przypadku zastosowanie opisu formalnego było naprawdę dużym sukcesem. Tak jak powiedział Simon o Haskellu — zazwyczaj dążenie do formalizowania absolutnie wszystkiego oznacza zbyt duży wysiłek, aby opłacało się go ponosić. W przypadku języka XQuery sformalizowaliśmy około 80% specyfikacji. Istniało jednak kolejne 20%, które było ważne, ale formalizacja tych 20% wiązałaby się z takim dużym wysiłkiem, że ostatecznie z tego zrezygnowaliśmy. Myślę, że z tego, co sformalizowaliśmy, odnieśliśmy wiele korzyści. Oprócz tego specyfikacja formalna, którą opracowaliśmy, stała się rdzeniem systemu Galax zaimplementowanego przez moich przyjaciół, Mary Fernandez i Jerome’a Simeona. Galax jest dziś jedną z najważniejszych implementacji XQuery. Oto kolejny przykład na to, że formalizacja może ułatwić implementację. Wszyscy matematycy, których znam, mówią, że jeśli matem atyka nie jest piękna, to prawdopodobnie jest nieprawidłowa.

Simon: To prawda. Na przykład ostatnio pracujemy nad dodaniem do Haskella funkcji poziomu typów. W związku z tym próbujemy opracować ich specyfikację formalną. W tym roku opracowaliśmy na ten temat referat na konferencję ICFP, ale ja w dalszym ciągu nie jestem do końca zadowolony z tego opisu. A zatem ciągle nad tym pracujemy.

11 Simeon J. i W adler Ph., The Essence of XML, wersja wstępna: POPL 2003, Nowy Orlean, styczeń 2003.

252

ROZDZIAŁ

ÓSMY

Ma to bezpośrednie konsekwencje dla implementacji. Moglibyśmy opracować implementację według tego opisu, który już jest. Powiedzielibyśmy: „Implementacja jest taka, jaka jest, w ypróbujcie ją” . Istnieje jednak duża szansa, że użytkownicy zwróciliby się do nas następnego dnia i powiedzieliby: „To jest program, który miał wykonywać kontrolę typów, ale tego nie robi. Powinien to robić czy nie?” . Musielibyśmy wtedy odpowiedzieć: „Im plem entacja nie zawiera kontroli typów, więc być może nie. Ale masz prawo pytać” . Nie jestem niezadow olony z tego, że nigdy nie sformalizowaliśmy całości języka. Nie oznacza to jednak, że sformalizowanie całego języka nie przynosi korzyści. O statnie 70% pracy przynosi jakieś zyski. Być może współczynnik kosztów do korzyści nie jest dobry, ale jakieś korzyści są. Być może istnieją interakcje pomiędzy w łasnościam i języka, których nie zrozumieliśmy. Formalizowaliśmy elementy, ale nie wiedzieliśmy, że gdyby istniał chytry plan A i skomplikowana własność B, to jedno może zniszczyć drugie. Zawsze zachodzi taka obawa. Jeśli nad jakimś aspektem języka pracuje duża grupa ludzi, w końcu popełniają błędy.

Simon: Zgadza się. W takim przypadku możemy powiedzieć: „Gdybyśmy poszli dalej z formalizacją większej części języka, byłby on w lepszej formie”. To niezwykle ważne. Jednak po chwili zastanow ienia świadomie zrezygnowaliśmy z tego, co zrobiono w języku ML. Czy stosują panowie jakąś technikę do obsługiwania problemów wstecznej zgodności?

Simon: Myślę, że ciągle rozwijamy taki mechanizm, choć w przeszłości w większym lub w mniejszym stopniu ignorowaliśmy problem. Dziś już tego nie robimy. Około 10 lat temu opracowaliśmy język, który nazwaliśmy Haskell 98 jako rodzaj stabilnego podzbioru języka. Był to język, co do którego mieliśmy pewność, że nie będziemy go zmieniać. Kom pilatory Haskella domyślnie akceptow ały kod Haskella 98. Jeśli ktoś chciał kom pilować inny kod niż Haskella 98, m usiał przekazać pewne flagi, które komunikowały: zaakceptuj kod po wprowadzeniu nowego rozszerzenia. Była jedna flaga, która włączała wszystkie rozszerzenia. Obecnie jest ponad 30 rozszerzeń. Ta jedna flaga została zastąpiona przez około 15 flag. Jeśli spojrzymy na m oduł źródłowy, zwykle możemy powiedzieć, z jakiego rozszerzenia języka on korzysta. W rezultacie staliśmy się bardziej ostrożni przy zapraszaniu programistów do identyfikowania używanej przez nich odmiany języka. Ograniczeniem pow inno być zapewnienie działania starych programów, chociaż nie tylko to. Niektóre rozszerzenia włączają dodatkowe słowa kluczowe — na przykład foral 1 . W Haskellu 98 m ożna było stosować słowo kluczowe fora 11 jako zmienną typową — kiedy jednak włączymy rozszerzenia, foral 1 stanie się słowem kluczowym i zmienna typowa o nazwie foral 1 będzie niedozwolona.

HASKELL

2 53

Użytkownicy jednak bardzo rzadko dbają o wsteczną zgodność. W większości przypadków rozszerzenia są zgodne w górę. Z całą pewnością istnieją programy w Haskellu 98, które przestaną działać, jeśli włączymy zbyt dużo rozszerzeń. Czy w przyszłości powstanie Haskell 2009 lub 2010, w którym wszystkie wprowadzone rozszerzenia zostaną zebrane i stworzą nowy standard?

Simon: Tak. Tworzenie nowego standardu to bardzo zaawansowany proces znany pod nazwą Haskell Prime. To nazwa robocza — jeszcze nie zdecydowaliśmy, jak nazwiemy nowy standard. Pierwotnie chcieliśmy, aby znaleźć grupę osób, która będzie prow adziła publiczną debatę. W jej w yniku m iał wyłonić się now y język — rodzaj standardu, który moglibyśmy opublikować, po czym stwierdzić: „To jest Haskell 2010” . Byłoby to nieco inne działanie w stosunku do tego, co zrobiliśmy w Haskellu 98. Trudno jednak znaleźć wystarczająco liczną grupę ludzi, która mogłaby poświęcić odpowiednio dużo czasu, by coś takiego zrealizować. Przypuszczam, że obecna sytuacja jest swego rodzaju katastrofą w wyniku sukcesu. W związku z tym, że GHC jest najpowszechniej używanym kompilatorem Haskella, stał się on czymś w rodzaju standardu de facto. Oznacza to, że w praktyce użytkownicy nie napotykają zbyt wielu problemów spowodowanych niezgodnością języka pomiędzy różnymi kompilatorami. Nie sądzę, aby była to sytuacja zdrowa dla języka, ale skutecznie redukuje ona zapędy ludzi do poświęcania swojego najcenniejszego zasobu — czasu — w celu kodyfikacji standardu języka. Czy kiedykolwiek powstaną konkurencyjne implementacje blisko naśladujące standard języka kompilatora GHC?

Sim on: Już istnieją konkurencyjne im plem entacje, które są nieco bardziej specjalizowane w określonych dziedzinach. Zupełnie niedaw no, kilka miesięcy temu, na konferencji ICFP podjęliśmy ważne decyzje. Zamiast dążyć do stworzenia pojedynczego m onolitu — standardu Haskell Prime — spróbujemy przeprowadzić kodyfikację rozszerzeń języka. Nie będziemy definiować rozszerzeń w kompilatorze GHC. Zamiast tego mam y zamiar zaprosić wszystkich do zgłaszania propozycji, które rozszerzenia języka powinny podlegać kodyfikacji. Następnie poddamy je pod dyskusję i wyłonimy grupę osób, która napisze dodatek do raportu. W skrócie treść raportu m ożna by wyrazić stwierdzeniem: „O to wyczerpujący opis działań, które powinny realizować rozszerzenia języka” . Kiedy to zrobimy, będziemy mogli powiedzieć, że Haskell 2010 jest zbiorem wzajemnie spójnych rozszerzeń. Możemy postąpić tak jak wcześniej: najpierw skodyfikować i nazwać rozszerzenia, a następnie podzielić je na grupy. Będzie to działanie podobne do wprowadzania rozszerzeń Glasgow, ale nieco bardziej spójne. M amy nadzieję, że ten sposób projektow ania języka będzie trochę przypom inać działanie społeczności open source podczas wydawania nowej wersji systemu GNOMĘ,

254

ROZDZIAŁ

ÓSMY

Linux czy też innego oprogram owania. W tle dzieje się wiele rzeczy, aż w końcu ktoś okleja paczkę taśmą i mówi: „Wszystkie elementy działają, a ta kolekcja części nazywa się GNOMĘ 2.9” . Luźna kolekcja elementów połączonych wspólną filozofią i spięta ładną kokardą?

Simon: To prawda, a także obietnica, że są one wzajemnie zgodne ze sobą. To właśnie robimy po stronie języka. Język jest niemal w całości zdefiniowany przez implementację, zatem proces ten został już w dużym stopniu uporządkow any. Brak zapału do wprowadzania zmian wynika z tego, że jest zbyt uporządkowany. Po stronie bibliotek jest całkowicie odwrotnie. Nad bibliotekami pracuje wiele osób. Czy ktoś słyszał o bibliotece Hackage? Nowe biblioteki powstają prawie codziennie. Obecnie jest ich ponad 700. Oznacza to, że trudno odpowiedzieć na pytania, czy ta biblioteka działa, czy jest zgodna z tą biblioteką. To dość ważne kwestie dla zwykłego Jana Użytkownika, który stara się używać Haskella. Moim celem, jako autora kompilatora, jest odejście od projektowania i utrzymywania bibliotek. Zamiast tego inna grupa osób powinna realizować ten sam proces rozwoju, ale w odniesieniu do bibliotek. Powinna stworzyć coś, co można nazwać Platformą Haskella. W rzeczywistości byłby to zbiór skodyfikowanych bibliotek. Myślę, że to dość konwencjonalne podejście. Platforma Haskella będzie właściwie metabiblioteką, która zależy od określonej wersji kilkunastu innych bibliotek. M ożna powiedzieć: „O trzym ując Platformę Haskella, otrzymujesz zbiór bibliotek. Wszystkie one są opatrzone znakiem kontroli jakości oraz zgodne ze sobą” . Niezgodność pom iędzy bibliotekami może wystąpić wtedy, kiedy dwie biblioteki będą zależały od różnych wersji wspólnej biblioteki bazowej. Jeśli spróbujemy użyć obu tych bibliotek, uzyskamy dwie kopie biblioteki bazowej, czego prawdopodobnie nie chcemy. Jeśli biblioteka bazowa definiuje typ, to dwie różne kopie biblioteki mogą stworzyć różne wersje typu, wzajemnie ze sobą niezgodne. W takiej sytuacji mogą przestać działać mechanizmy, których działania oczekiwaliśmy. Nie będzie to prosta awaria, ale kłopotliwy komunikat o błędzie, informujący, że typ T z M odułu M w Pakiecie PI wersja 8 nie pasuje do typu T z Modułu M w Pakiecie PI wersja 9. Myślę, że udzieliłem długiej, wyczerpującej odpowiedzi na pytanie dotyczące zgodności wstecz. Zaczynamy podchodzić do tego problemu znacznie poważniej. Przy wydaniu nowej wersji kompilatora GHC mamy problem, ponieważ kompilator jest zbyt ściśle związany z bazowym pakietem bibliotek. Wszystko zależy od biblioteki Prelude?

Simon: Tak, ale dzieje się tak dlatego, że biblioteka Prelude jest bardzo przydatna. Definiuje mnóstwo przydatnych funkcji. Mówiąc „ściśle związany”, miałem na myśli to, że kom pilator zna dokładną implementację typu map. Zna jego nazwę i miejsce, w którym został zdefiniowany. Istnieją biblioteki, z którymi kom pilator GHC jest bardzo ściśle związany.

HASKELL

255

Czy jest to potrzebne do oszukiwania w czasie kompilacji?

Simon: W pewnym sensie. Jeśli kompilator ma wygenerować kod, który wywołuje funkcje biblioteczne, musi wiedzieć, czy te funkcje istnieją i jakiego są typu. Ta wiedza jest wbudowana w kompilator. Oznacza to, że jeśli zmienimy interfejs pakietu bazowego, co jest praw dopodobne przy przechodzeniu z wersji do wersji, to w przyszłych wydaniach będziemy zmuszeni dołączyć rodzaj opakowania dla pakietu bazowego. Opakowanie to powinno dostarczać takiego samego API, jaki m iał poprzedni pakiet bazowy. Dzięki tem u zyskujemy możliwość odizolowania się od zmian. Wszystkie te zabiegi są związane z zapewnieniem zgodności wstecz — cechy, o którą wcześniej nie musieliśmy dbać. Ale to jest problem związany z nieprzestrzeganiem reguły polegającej na unikaniu sukcesu za wszelką cenę. Z popularnością wiąże się osobny zbiór problemów.

Simon: To prawda, ale mieć takie problemy to przyjemność. Jakie wnioski z lekcji na temat powstania, rozwoju i przystosowania się języka Haskell do warunków współczesnych mogą wyciągnąć programiści, którzy tworzą systemy komputerowe dziś oraz będą je tworzyć w najbliższej przyszłości?

Simon: Programowanie funkcyjne to laboratorium , w którym bada się m nóstw o interesujących pomysłów. Ze względu na to, że podstawowe środowisko jest prostsze, możemy lepiej rozwinąć obiecujące idee (przykłady: systemy typów, programowanie generyczne, programowanie reaktywne, kontynuacje). Jeśli wejdzie się do laboratorium programowania funkcyjnego, można znaleźć w nim mnóstwo interesujących rzeczy. Czasami są one użyteczne bezpośrednio, a czasami nie, ale to, co się dzieje w laboratorium, to przyszłość. Paul: Najbardziej interesująca lekcja jest taka: ścisłe trzym anie się ideału — w przypadku projektow ania Haskella jest nim czystość projektu — bardzo się opłaca. Znalezienie właściwego rozwiązania może zająć jakiś czas. Jeszcze więcej czasu potrzeba na to, aby korzyści stały się widoczne, ale w końcu zostaną dostrzeżone. Proste rozwiązania mogą przynieść szybsze efekty, ale jeśli ich wdrożenie wymaga naruszenia zasad, ostatecznie i tak poniesiemy straty.

256

ROZDZIAŁ

ÓSMY

ROZDZIAŁ

DZIEWIĄTY

ML

ML jest językiem funkcyjnym ogólnego przeznaczenia opracowanym przez Robina Milnera oraz zespół pod jego kierownictwem na Uniwersytecie w Edynburgu w latach siedemdziesiątych. Język ten powstał na bazie metajęzyka zaprojektowanego do opisywania dowodów matematycznych. Najcenniejszą własnością projektu języka jest użycie algorytmu wnioskowania Hindleya-Milnera, który zastosowano w wielu statycznych systemach typów. Język zainspirował powstanie między innymi takich języków, jak Standard ML, Caml, Haskell i F#.

257

Dowodzenie twierdzeń Stworzył pan LCF — jedno z pierwszych narzędzi do automatycznego dowodzenia twierdzeń — oraz język programowania ML do obsługi procesu dowodzenia. W ja k i sposób one działały?

R obin M ilner: W latach siedemdziesiątych prow adzono inne projekty związane z maszynowym dowodzeniem. Stosowano dwa ekstremalne podejścia: całkowicie autom atyczne m etody wyszukiwania dowodów (na przykład bazujące na słynnej zasadzie rezolucji Robinsona) lub m etody całkowicie nieautom atyczne. Te drugie sprawdzały jedynie to, czy każdy składowy krok wykonywany przez człowieka jest logicznie praw idłow y (tak jak w systemie A utoM ath Bruijna). Oba te podejścia wywarły istotny wpływ na stosowane współcześnie techniki dowodzenia twierdzeń. Ja poszukiwałem rozwiązania pośredniego. Człowiek miał zaprojektować taktykę (lub strategię bazującą na składowych taktykach) i wprowadzić ją do maszyny razem z twierdzeniem do udow odnienia. W dow odzeniu twierdzeń zachodzą interakcje. Jeśli jedna taktyka zawodzi lub częściowo zawodzi, maszyna mówi „nie” i człowiek może zasugerować inną. Kluczowe znaczenie m iało to, że choć taktyki bywały ryzykowne, maszyna zgłaszała sukces tylko wtedy, gdy został znaleziony prawdziwy dowód. W przypadku sukcesu maszyna m ogła wyeksportować znaleziony dowód. N astępnie mógł on być sprawdzony przez niezależnie napisany program (lub za pomocą innej, nieautomatycznej metody). Kluczową zasadą, na której działał system, było to, że ML — metajęzyk, w którym użytkownicy mogli pisać opracow ane przez siebie taktyki — miał system typów (w pewnym sensie nowatorski, choć nie do końca). Dzięki systemowi typów język mógł odmówić zgłoszenia sukcesu, o ile nie mógł wypełnić wszystkich szczegółów dowodów przedstawionych za pomocą taktyk jedynie w zarysie. A zatem język ML był jedynie nośnikiem w spółpracy pomiędzy człowiekiem m ającym nadzieję na poprawność swojego dowodu a drobiazgową maszyną. Jakie są ograniczenia narzędzia LCF?

Robin: Nie dostrzegam oczywistych ograniczeń. Projektanci współczesnych systemów tworzą dość ryzykowne taktyki w takich systemach jak HOL, COQ i Isabelle, a rozwiązywane problemy stopniowo stają się coraz trudniejsze. W systemie HOL uzyskano niedaw no dowód poprawności systemu typów języka ML. Jest to mniej więcej tak jak potw ierdzenie, że system reprodukcyjny tw oich rodziców działał prawidłowo. Jesteśmy jednak daleko od chwili, w której będziemy w stanie przechwycić myśli matematyków i zapisać je w postaci taktyk. Przypuszczam, że większe postępy zostaną osiągnięte głównie poprzez stworzenie stosu prostszych twierdzeń, spośród których m ożna wnioskować tw ierdzenia bardziej złożone. W łaśnie w ten sposób buduje się większość teorii matematycznych.

258

ROZDZIAŁ

DZIEWIĄTY

Jeśli chodzi o dowody na działanie programów, są one w pewnym stopniu możliwe już dziś. Użytkownik może opisać program, który jest do udowodnienia, w postaci asercji. Jej treść brzmi mniej więcej tak: „Za każdym razem, kiedy zostanie osiągnięty określony m om ent działania programu, pomiędzy zmiennymi program u zachodzą określone relacje” . Czy takie podejście pozwala na analizowanie kodu źródłowego programu w celu udowodnienia, źe nie zawiera on błędów?

Robin: Tak! Narzędzie LCF często jest wykorzystywane właśnie w tym celu. Zwłaszcza do niewielkich program ów , w których niezawodność odgrywa kluczową rolę — na przykład do sterowania hamulcami samochodu. Największe problemy powstają wtedy, kiedy ktoś nie potrafi sformułować (lub odmawia sformułowania) żądanej właściwości w sposób ścisły! Ile wysiłku potrzeba do zdefiniowania tych właściwości w ścisły sposób?

Robin: To właściwie jest pytanie do osób zajmujących się logiką, zgodnie z którą mogą być wyrażane te właściwości. Język ML nie ma pełnić roli takiej logiki, ale raczej medium, za pom ocą którego m ożna wyrażać dowody. Powinien także dostarczać heurystycznych algorytmów wyszukiwania tych dowodów. A zatem język ML jest hostem takich logik. Pierwotną logiką, dla której język ML pełnił rolę hosta, było narzędzie LCF — logika funkcji wyliczanych według Dany’ego Scotta. Zgodnie z tą logiką wykorzystaliśmy język ML (i jego poprzedników) do znalezienia i/lub sprawdzenia pewnych twierdzeń. Z zadowoleniem stwierdzam, że jednym z tych twierdzeń był (prawie kom pletny) dow ód popraw ności kom pilatora z bardzo prostego języka źródłowego do bardzo prostego języka docelowego. Czy tę ideę można przenieść do innych języków programowania?

Robin: Zgodnie z tym, w jaki sposób zdefiniowałem rolę języka ML (hosta logiki), sądzę, że najbliższe pytanie, które pan po tym postawi, a na które mogę odpowiedzieć, brzmi następująco: czy inne języki mogą być tak samo dobrym i hostam i logiki dowodów? Jestem pewien, że mogą, o ile będą m iały rozbudow any i elastyczny system typów oraz będą obsługiwały zarówno funkcje wyższego rzędu, jak i własności imperatywne. Język ML miał to szczęście, że został wybrany jako host przez kilku twórców logiki, którzy odnieśli sukces. Dzięki tem u jest w dalszym ciągu wybierany. Dlaczego do tego, by język stał się dobrym hostem dla własności nazwanej przez pana logiką dowodów, konieczne są funkcje wyższego rzędu?

Robin: Reguły w nioskow ania im plem entuje się jako funkcje przekształcające twierdzenia na inne twierdzenia. Są to zatem funkcje pierwszego rzędu. Twierdzenia są w zasadzie zdaniami, a więc reguła wnioskow ania to nic innego, jak funkcja przetwarzająca ciąg znaków na inny ciąg znaków. Opracowaliśmy również tzw.

M L

259

taktyki — wyrażamy cel, czyli zdanie, które chcemy udowodnić. W wyniku uzyskujemy zbiór celów pomocniczych razem z funkcją, która na podstawie dowodów tych celów pomocniczych generuje dowód celu. Taktyka jest zatem funkcją drugiego rzędu. Mieliśmy pewną liczbę takich taktyk. Zaprogramowaliśmy je, a następnie chcieliśmy opracować mechanizm, który zespoli je razem w celu stworzenia większych taktyk. Następnie stworzyliśmy funkcję trzeciego rzędu, którą nazwaliśmy taktyką zbiorczą. Funkcje tego typu pobierają taktyki i tworzą większe taktyki. Taka taktyka może brzmieć następująco: „Najpierw pozbądź się wszystkich implikacji; umieść je na liście założeń, następnie zastosuj regułę indukcji i wykorzystaj reguły uproszczeń” . Była to dość złożona taktyka, którą nazwaliśmy strategią. Strategia pozwala uprościć kilka twierdzeń. Kiedy pracowałem dla Joh na M cC arthy’ego na Uniwersytecie w Stanford, powiedziałem: „Spójrz, zrobiłem coś interesującego. Mam taką strategię, taką taktykę, a to jest właściwość złożona z ciągów znaków, które ta taktyka udowadnia. Ja tylko wyrażam cel: fakt dotyczący ciągów znaków, a następnie stosuję m oją taktykę, która tworzy tę asercję w postaci tw ierdzenia” . Odpowiedział mi: „Jak ogólna jest tw oja taktyka? Do czego jeszcze m ożna ją zastosować?” . „M am pewien pom ysł” — dodał. „Co by się stało, gdybym chciał udowodnić takie twierdzenie?” — tu podał kolejny przykład. Nie m ówiąc m u nic o tym wcześniej, udow odniłem ten drugi przykład z zastosowaniem tej samej taktyki. A zatem zastosowaliśmy taktykę do przykładu i jak m ożna było przypuszczać, zadziałała. Nic na to nie odpowiedział, poniew aż był to jego sposób akceptacji. Udało mi się pokazać, że m am y taktykę polimorficzną. Pozwalała na przeprowadzenie więcej niż jednego dowodu. Opracował pan również teoretyczny framework do analizy systemów współbieżnych — rachunek systemów komunikujących się ze sobą (Calculus of Communicating Systems — CCS). N a jego podstawie powstał rachunek pi. Czy mogą one pomóc w badaniach nad rozwojem obsługi współbieżności w nowoczesnym sprzęcie i oprogramowaniu?

Robin: Zacząłem myśleć na tem at systemów współbieżnych podczas pracy w laboratorium Sztucznej Inteligencji M cCarthy’ego na Uniwersytecie Stanford w latach 1971 - 1972. Uderzyło mnie to, że w istniejących językach nie było dobrych m echanizm ów, które by dobrze obsługiwały współbieżność. Przede wszystkim poszukiwałem teorii matematycznej, którą można by wykorzystać w językach w roli semantyki — to im plikowało potrzebę zastosow ania rozwiązania m odularnego. Należało stworzyć współbieżny system komunikacyjny złożony z mniejszych systemów. W tamtym czasie istniał już doskonały model stworzony przez Carla Adama Petriego — sieci Petriego — który doskonale modelował związki przyczynowe. Istniał również model Aktor Carla Hewitta. Sieci Petriego nie były modularne. Z kolei model Hewitta nie odpowiadał mi dlatego, że był daleko od współbieżnej teorii automatów, do której

260

ROZDZIAŁ

DZIEWIĄTY

chciałem się zbliżyć. Chciałem też zastosować prymityw synchronizowanej komunikacji (uzgadnianie). Poza tym teoria automatów z semantyką w postaci języków formalnych (zbioru ciągów symboli) niezbyt dobrze wykorzystywała algorytmy niedeterministyczne i interakcje. W związku z tym podjąłem własną próbę — był nią system CCS. Najbardziej chciałem dojść do algebraicznego ujęcia — najpierw elem entów statycznych, potem dynam icznych. Temu tem atow i poświęcono wiele lat badań. Istotnym wydarzeniem było wprowadzenie przez Davida Parka pojęcia bisymulacji, bazującego na twierdzeniu o maksymalnych punktach stałych (ang. maximal fixed points). Początkowo chciałem modelować systemy, które rekonfigurują swój stan. Na przykład procesy A i B nie mogą się ze sobą skomunikować, dopóki proces C będący w kontakcie zarówno z procesem A, jak i B nie prześle procesowi A adresu procesu B. Początkowo projekt, który realizowałem wraz z Mogensem Nielsenem, nie pow iódł się (z pow odów matem atycznych). Następnie Mogens popraw ił coś, co niezbyt dokładnie przemyśleliśmy. To doprowadziło do powstania rachunku pi, który Mogens opracował wspólnie z Joachimem Parrowem i Davidem Walkerem. Rachunek pi był doskonały nie tylko dlatego, że dotyczył rekonfiguracji, ale pozwalał także — bez żadnych dodatków — na reprezentowanie typów danych. Rachunek pi przypom ina więc podstaw ow y rachunek m obilnych systemów współbieżnych. Pod tym względem jest on podobny do rachunku lambda w odniesieniu do systemów sekwencyjnych. Rachunek pi znalazł wiele zastosowań. Okazał się przydatny naw et w systemach biologicznych. Ważniejsze jest jednak to, że na jego podstawie powstał cały szereg rachunków , które w bardziej bezpośredni sposób m odelują systemy rozproszone. Rachunki te obsługują takie zagadnienia jak mobilność oraz zachowania stochastyczne. Wydaje się, że zamiast sfinalizować teorię procesów współbieżnych, otworzyliśmy nową puszkę Pandory. Czy można zrozumieć system naukowo, zanim go się zaprojektuje i zbuduje?

Robin: Dużo o tym myślałem w pow iązaniu z wszechobecną dziś techniką obliczeniową, ale myślę że problem m a charakter ogólny. Potrzebny jest rodzaj modelu, który przedstawia zamierzony sposób działania systemu. Na najprostszym poziomie istnieje maszyna von Neumanna. Jest to naukowy, formalny, czyli ścisły model, który dał początek FORTRAN-owi oraz całej gamie języków sekwencyjnych. To jest model naukowy. Jest bardzo prosty i na tym polega jego piękno. Potrzebny jest model, z którego można wyodrębnić lub na podstawie którego można zdefiniować język programowania. Dla systemów powszechnego użytku model ten może bardzo odbiegać od maszyny von Neumanna. Musi być to urządzenie obsługujące w ogólny sposób populacje agentów i czujników, które działają i wzajemnie komunikują się ze sobą.

M L

261

Brzmi to tak, jakb y mówił pan o zbiorze metajęzyków wyrażających semantykę.

Robin: Nie jestem zwolennikiem używania słowa „język”, jeśli wcześniej nie istnieje jego model. Oczywiście pozostaje to w całkowitej sprzeczności z tym, co dzieje się w rzeczywistości. Języki są definiowane za pomocą metajęzyków i często dzieje się to, zanim powstanie dobry model — o ile oczywiście metajęzyk dostarcza jakiegokolwiek modelu. Być może metajęzyk jest w tym przypadku synonimem modelu. Z metajęzyka skorzystaliśmy podczas opracowywania definicji języka Standard ML. Był to rodzaj indukcyjnego wnioskowania dotyczącego tego, jakie instrukcje są dozwolone i jakie działania m ają wykonywać. Sądzę, że tym właśnie jest m odel w ogólnym ujęciu. Zgadzam się z panem. Mówię o zbiorze lub czymś w rodzaju rodziny metajęzyków. Każdy z nich jest wyspecjalizowany dla określonego systemu. Taki system można nazwać programem. Czy w tym sensie technika komputerowa jest definicją i formalizacją modeli w kilku warstwach, przy czym modele z niższych warstw pozwalają na budowanie modeli w warstwach wyższych?

Robin: Tak. Właśnie w taki sposób postrzegam powszechne urządzenia komputerowe. Chcemy, aby zachowanie określonego systemu odzwierciedlało bardzo wiele pojęć, ale bezpośrednie uwzględnienie wszystkich tych pojęć w jednym modelu nie zawsze jest możliwe. M ówiłem o stosie modeli, w którym dolna warstw a zawiera dość elementarną maszynę. W miarę przechodzenia w górę napotykamy bardziej interesujące elementy — bardziej ludzkie lub bardziej subtelne pojęcia — na przykład zarządzanie awariami, sam oświadomość, zaufanie, bezpieczeństwo itp. M odele są budow ane w sposób warstwowy. Dzięki temu w każdym modelu można mówić o zarządzalnym zbiorze pojęć. Następnie są one implementowane w modelu niższego poziomu. W kontekście języków Lisp i Forth często mówi się o wyodrębnianiu i budowaniu systemów z fragmentów wielokrotnego użytku. W pewnym sensie opracowuje się bogaty język do rozwiązywania pewnego problemu.

Robin: We wspomnianym przeze mnie stosie modeli na niższych poziomach znajdują się elementy, które można nazwać programami. Na wyższych poziomach znajdują się specyfikacje lub opisy tego, co może bądź nie może czy też powinno lub nie powinno się zdarzyć. Mogą one występować w różnych formach logicznych. Nawet w języku naturalnym. Kiedy schodzimy do niższych warstw, otrzymujemy znajome elementy zwane programami. Mogą one być uznawane za konkretne modele. Czy jest to potwierdzenie idei, że nasze modele obliczeniowe są wewnętrznie proceduralne?

Robin: Tak. Jeśli przyjrzeć się bardziej dynamicznym, jawnym modelom, to widać, że są one proceduralne. Może istnieć model specyfikacji. Mógłby on składać się z logicznych predykatów. Co prawda nie jest to zbyt dynamiczny model, ale pozwala

262

ROZDZIAŁ

DZIEWIĄTY

na wykorzystanie par formuł predykatowych do reprezentowania warunków wstępnych i ostatecznych. A zatem można oszacować poprawność implementacji na podstawie tego, czy m ożna ją logicznie zweryfikować. Od specyfikacji bądź modelu, które nie są w oczywisty sposób dynamiczne, przechodzim y do m odelu dynamicznego. To interesujące. Nie do końca rozumiem przejście od modeli dynamicznych w niższych warstwach do opisowych w warstwach wyższych, ale takie przejście jest faktem. Alternatywnie — na przykład w metodzie interpretacji abstrakcyjnej — na wyższym poziomie w dalszym ciągu jest model dynamiczny, ale wykorzystuje pewną abstrakcję danych. Właściwie nie jest to program, ale jest to konstrukcja dynamiczna. Właśnie taki mechanizm Francuzi wykorzystali do weryfikacji oprogramowania wbudowanego europejskiego Airbusa. Pytanie o to, kiedy model jest dynamiczny, a kiedy tylko opisowy, jest interesujące i bardzo złożone. Być może to przejście następuje w momencie, kiedy musimy potwierdzić prawa fizyki —

na przykład zachowanie bramek NAND. Rozumiemy te procesy fizyczne, ale istnieje

punkt, w którym stworzone przez nas modele uwzględniają ten poziom abstrakcji.

Robin: Tak. Istnieją schem aty obw odów kom putera i dotyczą one elektroniki. Na wyższym poziomie zaś jest kod asemblera, który już nie mówi o elektronice. Jeśli jednak przejść do wyższych warstw, okazuje się, że pozostaje dynamiczny element w programie, tak jakby miał być przekształcony na element dynamiczny w diagramach obwodów. Wydaje się, że pozostaje możliwość przechodzenia przez różne pojęcia dynamiczne przy zachow aniu dynam iczności, ale w miarę przechodzenia w górę zdolność ta jest zupełnie innej natury. Modele logiczne często także mają element dynamiczny. Na przykład logika modalna jest zdefiniowana w kontekście możliwych światów i przechodzenia z jednego świata do innego. Istnieje element dynamiczny, ale trochę zamaskowany. Mogę sobie wyobrazić osoby stawiające zarzuty, że błędy lub elementy nierozstrzygalności w niższych warstwach mogą mieć wpływ na możliwości obliczeń w warstwach wyższych.

Robin: W ydaje się, że to jest po prostu fakt z życia. Rozwiązanie problem ów nierozstrzygalności na niższym poziomie może być niemożliwe, ale na wyższych poziomach realizuje się je za pomocą mechanizmów kontroli typów. Typy są modelem abstrakcyjnym. Mogą być rozstrzygalne, ponieważ to słaba abstrakcja. Nie zawiera elementów, które prowadzą do nierozstrzygalności. Oczywiście mówią one tylko o pewnym wybranym aspekcie programu. W związku z tym zyskujemy rozstrzygalność, wchodząc do wyższych warstw kosztem szczegółowości. Nie myślałem o tym w ten sposób.

Robin: Ja również niespecjalnie. Jeśli chodzi o kontrolę typów, to istnieją systemy pozwalające na rozstrzygnięcie, czy w program ie są właściwie stosowane typy.

M L

2 63

Następnie wystarczy zrobić pozornie drobną operację i okazuje się, że system jest nierozstrzygalny. W ystarczy dodać do systemu typów trochę więcej szczegółów. W łaśnie to przytrafiło się systemowi typów zastosowanemu w języku ML. System był rozstrzygalny, ale po dodaniu typów koniunktywnych stał się nierozstrzygalny. Istnieje tu rodzaj rozdźwięku pomiędzy tym, co warto mieć, a tym, co daje się zarządzać. Wiele razy jest tak, że naw et jeśli nie zawsze m ożna coś sprawdzić w odniesieniu do koniunktyw nego systemu typów , to m ożna odnieść sukces z dostatecznie inteligentnym systemem dowodzenia twierdzeń. Wszystko to m ożna wyrazić za pom ocą pojęcia, które ja nazywam stosem modeli. W miarę wchodzenia coraz wyżej tracimy coraz więcej informacji. Możemy zyskać pewne cenne możliwości analityczne — sposobność do analizow ania pewnych właściwości programów. W arto coś o nich wiedzieć naw et wtedy, gdy obraz jest niepełny. Słyszałem, że można pójść w innym kierunku. Wyrażenie w modelu na wyższym poziomie oznacza możliwość usunięcia nierozstrzygalności z niższych poziomów, o

ile można udowodnić, że określone warunki nigdy nie zajdą.

Robin: Myślę, że tak. Niższy poziom składa się z modelu nierozstrzygalnego, ale przy pewnych ograniczeniach dla elementów może on stać się rozstrzygalny. Czyli nierozstrzygalność nie jest tak zła, ja k się wydaje?

Robin: Nie jest, ale to interesujący temat. Pozwala bowiem zobaczyć, jakie korzyści przynoszą nam modele i jaki jest ich wpływ na nierozstrzygalność. Myślę, że to dobry temat. W jaki sposób informatycy, inżynierowie komputerowi lub programiści powinni nauczać osoby, chcące jedynie wykonać swoją pracę, pojęć związanych z twierdzeniami, ich dowodzeniem i stosowaniem typów?

Robin: Myślę, że robienie tego zbyt wcześnie może mieć fatalne skutki. Temu jestem przeciwny, choć zdarza się to również w matem atyce. Robi się coś, co później będzie się robiło w sposób bardziej abstrakcyjny, ale ktoś zbyt wcześnie przechodzi do konkretów i występują trudności ze zrozumieniem prezentowanych zagadnień. Wykładamy geometrię Euklidesa, a nie mówimy nic o innych rodzajach geometrii. Później, na przykład w drugim roku nauki na uniwersytecie, studenci zaczynają rozumieć, czym m ogą być inne geometrie, podczas gdy ten poziom abstrakcji w większości nie jest dostępny dla przeciętnego siedem nastolatka. Przyjmowanie wielu założeń, które uzasadniają nauczanie rzeczy niezrozumiałych, jest niemądre. Wiem, że popełnia się również błędy, jeśli chodzi o poziom zaawansowania programu studentów ostatnich lat studiów, ponieważ także dla nich niektóre problemy są zbyt abstrakcyjne. Duża część teorii obliczeniowej jest zbyt abstrakcyjna nawet na tym

264

ROZDZIAŁ

DZIEWIĄTY

poziomie. Z tym po prostu trzeba nauczyć się żyć. Problem polega na tym, że aby dobrze zrozumieć tem at bez tych abstrakcji, trzeba stosować rodzaj hierarchii rozumienia zagadnień. Niektórzy nigdy nie lubią mówić o pojęciach abstrakcyjnych. Inni je uwielbiają. Należy zrobić wszystko, aby osoby te mogły ze sobą rozmawiać. Czy to ogranicza możliwość nauczania teorii programistów-praktyków? Czy można się spodziewać, że 2 0 % spośród nich będzie zainteresowanych teorią?

Robin: Jest zrozumiałe, że programiści-praktycy nie muszą rozumieć teorii. Język jest narzędziem, a istnieją różne narzędzia. Sprawdzanie modeli to narzędzie stosowane przez ludzi po to, by uniknąć konieczności rozumienia zbyt wielu szczegółów. Nie ma w tym niczego złego, pod warunkiem że jest kilku ludzi, którzy rozumieją, jak działają szczegóły, i którzy potrafią sprawdzić, czy narzędzie sprawdzania m odelu jest adekwatne. Ogólnie rzecz biorąc, w naszej dziedzinie nauki istnieje wiele narzędzi, które służą tylko do tego, aby zwolnić pewne osoby z obowiązku rozumienia niektórych zagadnień, dlatego że mają one ważniejsze rzeczy do zrobienia. W ykonują bardziej istotne i pilniejsze rzeczy. W łaśnie do tego służą języki program owania wyższego poziomu. Pewne teorie podobają mi się głównie z tego powodu, że można z nich wyodrębnić języki programowania. Pracuję z modelem graficznym powszechnych urządzeń komputerowych. To opisowy mechanizm, który może być trudny do zrozumienia dla wielu osób, ale można z niego w yodrębnić język, który w mojej opinii będzie dość łatwy do zrozumienia. Kiedy wyodrębniamy język, używamy pewnych metafor — czasami są to specjalne metafory, innym razem są to ograniczenia strukturalne. Przejście od m odelu abstrakcyjnego do języka programowania to krok zapewniający pewien komfort — zagwarantowanie ochrony przed poznaw aniem zagadnień, którym i nie chcemy się zajmować. Na przykład systemy typów gw arantują ochronę przed pewnymi zagadnieniami, których w większości przypadków nie chcielibyśmy znać. Czyż nie na tym polega natura naszej dziedziny: pnąc się w górę stosu modeli, osiągamy coraz wyższe poziomy abstrakcji? Każda osoba jest przygotowana do podążenia w górę lub w dół o określony dystans i ani kroku dalej. Przechodząc w górę stosu modeli, niekoniecznie uzyskujemy wyższe poziomy abstrakcji. Czasami osiągamy modele bardziej restrykcyjne. Doskonałym przykładem jest model MSC (ang. Message Sequence Charts) opisujący skończone fragmenty współbieżności systemu przekazywania kom unikatów — to, co się może zdarzyć, a co nie może się zdarzyć. Mnie ten model wydaje się restrykcyjny. Można go jednak łatwo przekształcić na bardziej złożony model obsługujący modele rekurencyjne oraz wiele różnych rzeczy, o których nikt nie chce myśleć, na przykład sytuacje wyścigu. Piękno modelu MSC polega na tym, że mogą go zrozumieć osoby mniej zaawansowane technicznie. Dzięki temu idąc w górę stosu modeli, nie tylko tworzymy abstrakcje ułatwiające teoretyczne zrozumienie tem atu, ale także m ożemy wprowadzać ograniczenia, dzięki którym tem at staje się dostępny dla osób o niższym poziomie zaawansowania.

M L

265

Pod pewnymi względami model jest bardziej ogólny, ale pod innymi bardziej szczegółowy.

Robin: D okładnie tak jest. W edług mnie to rodzaj łamigłówki. M ożna umieścić bardziej szczegółowe rzeczy w niższych warstwach zamiast wyższych, choć osobiście umieściłbym je w wyższych warstwach. Najważniejsze są różnice pomiędzy warstwami. Celem kolejnych warstw jest zaprezentowanie zagadnień w sposób bardziej przystępny dla pewnej grupy osób kosztem ogólności modelu. Wydaje się, że w arto to robić. Jeśli model jest kolekcją twierdzeń zbudowanych na podstawie bardziej podstawowych zasad, w ja k i sposób wpływa to na idee, które można wyrazić z wykorzystaniem tego konkretnego modelu?

Robin: Oto pewien przykład. M am nadzieję, że nie będzie on zbyt abstrakcyjny. Dotyczy modelu, nad którym pracuję. Mamy model systemów mobilnych. Systemów, w których są przekazywane komunikaty, występują czujniki, aktuatory — wszystko to, co spotyka się w powszechnych systemach kom puterowych. M ożna stworzyć model w taki sposób, aby można było jak najwięcej powiedzieć o systemie. Można wyrazić niezmienniki tak, że nigdy nie dojdzie do stanu, w którym w tym samym pokoju będzie przebywać więcej niż 15 osób, lub inne tego rodzaju ograniczenia. Jednak w tej wersji modelu nie da się prześledzić ruchów jednej osoby i powiedzieć: „Ta osoba nigdy nie była w tym pokoju” . Ten przykład może się wydawać złożony, ale w rzeczywistości jest dosyć prosty. W tym modelu nie ma żadnych mechanizmów śledzących tożsamość pojedynczej osoby za pośrednictwem różnych zdarzeń i konfiguracji. Nie można nawet sformułować pytania: „Czy ta osoba kiedykolwiek była w tym pomieszczeniu?” . Nie możemy tak powiedzieć, ponieważ nie potrafimy powiedzieć „ta” osoba. Słowo „ta” implikuje tożsamość, która może przetrwać w czasie — zwłaszcza w połączeniu z czasownikami w określonych czasach. Jest to rodzaj modelu, w którym istnieją elementy niemożliwe nawet do wyrażenia. Taki model bardzo mnie intryguje, ponieważ wydaje mi się, że świetnie nadaje się do pew nych celów. Doskonale daje się zastosować w systemach biologicznych, gdy mówimy o populacjach milionów molekuł i nie interesuje nas to, która molekuła jest która. Skupiamy się jedynie na możliwości określenia liczby molekuł w ciągu 15 m in ut lub odpowiedzi na tym podobne pytania. Ten model może być bardzo przydatny w odniesieniu do biologii. Tam, gdzie nie m a potrzeby w yrażania tożsamości poszczególnych molekuł. Tożsamość jest mniej istotna od opisu stochastycznego?

Robin: To praw da w tym konkretnym przypadku, choć w wielu zastosow aniach nie musi tak być. Oczywiście próbowałem tu znaleźć analogię pomiędzy systemem biologicznym a pow szechnym. W tym drugim występują ludzie lub urządzenia

266

ROZDZIAŁ

DZIEWIĄTY

w pew nym kontrolow anym środowisku. W związku z tym istnieje bardziej uzasadniona potrzeba mówienia o tożsamości konkretnej osoby. On nie był w tym barze, kiedy popełniono przestępstwo. Chcemy mieć możliwość, by coś takiego powiedzieć, dlatego musimy wiedzieć, co w czasie oznacza słowo „on” . Przy modelach systemów przetwarzania powszechnego często mówi się o przestrzeni w sposób całkowicie dyskretny, nie w spominając nic o odległości. Mówi się tylko o tym, że pewne obiekty sąsiadują ze sobą lub są zagnieżdżone jeden wew nątrz drugiego. Nie m usimy modelować ciągłości przestrzeni, dlatego całkowicie o tym zapominamy. Wydaje się, że istnieje wiele elementów, bez których modele mogą się obyć dla określonych celów. Z kolei dla innych celów trzeba stosować bardziej szczegółowe modele. Przypuśćmy, że tworzę API. Im lepsze decyzje projektowe zostaną podjęte, tym bardziej ekspresywny i łatwiejszy do zrozumienia będzie model. Poza tym mogę stworzyć system, który będzie łatwiej używać prawidłowo niż nieprawidłowo.

Robin: To praw dopodobnie dotyczy dowolnej rodziny systemów. Na przykład w systemach bezpieczeństwa w określonym modelu może być trudno wyrazić pewne aspekty bezpieczeństwa, natom iast przedstawienie innych nie będzie sprawiało żadnych trudności. Model może pozwolić na wyrażanie właściwości bezpieczeństwa, ale nie pozwoli na powiedzenie czegokolwiek na temat prywatności. Takie właściwości, jak autentyczność, prywatność, zabezpieczenia i bezpieczeństwo, różnią się od siebie w subtelny sposób. Większość modeli pozwala na wyrażanie tylko niektórych spośród nich. Posłużę się jeszcze raz przykładem z biologii — nie muszę rozumieć procesu konwersji ATP zachodzącego w komórkach, abym potrafił hodować kota. Moje komórki nie wiedzą nic o kocie. Dla nich kot jest zbiorem innych komórek.

Robin: Pod tym względem nie różnim y się zbytnio od nauk naturalnych. Można zastosować prawa mechaniki Newtona o poruszających się ciałach, ale zignorować tarcie. Czy zanosi się na renesans języków możliwych do udowodnienia?

Robin: Myślę, że proces ten będzie kontynuow any tak długo, jak ludzie będą projektowali języki z jawną semantyką. Ta semantyka do niczego się nie przyda, jeśli nie będzie twierdzeń możliwych do udowodnienia w postaci „Żaden program nigdy nie zrobi niczego takiego głupiego” lub być może bardziej pozytywnych w rodzaju „Ilość pamięci w ym agana przez program y zawsze da się wyrazić wielomianem o stopniu równym rozmiarowi danych wejściowych” .

M L

2 67

Czy może to być bodziec dla osób projektujących nowe języki do przeprowadzania tego rodzaju dowodów?

Robin: Chciałbym, aby tak było. Języki ogólnego przeznaczenia są projektow ane bez żadnego dowodzenia. Chciałbym, aby to się zmieniło, ale właśnie w ten sposób są projektowane języki. Zwykle projektują je zespoły złożone z różnych ludzi. Niektóre projekty są doskonałe, choć zazwyczaj nie myśli się o dowodzeniu. Zdarzają się błędy, podobne do błędu w Pascalu dotyczącego rekordów z wariantami, w którym występuje naruszenie dyscypliny typów. Brak twierdzeń w językach jest dość powszechny. Myślę, że dokonuje się postęp, ponieważ widzimy dow ody działania języków. Są one przeprowadzane bez większego wysiłku ze względu na wydajność systemów dowodzenia. Dzięki tem u możliwy jest przepływ informacji pom iędzy ludźmi przeprowadzającymi dowody a projektantami. Dobrze by było, gdyby taki przepływ informacji miał miejsce.

Teoria znaczenia Jakie je st powiązanie pomiędzy projektem języka a projektem oprogramowania napisanego w tym języku?

Robin: To bardzo trud ne pytanie. Dawno tem u, w latach sześćdziesiątych lub siedemdziesiątych, pojawiły się nadzieje na stworzenie uniwersalnego języka komputerowego UNCOL (ang. UNiversal Computer Oriented Language). Nigdy nie udało się go stworzyć. Idea była taka, że język UNCOL będzie wykorzystywany w bardzo wielu zastosowaniach. Gdyby taki język istniał, niemożliwe byłoby stworzenie powiązania pomiędzy projektem języka a programami. Obecnie (a także przed pow staniem idei języka UNCOL) możemy obserwować progresję języków, z których prawie wszystkie faworyzowały pewne aplikacje. Język Prolog najlepiej sprawdzał się w aplikacjach, w których m ożna było wygodnie opisywać działania za pomocą formuł logicznych. Zatem projekt programu w Prologu m iał strukturę w arunków logicznych. Języki ML i Haskell mają bogatą strukturę typów, dlatego projekt programów w języku ML i Haskellu często jest blisko związany ze strukturą typów. I tak dalej. Każde zadanie można zapisać w wielu różnych językach program ow ania. Może się zdarzyć, że struktura program u w głowie program isty będzie taka sama niezależnie od języka, ale każdy język będzie się bardziej nadawał do jawnego renderowania pewnych fragmentów struktury, podczas gdy inne pozostaną niejawne. Fragmenty, które mogą być jawne, są różne dla różnych języków. Kiedy program ista staje przed określonym zadaniem, często wybiera język, który jawnie modeluje najważniejsze w jego opinii aspekty zadania. Niektóre języki pozwalają jednak osiągnąć więcej: wywierają w pływ na sposób, w jaki program ista myśli o zadaniu. Dobrym przykładem są tu języki obiektowe. Pojęcie obiektów pozwala na objaśnienie sposobu myślenia w wielu różnych aplikacjach.

268

ROZDZIAŁ

DZIEWIĄTY

Czy oprócz programowania obiektowego inne paradygmaty programowania także mają wpływ na sposób, w ja k i programista myśli oraz w ja k i projektuje aplikacje?

Robin: Tak. Myślę, że program ow anie logiczne oraz program ow anie funkcyjne wywierają taki wpływ. Uważam, że wpływ na myślenie programisty mają również paradygm aty rachunku procesów. Z pewnością oddziaływały one w przypadku języka specyfikacji Lotos, a także języka Ada — na przykład poprzez polecenia ALT. Czy zamiast wybierać język dla każdego zadania, programista powinien używać własnego języka programowania? Czy też języki skupią się w kilku rodzinach?

Robin: Gdyby każdy programista używał swojego własnego języka, byłaby to anarchia, jeśli znaczenie tego języka nie byłoby ograniczone przez zaakceptow aną teorię. W końcu jak inaczej można by zdefiniować znaczenie języka, jeżeli nie w kontekście zaakceptowanej teorii? Kiedy już istnieje teoria, program ista może wynaleźć syntaktyczne frazy, które są objaśnione przez teorię. A zatem będzie on używał własnej składni, ale o znaczeniu pochodzącym z teorii. Opisując swój język, programista jawnie odwołuje się do tej teorii. Nie ma w tym niczego złego. Ponieważ jednak język ma podstaw y teoretyczne, m ożna się spodziewać, że będzie m iał wiele wspólnego z teorią. W ja k i sposób zdefiniowałby pan ideę projektowania języka programowania? Czy jes t to narzędzie do wyrażania poglądów, czy do wyrażania celów?

Robin: W przypadku programowania funkcyjnego, a także programowania logicznego teorie już istniały. Dla program ow ania funkcyjnego była to teoria funkcji, typów i wartości, natom iast dla program ow ania logicznego dobrze ugruntow ana teoria logiki pierwszego rzędu. Teorie te istniały, zanim jeszcze pow stały języki, a języki w mniejszym bądź większym stopniu bazują na tych teoriach. Istnieją zatem przykłady teorii, które powstały przed językami. Sądzę, że potrzebujemy więcej takich teorii. Nie wiem, jak wiele różnych teorii potrzebujemy. Można powiedzieć, że cel, ja k i ktoś chce osiągnąć, ma fundamentalne znaczenie dla projektowania języka.

Robin: Może się zdarzyć, że ktoś chce wyrazić cel lub zaprezentować właściwości działania program ów w różnych językach albo przy użyciu różnych narzędzi teoretycznych. Na przykład ktoś chce napisać specyfikację i posłużyć się przy tym określoną logiką, a język programowania ma być w większym stopniu algebraicznym rodzajem języka. Jednak te dwa elementy — algebra i logika — będą ze sobą połączone, zanim jeszcze zaprojektujemy ich fragmenty w postaci języka programowania. Myślę, że narzędzie wykorzystywane do wyrażania celów oraz żądanych właściwości nie musi być takie samo jak to, z którego skorzystaliśmy, by zaprezentować program. Narzędzia te powinny jednak być połączone za pomocą pewnej teorii istniejącej być może nie tylko w celu tworzenia programów, ale także w celu zrozumienia naturalnych

M L

269

zjawisk — na przykład zjawisk biologicznych, o których wspominałem. Wydaje się, że jeśli potrafim y zrozumieć inform atykę, potrafim y również rozumieć systemy naturalne z informatycznego punktu widzenia. To właśnie robią naukowcy zajmujący się naukami naturalnymi. Być może jednak można użyć tych samych formalizmów, tych samych matematycznych konstrukcji i właściwości do zdefiniowania języków. W ten sposób tworzymy artefakty, które nie są naturalnymi zjawiskami. Zatem nie widzę pow odu, dla którego opis informatyczny systemów naturalnych powinien być inny niż inform atyczny opis języków program ow ania lub systemów oprogramowania. Przypuśćmy, że dziś znalazł pan błąd w systemie, który tworzył pan pięć lat temu. Specyfikacja jest zsynchronizowana z implementacją. Co zrobić, jeśli błąd znajduje się w projekcie języka? Co będzie, jeśli błąd doprowadzi do określonego rodzaju awarii?

Robin: Mogę z zadow oleniem powiedzieć, że to się nie zdarzyło i że nie wiem, co należałoby zrobić. Prawdopodobnie powiedzielibyśmy, że trzeba z tym żyć. Pewnie opublikowalibyśmy kom unikat następującej treści: „OK, to działa nieprawidłowo, ale jeśli zrobi się to i to, i to, nie ma powodów do obaw” . Definicje są dość wrażliwe. Mam na myśli to, że niektórzy ludzie pracują nad pomysłem, aby m echanizm y definicji stały się mniej wrażliwe i bardziej modularne. Sądzę, że to naprawdę jest dość trudne. Nie wiem, jak się to robi. Wydaje mi się, że dążyłbym do tego, by niczego nie zmieniać, lecz by powiedzieć ludziom, że istnieje problem . To po prostu praktyczny ruch, tak by nie rwać sobie włosów z głowy. Języki ML i Haskell mają bogaty system typów. Jakie idee taki system typów ujawnia w projektach programów napisanych w tych językach?

Robin: Jeśli program przejdzie przez etap kompilacji — czyli zostanie sprawdzony przez mechanizm kontroli typów — to niektóre sytuacje nie będą mogły się zdarzyć. Wiadomo, że nie wystąpią określone błędy fazy wykonywania programu. Co prawda nie ma pewności, czy nie przepełni się tablica lub nie wystąpią inne uciążliwe błędy, takie jak nieskończone pętle itp., ale pewne grupy błędów można wykluczyć. W przypadku aplikacji, z którymi my mieliśmy do czynienia — aplikacji do dowodzenia twierdzeń m atem atycznych — w spaniale było móc powiedzieć: „Jeśli uważasz, że udowodniłeś twierdzenie w języku ML i sądzisz, że Twoja reprezentacja logiki w ML została dobrze wykonana, a program w ML wygenerował dowód twierdzenia, to znaczy, że twierdzenie zostało rzeczywiście udo w odnione” . W szystko dzięki mechanizmowi abstrakcyjnych typów, który pozwala na wyrażanie typu twierdzeń jako czegoś, czym można manipulować wyłącznie za pomocą reguł wnioskowania. Niezależnie od tego, jak sprytnych sztuczek użyjemy w celu wyszukiwania możliwych reguł wnioskowania, to jeśli jedna z operacji wyszukiwania możliwej sekwencji reguł w nioskow ania się powiedzie, należy w ykonać tę regułę dla określonego typu

270

ROZDZIAŁ

DZIEWIĄTY

twierdzenia. W iadom o, że m ożna wykonać jedynie praw idłow e reguły. Może się zdarzyć, że określone operacje wyszukiwania możliwych sekwencji reguł wnioskowania nigdy się nie powiodą. Jeśli jednak się powiodą, to należy wykonać te reguły, albo zrobi to za nas system. W ramach weryfikacji słuszności implementacji oraz słuszności projektu języka wiadomo, że mamy do czynienia z twierdzeniem. W przypadku języka bez bogatego systemu typów nie ma takiej pewności. Istnieje tylko zbiór operacji.

Robin: System m ógłby zakom unikować: „Twierdzę, że jest tak ”, na co my moglibyśmy odpowiedzieć: „Skąd możemy mieć taką pewność?”. To naprawdę ważne. Pamiętam moje początkowe lata na Uniwersytecie Stanford, kiedy pracowaliśmy nad pierwszą wersją języka. W zasadzie nie był to nawet język ML. Pracowaliśmy nad zautomatyzowanym systemem wnioskowania. Sądziliśmy, że zautomatyzowaliśmy go prawidłowo. W nioski m ożna było wyciągać wyłącznie za pom ocą reguł w nioskowania. Pamiętam, kiedy pracow ałem do późna w nocy nad pewnym twierdzeniem do udowodnienia. Nie musiałem się obawiać, ponieważ ufałem typom, ufałem implementacji. Ufałem im do tego stopnia, że choć robiłem szalone rzeczy przy terminalu, nic nie mogło mieć wpływu na możliwości systemu. To napraw dę dość silna właściwość i zawsze taka była. Zwłaszcza dzięki takim systemom, jak Isabel, HOL oraz wszystkim innym współczesnym systemom. Ta cecha daje niesamowite poczucie wolności. Istnieje punkt, od którego nie trzeba się niczego obawiać. Pytanie brzmi: w ja k i sposób przekonać komputer, aby powiedział nam, co oznacza program?

Robin: Konkretny program praw dopodobnie m a następujące znaczenie: jeśli wykonasz taką czynność, zdarzy się coś takiego. Jeśli wykonasz inną czynność, zdarzy się coś innego. Typy pozwalają na formułowanie stanowczych twierdzeń tego rodzaju. W łaśnie w tym przypadku pom aga nam kom puter. K om pilator pom aga nam za pośrednictwem mechanizmu kontroli typów. Oczywiście nie musi to być rozstrzygalny mechanizm kontroli typów. Może to być taki mechanizm, w którym — w przypadku gdy wyciągnie wniosek, że w programie są prawidłowo stosowane typy — tak istotnie będzie. W odniesieniu do niektórych program ów wyciągnięcie takiego wniosku okazuje się jednak niemożliwe. Potrzeba bogatego systemu typów, który jest nierozstrzygalny, ale posiada cechę, którą można nazwać pozytywnym bezpieczeństwem: jeśli pojawi się twierdzenie, to jest ono prawdziwe. Jakiej rady udzieliłby pan komuś, kto chciałby zostać lepszym projektantem oprogramowania?

Robin: Poradziłbym, aby podjął decyzję, czy chce zarabiać pieniądze, czy też zajmować się nauką. Nie można nakazać nikom u tego, którą drogę powinien wybrać. Istnieje

M L

271

wiele sposobów zarabiania pieniędzy bez zajmowania się nauką. Istnieje również wiele sposobów zajmowania się nauką. Gdybym radził coś komuś, kto chce zajmować się nauką, powiedziałbym mu: „Porozmawiaj z ludźmi zajmującymi się projektami; nie siedź w hermetycznym pokoju przy projekcie teorii, która świetnie wygląda, ale zadbaj o to, aby miała jakieś praktyczne znaczenie” . Opisał pan problem błędu milenijnego jak o dobry przykład sytuacji, w której nie wiadomo, z jakiego rodzaju problemami przyjdzie nam się zmierzyć. W ja k i sposób można zapobiegać podobnym problemom strukturalnym w fazie projektowania?

Robin: Nie wiem. Rynek jest tak głodny produktów program ow ych, że jeśli poświęcimy czas na analizę tego, co chcemy sprzedawać, kontrakt weźmie już ktoś inny. Brzmi to bardzo cynicznie, ale myślę, że taka właśnie jest prawda. Ten, kto chce zaistnieć na rynku, nie odniesie sukcesu, jeżeli będzie korzystać z narzędzi analitycznych, choćby takie istniały. Oczywiście bardzo często okazuje się, że takie narzędzia jeszcze nie istnieją. W przypadku błędu milenijnego dysponowaliśm y teorią potrzebną do tego, by całkowicie uniknąć problem u. Stałoby się tak, gdyby program y były pisane w odpowiedni sposób. Trzeba było jedynie zadbać o wykorzystanie teorii typów, która była w użyciu od około 20 lat. Oczywiście istnieje wiele przypuszczeń co do tego, dlaczego tę teorię zignorowano. Myślę jednak, że głównym powodem były naciski rynku. Może pomogłaby dokumentacja? W ja k i sposób twórcy oprogramowania powinni pisać dokumentację?

Robin: Z całą pewnością powinni pisać komentarze w kodzie, ale powinny również istnieć pewne ścisłe reguły. Sądzę, że trudność w stworzeniu odpowiednich komentarzy zwiększa się nieliniowo wraz ze wzrostem rozmiaru programów. M ilion wierszy to zdecydowanie zbyt dużo dla programu. Odpowiedni rozm iar program u pow inien wynosić co najwyżej kilka tysięcy wierszy. Przy dużych programach wszystko bardzo się komplikuje. Złożoność interakcji pomiędzy różnymi częściami programu rośnie bardziej niż liniowo. Stąd potrzeba ścisłej specyfikacji praktycznych programów jest bardzo duża. Nawiasem mówiąc, kompletną formalną specyfikację języka ML napisaliśmy w postaci reguł wnioskowania. Stworzyliśmy formalną definicję języka. Nie zapisaliśmy, jaki pow inien być związek im plem entacji ze specyfikacją form alną. Ponieważ jednak mieliśmy specyfikację form alną, wiedzieliśmy, co próbujem y implementować. Po pierwsze, mieliśmy bardzo czytelną specyfikację, a po drugie, nie mieliśmy zamiaru jej zmieniać, a jeśli już, to bardzo powoli.

272

ROZDZIAŁ

DZIEWIĄTY

W spółczesne program y muszą zapewniać możliwość modyfikacji lub usuw ania fragm entów, adaptow ania fragm entów , w prow adzania innych fragmentów. Dzieje się tak dlatego, że zmienia się specyfikacja. Zatem w przypadku programów do praktycznych zastosowań istnieje dodatkowy powód, by zachować ostrożność podczas określania relacji pomiędzy specyfikacją a implementacją. Wiadomo bowiem, że specyfikacja będzie się zmieniać. W związku z tym trzeba wiedzieć, jaki to będzie miało wpływ na zmianę implementacji. Czy zna pan jakąś interesującą anegdotę związaną z tworzeniem języka ML?

Robin: Ciekawe jest to, że poświęciliśmy znacznie więcej czasu na dyskusje na temat składni niż na temat semantyki. W dużym stopniu zgadzaliśmy się co do funkcjonalnego rozumienia języka, ale bardzo często, kiedy w grę wchodziły kwestie gustu — na przykład jakie słowo należy zastosować w składni — dyskutowaliśmy w nieskończoność. Nie mieliśmy bowiem podstaw naukowych, na których można by oprzeć decyzję. Inna historia: opracowaliśmy dla języka ML bardzo ściśle przemyślany system typów, który był umiarkowanie rozbudowany w porównaniu z niektórymi teoriami typów będącymi w użyciu. Wykorzystywaliśmy język ML do przeprowadzania formalnych dow odów z w ykorzystaniem logiki m atem atycznej. Jedną z czynności, które musieliśmy wykonać, było zadbanie o wydajną implementację tzw. reguł uproszczeń. Przekształcanie skom plikow anego wyrażenia na wyrażenie w określonej formie, czasami nazywanej postacią normalną lub postacią kanoniczną, wymaga zastosowania wielu reguł. Aby szybko przeprowadzić transform acje, reguły te należy zaim plem entow ać w sprytny sposób. Trzeba zadbać o możliwość przeglądania dostępnych reguł i dopasowywać je w pewnym sensie równocześnie. Implementowaliśmy ten mechanizm równoczesnego dopasowywania w języku ML i odkryliśmy, że coś działało niezbyt wydajnie. Okazało się, że przyczyną były pewne ograniczenia naszego systemu typów. Dla tego fragm entu im plem entacji — implementacji narzędzia analitycznego do dowodzenia twierdzeń — zdecydowaliśmy się wstrzymać stosowanie reguł języka ML. Następnie opracowaliśmy je w sposób bardziej wydajny. W rzeczywistości okazało się to nie takie złe, ponieważ bogatszy system typów, który wykorzystaliśmy, był dość dobrze zrozumiały. Ale i bardziej złożony. Chcieliśmy, aby język ML miał prosty system typów, a potrzebowaliśmy nieco bogatszego, ale nieco bardziej złożonego systemu po to, by pewne operacje mogły być wykonywane wydajniej. Gdyby miał pan zacząć od początku i zaprojektować język ML dziś, to czy z powodu postępów w technice obliczeniowej oraz zmian w pańskim spojrzeniu na pewne zagadnienia projekt zmieniłby się znacznie, czy też pozostałby w większości taki sam?

Robin: Język został opracowany jako narzędzie dowodzenia twierdzeń. Okazało się, że dowodzenie twierdzeń było tak wymagającym zadaniem, że ML przekształcił się

M L

2 73

w język ogólnego przeznaczenia. W związku z tym rodzi się pytanie, do czego zaprojektow ałbym język ML dziś. Gdybym ponow nie projektow ał go w celu dowodzenia twierdzeń, problemy byłyby takie same. Potrzebne byłoby narzędzie pozwalające na zmianę stanu. Nie chcielibyśmy w pełni funkcjonalnego języka, poniew aż stan m iałby się zmieniać często. Chcielibyśmy zarządzać drzewem wnioskowania lub inną rozrastającą się strukturą albo drzewem celów oraz celów pomocniczych. To należałoby zmienić. Przy przejściu z tamtego świata do obsługi znacznie bardziej jawnie dynamicznych systemów, takich jak powszechna technika obliczeniowa, czułbym się zagubiony, gdybym używał języka funkcyjnego. Gdybym projektował język do dowodzenia twierdzeń, to może mechanizmy Haskella — monady — służące do obsługi sekwencji byłyby lepszym pomysłem, ale nie m am takiej pewności. M usiałbym dokładnie wiedzieć, w jakim celu projektuję język. Zastanawiam się, jak m ożna projektować języki bez wybrania jakiejś preferowanej dziedziny zastosowań, pewnego rodzaju specyficznych operacji, które mają być łatwo wykonywane. Projektanci Javy praw dopodobnie dokładnie zdawali sobie sprawę, do czego ma służyć ten język, i dlatego jest on taki dobry. Jednak przestrzeń możliwych dziedzin zastosow ań jest dziś dość szeroka. Dlatego właśnie powstaje m nóstw o różnych języków przeznaczonych do różnych celów. Cel jest brakującym parametrem. Gdybym projektował język do tego samego celu, to istnieje duże prawdopodobieństwo, że opracowałbym język bardzo podobny. Przyglądając się pańskiej pracy, odnosiłem wrażenie, że stosuje pan następujące podejście: „W obrębie tego problemu chcę stworzyć zbiór prymitywów możliwych do wielokrotnego zastosowania — moich twierdzeń — a następnie będę tworzył inne twierdzenia na ich podstawie".

Robin: Myślę, że można używać języka ML, mimo że nie ma się w głowie zbyt wielu twierdzeń, ale być może mówi pan bardziej o projektantach niż o użytkownikach. Rzeczywiście, kiedy projektowaliśmy język, stosowaliśmy różne struktury operacyjne i tworzyliśmy semantykę, pam iętaliśm y o tym, że chcemy udow odnić określone twierdzenia na temat całego języka — na przykład że nie będzie referencji wiszących w powietrzu. Chcieliśmy, aby kilka własności było prawdziwych. Później rzeczywiście zostały one udow odnione za pom ocą autom atycznych lub półautom atycznych systemów dowodzenia. Przyjąłem to z ulgą! Mieliśmy nieform alne przeczucie, że nie będzie żadnych „wiszących” referencji, ale dobrze jest dysponować formalnym dowodem tego faktu. W ten sposób ma się pewność, że nie został popełniony jakiś głupi błąd. Z drugiej strony mieliśmy kłopoty z typam i referencyjnymi — typam i zm iennych, do których m ożna przypisywać w artości. Pewna grupa osób pokazała, że występują z tym problem y w systemie typów. Gdybyśmy zastosowali pewne ograniczenia na język, ten kłopot by nie wystąpił. Po badaniach okazało się, że ograniczenie dotyczyłoby tylko 3% programów. Gdyby

274

ROZDZIAŁ

DZIEWIĄTY

zadowoliło nas pozostałe 97%, to moglibyśmy uniknąć kłopotów. Zmodyfikowaliśmy język w celu w prow adzenia ograniczenia. W ten sposób pow stała wersja ’97 w odróżnieniu od wersji ’90, kiedy powstała pierwsza wersja semantyki. Czy w przypadku wprowadzania poprawek w języku formalna modyfikacja wersji jest jedynym sposobem na zapewnienie synchronizacji implementacji ze specyfikacją?

Robin: Myślę, że dbaliśmy o to, by były one zsynchronizowane. Byliśmy w stanie udowodnić istnienie zgodności w górę. Inaczej mówiąc, stare implementacje działały, 0 ile im plem entow ały program y w nieco ograniczonej postaci. Zgodność w górę była prawdziwym problemem podczas modyfikowania wersji. Właściwie po cichu byłem przeciwny modyfikowaniu języka, ale poprawienie problemu 1 w ykonanie operacji w prostszy sposób — tak jak sugerowały osoby zgłaszające problem — było kuszące. Przy okazji zmiany wersji zmodyfikowaliśmy także inne elementy. Opracowanie nowej wersji wymagało od nas więcej wysiłku, niż byliśmy gotowi ponieść. Niewiele brakowało, byśmy zrezygnowali z jej tworzenia. Ale ostatecznie jestem zadowolony, że opracowaliśmy nową wersję — udało się bowiem dowiedzieć czegoś cennego o systemie typów oraz uprościć realizację pewnego mechanizmu.

Wykraczając poza informatykę Jakie są dziś najważniejsze problemy w informatyce?

Robin: O statnio pracuję nad pojęciem struktury modeli. Jeśli ktoś pracuje w wysokopoziomowym języku programowania, to posługuje się pojęciami z języków niższych poziomów. Te z kolei są wyrażone w kodzie asemblera, który jest bardziej niskopoziomowy. Natomiast sposób działania kodu asemblera jest wyrażony za pomocą układów logicznych znajdujących się pod spodem. Nie jest to już zatem m odel programowy, ale model obiektów elektronicznych. Ten model jest z kolei wyjaśnieniem zjawiska, jakim jest komputer — urządzenia, które uruchamia nasz program znajdujący się o cztery poziomy wyżej w hierarchii modeli. Nie jest to koniec tej historii, ponieważ m ożna by pójść wyżej — przejść od języka program ow ania do języka specyfikacji będącego w pewnym sensie modelem wyższego rzędu. Razem mamy już więc pięć poziomów. Pomyślmy o wszechobecnych urządzeniach komputerowych — systemach, które będą pozwalały na zarządzanie domowymi zakupami, zapełnią nam lodówkę lub będą m onitorow ały nasze zdrowie, jeśli się do nich podłączymy lub ktoś wszczepi nam odpow iedni układ. Aby zrozumieć takie systemy, potrzeba wielu poziom ów modelowania. Ludzie mówią bowiem o agentach programowych jako urządzeniach, które ze sobą negocjują, żądają wzajemnie zasobów, ufają sobie oraz odzwierciedlają własne zachowanie. Innymi słowy, wykazują wiele cech ludzkich. Pewne działania tych systemów powinny być wyrażone za pomocą logiki bardzo wysokiego poziomu

M L

275

— z wykorzystaniem zaufania, wiedzy, wiary itp. Potrzebna jest zatem teoria takiej logiki, która wyjaśniałaby, w jaki sposób określać programy za pomocą prostszych działań. Będą to zwykłe specyfikacje — opisujące operacyjne zachowania programu. Jednak na wyższych poziomach będziemy zadawać sobie takie pytania: „Czy to prawda, że ten program ufa innemu programowi?” a lb o ,Jak zaimplementować pojęcie zaufania pom iędzy kom puterow ym i agentam i?” , albo „Jak spowodować, aby jeden agent zrozumiał, co inny agent chce zrobić, lub by zauważył, że inny agent jest zagrożeniem dla jego własnych aspiracji?” . Takie pytania można stawiać jakieś trzy poziomy wyżej w porównaniu z normalnym poziomem specyfikacji, jakiego używamy dla standardowych programów. Niezależnie od tego, jakie są modele, modelują one oprogramowanie, albo modelują inne modele objaśniające oprogramowanie na pewnym niższym poziomie. Poza tym, w przypadku tworzenia takiego urządzenia jak samolot Airbus, łączymy model oprogramowania z elektrom echanicznym modelem sposobu działania samolotu, a być może naw et z modelem warunków atmosferycznych, jakie mają występować podczas lotu. A zatem podejm owaliśm y wysiłki w celu łączenia modeli, czasami pochodzących z nauk przyrodniczych — na przykład modeli meteorologicznych lub elektromechanicznych modeli inżynieryjnych z modelami oprogramowania. Na poziomie tak połączonych modeli istnieje możliwość prognozowania sposobu, w jaki będzie działał Airbus. Podoba mi się pomysł łączenia modeli naturalnych ze sztucznymi, przy czym to samo pojęcie modelowania jest stosowane w odniesieniu do obu. Różnica polega na tym, że w przypadku dziedzin sztucznych model poprzedza produkt, natomiast w przypadku nauk naturalnych najpierw występuje zjawisko, a modelowanie jest przeprowadzane później. To swego rodzaju integralność pomiędzy informatyką a naukami naturalnymi. W przypadku projektowania sprzętu można go fizycznie testować. W przypadku oprogramowania może nastąpić niepowodzenie na etapie implementacji, jeszcze zanim pojaw ią się użytkownicy. W ja k i sposób połączyć te różne etapy projektowania, implementacji i praktycznego wykorzystywania?

Robin: Jeśli przyjrzymy się modelom , które stw orzono w naukach naturalnych, zauważymy, że są one weryfikowane tylko na podstawie obserwacji tego, czy świat fizyczny zachowuje się w sposób przewidziany przez stworzone modele. Oznacza to, że nigdy nie m ożna w pełni zweryfikować modeli. M ożna jedynie im zaprzeczyć poprzez odkrycie, że pewne obserwacje przeczą temu, co przewidywano. Nie da się zaobserwować, że wszystkie zjawiska zachowują się tak, jak przewidziano, ponieważ jest to nieskończona praca. Jeśli implementujemy język program ow ania wyższego poziomu z wykorzystaniem pojęć języka niższego poziomu, jesteśmy w lepszej sytuacji. Dysponujemy opisem formalnym wszystkich pojęć języka niższego poziomu, dlatego możemy zweryfikować implementację, która jest tłumaczeniem programów wyższego poziomu na programy

276

ROZDZIAŁ

DZIEWIĄTY

niższego poziomu. Wystarczy zaobserwować, czy teoretyczne wyjaśnienie działania program u na wyższym poziomie jest spójne z wyjaśnieniem na niższym poziomie. Mamy zatem szansę weryfikacji sposobu postrzegania jednego modelu przesz inny, znajdujący się na niższym poziomie. Takich matematycznych dowodów nie możemy przeprowadzić tylko w przypadku próby implementacji programów niskiego poziomu jako fizycznych zjawisk. Jednak na każdym wyższym poziomie mamy taką możliwość, pod warunkiem dobrego zaprezentowania modeli oraz pod warunkiem że znaczenie obiektów na każdym poziomie jest częścią m odelu na tym poziomie. Na każdym poziomie występują pojęcia oraz objaśnienia sposobu ich zachowywania się. Jest to medium, poprzez które spodziewamy się walidacji implementacji modelu wyższego poziomu za pomocą implementacji niższego poziomu. To jest sposób myślenia, do jakiego starałem się przekonywać. Na przykład jeden z m oich ostatnich referatów nosił tytuł „Powszechne urządzenia kom puterow e: czy powinniśmy je rozumieć?”. Mówiąc o „zrozumieniu”, miałem na myśli możliwość specyfikacji zachowania jednego z takich systemów. Na przykład jest urządzenie, które monitoruje działanie naszego organizmu, a my chcemy zrozumieć, w jaki sposób działanie tego systemu zostało zaimplementowane przez agenty. Nie sądzę, że przekonanie ludzi do takiego sposobu myślenia jest łatwe. Zwykle słyszy się, że jest to niemożliwe. Systemy są bowiem często tak rozbudowane, że trudno to zrobić. Czytałem kiedyś pewien raport, w którym stwierdzono, że nikt nie będzie w stanie przeanalizować działania powszechnych systemów komputerowych. Wydaje mi się, że ktoś, kto to powiedział, zupełnie nie m iał racji. Tylko od nas zależy, czy zaprojektujemy systemy możliwe do przeanalizowania, czy nie. W związku z tym powinniśmy tworzyć takie systemy, aby ich analiza była możliwa. Jakie powiązania widzi pan pomiędzy inżynierią a informatyką?

Robin: Inżynieria bardzo często bazuje na naukach naturalnych, które istniały znacznie wcześniej. Wiele produktów inżynierii chemicznej pow stało dlatego, że teorie chemiczne zostały zaprojektowane i przetestowane w praktyce. A zatem inżynieria chemiczna powstała jako efekt nauki chemii zajmującej się obserwacją naturalnych zjawisk. Inżynieria może się rozwijać na bazie zrozumienia zjawisk zachodzących w naturze. To samo można powiedzieć o fizyce, ale w przypadku oprogramowania jest inaczej. W naturze nie ma czegoś takiego jak oprogramowanie. Próby interpretacji procesów zachodzących na przykład w naszych umysłach są moim zdaniem naciągane. Ponieważ zatem oprogramowanie nie występuje w naturze, nie powstała nauka, która mogłaby stać się podstawą inżynierii programowania. Uważam więc, że pomiędzy inżynierią a oprogram ow aniem występuje właściwie bardziej kontrast niż łącze. Inżynieria oprogram ow ania nie bazuje na żadnej dobrze ugruntow anej nauce, podczas gdy w innych dziedzinach inżynierii takie nauki istnieją.

M L

2 77

Jaka jest rola matematyki w informatyce?

Robin: Wykorzystywane są różne fragmenty matematyki. Używa się logiki, algebry, teorii prawdopodobieństwa. W systemach hybrydowych, w których łączy się zjawiska ciągłe z dyskretnymi działaniami, używamy różnych rachunków. Różne dziedziny matematyki spełniają zatem różne role. Nie zawsze jest jednak jasne, w jaki sposób wybrać odpow iednią. Czy wybiera się ją ze względu na upodobanie do teorii prawdopodobieństwa, czy statystyki? A może wybieramy ją dlatego, że wymyśliliśmy rodzaj systemu kom puterow ego lub systemu inform atycznego i m am y nadzieję, że wybrana dziedzina matematyki pozwoli nam go opisać. Matematyk może wybrać sobie tę gałąź matematyki, którą chce badać. My musimy patrzeć na systemy występujące w realnym świecie — czy są naturalne, czy sztuczne. Następnie powinniśmy sobie zadać pytanie, czego potrzebujemy, by te systemy opisać. O statnio m usiałem zrozumieć analizę stochastyczną — naukę zajmującą się praw dopodobieństw em trw ania, upływem czasu itp. W ydaje się ona absolutnie konieczna, jeśli mamy zamiar użyć któregoś z naszych modeli do wyjaśniania zjawisk biologicznych. Wysłano mnie na szkolenie z nieznanej mi wcześniej dziedziny, abym zapoznał się z tą teorią. Przy okazji uczyłem się pewnych bardziej abstrakcyjnych dziedzin matematyki, takich jak algebra, teoria kategorii itp. Zazwyczaj wydaje się, że potrzebujemy tylko pewnych fragmentów teorii. W związku z tym nie wymyślamy bardzo zaawansowanych klasycznych teorii podobnych do tych, które opracowują matematycy. Zamiast tego bierzemy fragmenty z różnych miejsc. Fragmenty te mogą już być dobrze znane lub nieco mniej znane z uwagi na to, że nie są zbyt ciekawe. Ostatecznie stajemy się twórcami klasycznych teorii, mim o że naszym celem jest wyjaśnienie realnego zjawiska. Czy określa pan siebie mianem naukowca komputerowego, czy raczej badacza?

Robin: Nie lubię określenia „naukowiec komputerowy”, ponieważ jest w nim położony zbyt duży nacisk na słowo komputer. Myślę, że komputer to tylko egzemplarz działania inform atyki, zatem pow iedziałbym raczej „inform atyk-naukow iec” . Oczywiście wszystko zależy od tego, co rozumiemy pod pojęciem „informatyka” . Myślę, że słowo to oznacza połączenie obliczeń z kom unikacją, przy czym kom unikacja odgrywa bardzo ważną rolę. Jaka jest pańska rola jako naukowca-informatyka?

Robin: Myślę, że moja rola polega na próbie stworzenia pojęciowego frameworku, wewnątrz którego można przeprowadzać analizy. Aby to zrobić, trzeba uwzględnić to, co rzeczywiście dzieje się w oprogramowaniu, na przykład na czym polega działanie systemów powszechnych. Trzeba jednak w pewien sposób od tego abstrahować. To naprawdę trudne: popełnia się błędy, wynajduje nieprawidłowe koncepcje, które

278

ROZDZIAŁ

DZIEWIĄTY

nie działają tak, jak powinny, lub się nie skalują. Szukamy pojęć podstawowych, które można skalować. Dzięki temu da się je wykorzystać do wyjaśnienia istniejących bądź planowanych dużych systemów oprogramowania. Myślę, że pojęcie komunikacji pomiędzy agentami jest bardzo ważne. Jest to bowiem jedno z podstawowych pojęć do wyizolowania z teorii obliczeniowej, których logicy do tej pory jeszcze nie studiowali. Idea populacji interaktyw nych agentów: jaka pow inna być ich struktura? W jaki sposób agenty pow inny być ze sobą połączone? Które powinny się komunikować z którymi? Czy agenty mogą tworzyć nowe agenty? Czy mogą dostosowywać swoje działania zgodnie z żądaniami lub działaniami swoich sąsiadów? Napotykamy wiele pytań. Można je zadać tylko wtedy, gdy mamy populację agentów, a nie — jak w początkach program owania — tylko pojedynczy program zdolny do wykonywania tylko jednego zadania. Czy internet może nam pomóc w znalezieniu rozwiązań?

Robin: Myślę, że internet może stwarzać problemy. Zrozumienie go jest problemem samym w sobie. Może być pomocny, jeśli stworzymy pojęciowe narzędzia, które będzie m ożna wykorzystać do analizy działania internetu. Te narzędzia mogą być nam potrzebne do zrozumienia swego rodzaju populacji agentów, podobnych do agentów monitorujących ludzki organizm lub sterujących ruchem na autostradzie. Wiele zjawisk zachodzących w internecie zostało doskonale zaprojektow anych. To dobry przykład do studiow ania, poniew aż doskonale działa w praktyce. Myślę więc, że jest on w części rozwiązaniem, a w części problemem. W ja k i sposób postrzega pan dziś badania prowadzone w informatyce?

Robin: Widzę, że są one bardzo rozpow szechnione. Ludzie, którzy budują duże systemy, nie stosują ścisłych m etod ich specyfikowania. Poza tym ludzie pracujący na niższych poziom ach lub na bardziej form alnych poziom ach są czasami zajęci czystą m atem atyką i nie chcą sięgać do realiów świata. M amy więc do czynienia z rozwidleniem społeczności. Istnieje spektrum wielu różnych społeczności pomiędzy aplikacjami a teoriami. Na tej dość długiej ścieżce można spotkać ludzi, którzy mają trudności ze zrozumieniem osób znajdujących się z ich lewej lub prawej strony. Uważam, że pomiędzy tymi społecznościami nie ma zbyt dobrej łączności. W Wielkiej Brytanii rozpoczęliśmy duży program pod hasłem „Przetwarzanie bez granic: doświadczenia, projektowanie i nauka” . Przez doświadczenia rozumiemy eksperymenty z instrum entam i przeprow adzane w różnego rodzaju środowiskach. Może to być umieszczenie w każdym pokoju budynku kom puterów , które rozpoznają wejście osób do pokoju lub informują o ich ruchach. A zatem pewna grupa ludzi eksperymentuje z alternatywnymi projektami. W tle działa inna grupa ludzi, którzy implementują te projekty w sposób zgodny z dobrym i zasadami projektow ania. Są też naukowcy,

M L

279

którzy zajmują się abstrakcyjnymi modelami pracy inżynieryjnej. Z kolei inżynierowie używają narzędzi stworzonych przez naukowców do przeprowadzania eksperymentów w realnym świecie. Te trzy poziomy — doświadczenia, projekty i nauka — są próbą wypełnienia luki, o której mówiłem wcześniej. Zacząłem rozmawiać z ludźmi, z którymi norm alnie bym nie rozmawiał — ludźmi zajmującymi się oddziaływ aniem na społeczeństwo pow szechnych systemów komputerowych. Uważają oni je nie za systemy do używania przez ludzi, ale za systemy, w których ludzie są komponentami. Mamy konkretne poziomy rozumienia systemów i musimy przebijać się przez zasady inżynieryjne do pojęć, które mogą być wykorzystane jako podstawa do analizy całości. Czy dostrzega pan różnicę pomiędzy dzisiejszym sposobem prowadzenia badań a sposobem ich prowadzenia w latach sześćdziesiątych i siedemdziesiątych?

Robin: Obecnie jest znacznie większe zainteresowanie powszechnymi systemami kom puterow ym i. Będą one coraz częściej wykorzystywane w naszym otoczeniu. To bardzo duża różnica. Weźmy pod uwagę w szczególności systemy czasu rzeczywistego działające w pojazdach lub innych ważnych urządzeniach. Walidacja oprogram ow ania działającego w czasie rzeczywistym wymaga specjalnej analizy. Dawniej znacznie mniejszym problemem były relacje pomiędzy obliczeniami a czasem rzeczywistym. Oczywiście dzisiejsi inżynierowie budujący Airbusa lub dow olny inny system przetwarzania powszechnego o wiele bardziej przejmują się tym, co się dzieje w czasie rzeczywistym. Pod tym względem ich badania przypom inają badanie procesów fizycznych, kiedy wiadomo, jak długo będą trwały określone zjawiska. Czy technologie przetwarzania bez granic spowodują przełom w dziedzinie sztucznej inteligencji?

Robin: Tak, ale sądzę, że do sztucznej inteligencji należy podchodzić ostrożnie. Nigdy nie byłem zadowolony z akcentów, jakie kładziono na sztuczną inteligencję w latach sześćdziesiątych lub siedemdziesiątych. Myślę, że niektóre oczekiwania były przesadzone. Od kiedy zaczęliśmy używać takich słów, jak „przekonanie” , „w iedza” itp., do zrozumienia populacji agentów, zaczęliśmy uważać sztuczną inteligencję nie za coś, co istnieje bądź nie istnieje, ale za coś, co stopniowo się rozwija. Systemy stają się coraz bardziej inteligentne. Stają się też coraz bardziej odblaskowe, co oznacza, że mogą inform ow ać o w łasnym działaniu oraz je analizować. W związku z tym stopniowo coraz więcej pojęć uznawanych za elementy sztucznej inteligencji będzie się pojaw iało w m ałych ilościach. Będzie ich coraz więcej w miarę pow staw ania coraz większej liczby tych systemów.

280

ROZDZIAŁ

DZIEWIĄTY

W obec tego nie jestem pewien, czy wszystkie prace, jakie w ykonano w dziedzinie sztucznej inteligencji, będą pomocne. Myślę, że w miarę tworzenia większych systemów do opisywania zdarzeń będziemy coraz częściej używali sform ułow ań typowych dla ludzi, na przykład terminu „przekonanie” . Wtedy różnice pomiędzy podmiotami inteligentnymi a nieinteligentnymi rozmyją się, ponieważ będą istniały różne poziomy inteligencji. Jakie wnioski pana zdaniem płyną z badań, które nie są stosowane w praktyce?

Robin: Większość języków programowania opracowano bez wcześniejszego tworzenia teorii, na której język mógłby bazować. W związku z tym często język jest projektowany i implementowany, a później trudno przewidzieć, co oznacza program i co powinno się zdarzyć, kiedy program zacznie działać. Oczywiście istnieją przypadki języków, które były doskonale przewidywalne. Na przykład opracowany w 1960 roku raport języka Algol 60 był tak dokładny, że można było z dużą precyzją powiedzieć, co zdarzy się w kodzie. Nie zawsze tak jest. Nawet w przypadku dobrych języków zdarza się, że formalne podstawy nie istnieją przed pojawieniem się języka. W takich przypadkach często później tworzy się teorię, która ma znaczenie dla języka. Może to oznaczać, że projekt nie może skorzystać z podstaw teoretycznych. Późniejsze analizy są również wykonywane dla dużych systemów oprogramowania. W Wielkiej Brytanii można wskazać wiele przykładów takich systemów. Powodują one wielkie opóźnienia, a czasami doprowadzają do katastrofy. Dla dużych systemów byłoby znacznie lepiej, gdyby wcześniej istniała ścisła specyfikacja i pewnego rodzaju analiza naukowa. Co pan ma na myśli, mówiąc, że język posiada teorię znaczenia?

Robin: To teoria mówiąca o tym, co będzie realizowała implementacja. Język ML ma teorię znaczenia, ponieważ mogę udowodnić na podstawie semantyki jego operacji, że w programie nie będzie referencji wiszących w powietrzu. Ostatnio zaprezentowano wiele faktów dotyczących semantyki języka C i stworzono teorię znaczenia dla języka C. Jeśli dysponuje się semantyką języka C, można udowodnić pewne twierdzenia na temat wszystkich możliwych program ów w C korzystających z tej semantyki. Ostatnio odniesiono w tej dziedzinie wiele sukcesów. Myślę, że sprawy idą w dobrym kierunku. Za język programowania uważa pan specyfikację języka wraz z jego projektem. Czy może to zapobiec pewnym błędom popełnianym przez użytkowników języka?

Robin: Tak, to oznacza, że błędy użytkowników można sprawdzać ze specyfikacją. W ten sposób m ożna znaleźć niezgodności, zanim program zostanie uruchom iony lub użyty w praktyce. Obecnie pracuję nad teorią behaw ioralną populacji agentów kom unikujących się ze sobą. Mogę opisać, w jaki sposób populacja złożona z ludzi i maszyn funkcjonuje w pewnym wspólnym środowisku, w jaki sposób ludzie komunikują się z maszynami

M L

281

i na odwrót. Mam nadzieję, że tę samą teorię m ożna wykorzystać do zrozumienia systemów biologicznych — na przykład w jaki sposób komórka może stworzyć nową komórkę przez pączkowanie. Istnieje możliwość stworzenia ogólnej nauki informatyki, która nie zależy od określonego zastosowania. Przed przystąpieniem do tw orzenia języka program ow ania trzeba dysponować teorią, która zarządza projektem języka programowania. Chcę stworzyć taką teorię, zanim język uzyska ustaloną formę.

282

ROZDZIAŁ

DZIEWIĄTY

ROZDZIAŁ

DZIESIĄTY

SQL

W jaki sposób zapewnić wydajny sposób zbierania, wydobywania i aktualizowania informacji w dużej kolekcji danych posiadających zdefiniowaną strukturę, jeżeli nie wiadomo, jakich operacji użytkownicy będą potrzebowali? Jest to fundamentalna idea, która dała podstawy modelu relacyjnego, opracowanego przez Edgara F. (Teda) Codda. Język SQL jest najbardziej widoczną implementacją modelu relacyjnego — językiem deklaracyjnym, w którym opisujemy to, czego chcemy, a nie to, w jaki sposób należy to osiągnąć. Wykorzystując idee Codda, Donald Chamberlin i Raymond Boyce stworzyli język SQL.

2 83

Ważny dokument W ja k i sposób doszło do zaprojektowania języka SQL?

D on Chamberlin: W początkach lat siedemdziesiątych dopiero zaczynano wdrażać na powszechną skalę zintegrowane systemy baz danych. Dzięki postępom w technice i ekonomii po raz pierwszy powstała możliwość przeglądania danych biznesowych w postaci zasobów korporacyjnych, które można było współdzielić pomiędzy wieloma aplikacjami. Ta now a perspektyw a dla danych stworzyła okazję do opracow ania technologii zarządzania danymi nowej generacji. W latach siedemdziesiątych głównym produktem bazodanow ym był system IMS, ale oprócz grupy pracującej nad rozwojem IMS nad problem em baz danych pracow ało kilka grup badawczych w różnych ośrodkach firmy IBM. Dr Edgar F. (Ted) Codd był liderem jednej z takich grup w IB Research Laboratory w San Jose w Stanie Kalifornia. Ray Boyce i ja należeliśmy do innej małej grupy badawczej w IBM-owskim W atson Research Center w Yorktown Heights w stanie Nowy Jork. Ray i ja studiowaliśm y języki zapytań do baz danych. Próbowaliśmy znaleźć sposoby uspraw nienia języków będących wówczas w pow szechnym użyciu. W czerwcu 1970 roku Ted Codd opublikow ał dokum ent1, w którym przedstawiał relacyjny model danych i opisywał jego zalety dla niezależności danych oraz tworzenia aplikacji. Artykuł Codda spotkał się z dużym zainteresowaniem — zarówno wewnątrz, jak i na zewnątrz firmy IBM. W 1972 roku Ray Boyce i ja braliśmy udział w sympozjum dotyczącym relacyjnego modelu danych. Zostało ono zorganizowane przez Codda w W atson Research Center. Sympozjum to wywarło bardzo silny wpływ na mnie i na Raya. Byliśmy pod wrażeniem elegancji i prostoty przechowywania danych w postaci relacyjnej. Mogliśmy zobaczyć, jak łatwo można było wyrazić w relacyjnej formie wiele typów zapytań. Po sympozjum Ray i ja zaangażowaliśm y się w grę zapytań, w której postawiliśmy sobie za cel zaprojektowanie języków na tyle elastycznych, by można było za ich pomocą wyrażać wiele rodzajów zapytań. W 1973 roku pom ysły Codda zyskały takie uznanie, że firma IBM zdecydowała skonsolidować swoje wysiłki badawcze w dziedzinie baz danych w instytucie Codda w San Jose. Celem było stworzenie prototypu na skalę przemysłową o nazwie System R, który miał być dowodem słuszności koncepcji relacyjnych baz danych. Ray Boyce i ja oraz kilku innych badaczy IBM z Yorktown i Cambridge przenieśliśmy się do Kalifornii w celu dołączenia do zespołu pracującego nad Systemem R. Ponieważ Ray i ja interesowaliśmy się językami, naszym pierwszym zadaniem było opracowanie języka zapytań, który miał służyć jako interfejs użytkownika dla Systemu R.

1 Codd E.F., A Relational Model of Data for Large Shared Data Banks, Communications of the ACM, czerwiec 1970.

284

ROZDZIAŁ

DZIESIĄTY

Studiowaliśmy języki relacyjne zaproponowane przez Codda i innych i postawiliśmy sobie następujące cele: •

Chcieliśmy zaprojektow ać język bazujący na znanych słowach kluczowych pochodzących z języka angielskiego oraz łatw y do wpisywania z klawiatury. Język miał bazować na znanych pojęciach, takich jak tabele złożone z wierszy i kolum n. Zgodnie z pierw otną propozycją języka, opracow aną przez Codda, chcieliśmy, aby nasz język był deklaracyjny, a nie proceduralny. Chcieliśmy wykorzystać siłę podejścia relacyjnego, a jednocześnie uniknąć stosowania pewnych matematycznych pojęć i terminologii, na przykład uniwersalnych kwantyfikatorów oraz relacyjnych operatorów dzielenia, które pojawiły się we wczesnych artykułach Codda. Chcieliśmy również wprowadzić pewne wysokopoziomowe koncepcje zapytań, na przykład grupowanie, które naszym zdaniem trudno było wyrazić w innych językach relacyjnych.



Zakładaliśmy, że język oprócz zapytań będzie dostarczał innych funkcjonalności. Najbardziej oczywistym rozszerzeniem było dołączenie operacji wstawiania, usuwania i aktualizowania danych. Chcieliśmy również zająć się zadaniami, które tradycyjnie były wykonywane przez administratorów baz danych — na przykład tw orzeniem now ych tabel i widoków, zarządzaniem dostępem do danych, definiow aniem ograniczeń i wyzwalaczy w celu utrzym ania integralności bazy danych. Chcieliśmy, aby wszystkie te zadania mogły być zrealizowane w jednorodnym syntaktycznym fram eworku. Zakładaliśmy, że upraw nieni użytkownicy będą mogli wykonywać zadania administracyjne — na przykład definiować nowe perspektywy danych bez zatrzymywania systemu i wywoływania specjalnych narzędzi. Inaczej mówiąc, postrzegaliśmy zadania zapytań, aktualizacji i administracji jako różne aspekty tego samego języka. Pod tym względem mieliśmy unikatową okazję, ponieważ nasi użytkownicy tworzyli swoje relacyjne bazy danych od podstaw. Nie było więc ograniczeń związanych z problem am i wstecznej zgodności.



Chcieliśmy, aby nasz język był używany jako samodzielny język zapytań zarówno do w spom agania procesu decyzyjnego, jak i do tworzenia bardziej złożonych aplikacji. Ten ostatni cel wymagał od nas znalezienia sposobów na stworzenie interfejsu pomiędzy naszym nowym językiem a różnymi popularnymi językami programowania aplikacji.

Bazując częściowo na naszych wcześniejszych doświadczeniach w grze zapytań, Ray i ja opracowaliśm y w stępną propozycję dla relacyjnego języka zapytań o nazwie SEQUEL (akronim od Structured English Query Language) i opublikow aliśm y tę propozycję w formie 16-stronicowego referatu2, który wygłosiliśmy na dorocznej

2 Cham berlin D. i Boyce R., SEQUEL: A Structured English Query Language, m ateriały z konferencji ACM SIGFIDET (prekursor konferencji SIGMOD), Ann Arbor, M ichigan, maj 1974.

SQL

285

konferencji ACM SIGFIDET (będącej prekursorem konferencji SIGMOD) w maju 1974 roku. (Była to ta sama konferencja, podczas której odbyła się słynna debata między Tedem Coddem a Charlesem Bachmanem). Wkrótce po opublikowaniu tego wstępnego dokum entu Ray Boyce zmarł nagle z powodu tętniaka mózgu. Po opublikow aniu wstępnej propozycji język SEQUEL przeszedł fazę walidacji i ulepszeń, która trw ała mniej więcej od 1974 do 1979 roku. W tym okresie język SEQUEL był im plem entow any w ram ach eksperymentalnego projektu baz danych System R w IBM-owskim laboratorium badawczym w San Jose. W Systemie R badano różne aspekty zarządzania bazami danych, w tym indeksy B-drzew, metody złączeń, optym alizację kosztów oraz spójność transakcji. Doświadczenia z implem entacji zapoczątkowały ewolucyjne zmiany w projekcie języka. Na kształt języka SEQUEL w pływały również kom entarze trzech klientów firmy IBM, którzy zainstalowali prototyp Systemu R i eksperymentalnie go wykorzystywali. Zespół Systemu R spotykał się co kwartał z przedstawicielami klientów. Tematem tych spotkań było omawianie sposobów usprawniania języka i jego implementacji. Język SEQUEL bardzo się rozw inął podczas trw ania projektu Systemu R. W celu uniknięcia problemów ze znakami handlowymi nazwę SEQUEL skrócono do SQL. Do języka dodano mechanizm złączeń ogólnego przeznaczenia, którego brakowało w pierwotnej propozycji. D odano możliwości grupow ania oraz klauzulę HAVING pozwalającą na filtrowanie grup. W celu obsługi brakujących informacji do języka dodano wartości nul 1 oraz logikę trójwartościową. Dodano również kilka nowych rodzajów predykatów, włącznie z predykatem Like do realizacji częściowego dopasow yw ania oraz Exi sts do testowania niepustych podzapytań. Opublikowano także dodatkowe dokumenty opisujące ewolucję języka3. W tej fazie projektu języka decyzje były podejmowane na podstawie przesłanek praktycznych, zgodnie z naszymi doświadczeniami w implementacji oraz potrzebami eksperymentalnych użytkowników. Faza badań nad językiem SQL zakończyła się w firmie IBM w 1979 roku wraz z zakończeniem projektu System R. W tym m omencie odpowiedzialność za język przekazano zespołom projektowym. Zespoły te przekształciły prototyp Systemu R na produkty komercyjne dla różnych platform IBM. Jednak pierwszy komercyjny produkt bazujący na języku SQL został opublikowany w 1979 roku nie przez firmę IBM, ale przez m ałą firmę o nazwie Relational Software, Inc. Produkt nosił nazwę Oracle. Później tę firmę przejęła taka, która teraz już nie jest mała. W krótce po w ydaniu im plem entacji Oracle pojaw iły się im plem entacje SQL firmy IBM, a następnie wszystkich ważniejszych producentów baz danych. Obecnie SQL jest najpowszechniej wykorzystywanym na świecie językiem zapytań do baz danych.

3 H utchison N. et al., SEQUEL 2: A Unified Approach to D ata D efinition, M anipulation, andControl, IBM Journal o f Research and Development, listopad 1976; Chamberlin D., A Summary of User Experience with the SQL Data Sublanguage, referat na Międzynarodową konferencję na temat baz danych, Aberdeen, Szkocja, lipiec 1989.

286

ROZDZIAŁ

DZIESIĄTY

W celu zapewnienia przenośności aplikacji pomiędzy różnymi implementacjami języka SQL instytut standaryzacyjny ANSI rozpoczął projekt mający na celu opracowanie standardowej specyfikacji dla języka SQL. Efektem tych prac był język baz danych SQL, który opublikow ano jako standard ANSI w 1986 roku4 oraz jako standard ISO w roku 19875. Opracow anie standardu języka SQL stało się inspiracją do kontynuow ania ewolucji języka. Aby sprostać zmieniającym się wymaganiom, dodaw ano do niego nowe własności. Organizacja ISO opublikow ała nowe wersje standardu SQL w latach 1989, 1992, 1999, 2003 i 2006. Adin Falkoff pracował razem z Kenem Iversonem nad językiem APL. Jego prace przypom inały pańską pracę nad językiem SQL. W obu przypadkach punktem wyjścia był ściśle zdefiniowany model. Czy formalizmy w rodzaju notacji Iversona lub modelu relacyjnego Teda Codda pomagają w stworzeniu skutecznego języka programowania?

Don: Sądzę, że opracow anie relacyjnego m odelu danych m iało fundam entalne znaczenie dla zaprojektowania języka SQL. Uważam, że każdy język programowania służący do wykonywania obliczeń na deterministycznych wynikach wymaga dobrze zdefiniowanego zbioru obiektów i operatorów. Można je nazwać formalnym modelem danych. Myślę, że właśnie to stanowi podstawę programowania deterministycznego. Nawet języki o luźnej typizacji, takie jak Python, bazują na dobrze zdefiniowanym modelu danych. Jest to model bardziej elastyczny od relacyjnego, ale musi być ściśle zdefiniowany po to, aby mógł być wykorzystany w roli bazy dla semantyki języka. Czy gdybym chciał sam stworzyć nowy język, to poleciłby mi pan, żebym wyszedł od ścisłego modelu danych, czy też je st to cecha, którą można dodać do języka w trakcie jego rozwoju?

Don: W zasadzie m ożna pójść obydw om a drogami. Nie zawsze jednak m am y możliwość projektowania nowego modelu danych razem z definiowaniem nowego języka. Na przykład projektanci języka XQuery nie mieli szansy tworzenia języka XML — musieli pracować z modelem danych zdefiniowanym wcześniej przez standard XML Schema oraz wykorzystywać inne standardy organizacji W3C.

Język Dlaczego zainteresował się pan językami zapytań?

Don: Zawsze interesowałem się językami.

4 American National Standards Institute, Database Language SQL, Standard Nr X3.135 (1986 i późniejsze aktualizacje). 5 International Organization for Standardization, Information Technology — Database Language SQL, Standard Nr ISO/IEC 9075 (1987 z późniejszymi aktualizacjami).

SQL

2 87

Czy zna pan jakiś język poza angielskim?

Don: Nie, nie znam żadnego obcego języka, ale lubię czytać, pisać i uważam języki za fascynujący temat. W swoim życiu miałem wielkie szczęście być w odpowiednim miejscu i czasie wtedy, gdy Ted Codd dokonyw ał swoich przełom ow ych odkryć związanych z relacyjnym modelem danych. Była to okazja — taka zdarza się raz w życiu — do wzięcia udziału w projekcie mającym wpływ na nasze pierwsze badania dotyczące baz danych. Moje zainteresowanie językami pozwoliło mi znaleźć dla siebie miejsce w tym projekcie. Jestem bardzo wdzięczny za to, że miałem taką możliwość. Jedną z pierwszych decyzji projektowych było to, aby język SQL był deklaracyjny, a nie proceduralny. Jakie ważne kryteria przemawiały za takim wyborem?

Don: Było ku temu kilka powodów. Po pierwsze, chcieliśmy, aby język można było optymalizować. Jeśli użytkownik szczegółowo poinformuje system o tym, jakiego algorytmu powinien użyć do przetwarzania zapytania, to mechanizm optymalizacji nie będzie m iał możliwości w prow adzania zm ian — na przykład w ybrania alternatywnej ścieżki dostępu lub lepszej kolejności dla stw orzenia złączenia. Język deklaracyjny daje się znacznie łatwiej optymalizować od niskopoziomowego języka proceduralnego. Drugim powodem było to, że byliśmy bardzo zainteresowani niezależnością danych. Uważaliśmy, że adm inistratorzy systemów pow inni mieć możliwość swobodnego dodawania lub usuwania indeksów, modyfikowania organizacji danych oraz tworzenia nowych perspektyw danych. Użytkownik powinien mieć możliwość pisania aplikacji w taki sposób, aby nie było zależności od fizycznej organizacji danych oraz ścieżek dostępu występujących na poziomie fizycznej pamięci masowej. A więc niezależność danych była drugim co do ważności pow odem , dla którego chcieliśmy stworzyć język deklaracyjny. Trzeci powód ma związek z wydajnością użytkowników. Sądziliśmy, że użytkownikom będzie łatwiej wyrażać swoje intencje na wyższym poziomie przy użyciu znajomej terminologii, niż formułować zapytania z wykorzystaniem niskopoziomowych pojęć maszynowych, które były znacznie mniej znajome. Sądziliśmy zatem, że zastosowanie języków deklaracyjnych będzie się wiązało z istotnym i korzyściami z pu nktu widzenia optymalizacji, niezależności danych oraz wydajności użytkowników. Czy były to wówczas powszechne poglądy w pana środowisku?

Don: Myślę, że dobrze rozum iano zalety języków deklaracyjnych, ale uważam, że istniał znaczący stopień niepewności co do tego, czy uda się zaimplementować tak złożony język deklaracyjny jak SQL i jednocześnie zapewnić poziom wydajności odpowiedni dla aplikacji komercyjnych.

288

ROZDZIAŁ

DZIESIĄTY

Perspektywy jako abstrakcyjny sposób zaprezentowania fizycznej struktury danych na dyskach. Czy w tamtych czasach celem było zapewnienie możliwości interakcji z danymi za pośrednictwem perspektyw zamiast bezpośrednio za pomocą tabel?

Don: Uważaliśmy, że perspektywy będą służyły głównie do formułowania zapytań 0 dane, ponieważ różne aplikacje wymagają dostępu do danych w inny sposób. Na przykład różne aplikacje mogą przeglądać dane na różnych poziomach agregacji 1 mogą być uprawnione do przeglądania różnych fragmentów danych. Perspektywy zapewniają bardzo naturalny mechanizm implementacji tych różnic podczas dostępu do danych. Z kolei w przypadku aktualizacji sytuacja jest znacznie trudniejsza — za pośrednictwem perspektywy system musi odwzorow ać aktualizacje na dane zapisane na dysku. W niektórych sytuacjach można to zrobić, natomiast w innych nie istnieje unikatowe odwzorowanie, które można by zastosować. Na przykład bez problemu sformułuje się zapytanie tworzące perspektywę zawierającą średnie płace według działu. Jeśli jednak ktoś spróbuje zaktualizować tę perspektywę, trudno będzie stwierdzić, co to znaczy — zmodyfikować średnią płacę dla działu. Zatem doszliśmy do wniosku, że wykorzystanie perspektyw znacznie bardziej nadaje się do tworzenia zapytań niż do aktualizacji danych. SQL był jednym z pierwszych języków, który musiał obsłużyć równoległy dostęp do współużytkowanych danych. Jaki wpływ na projekt języka SQL miała ta sprawa?

Don: Utrzymywanie spójności bazy danych w środowisku równoległych aktualizacji było jednym z najważniejszych problem ów badawczych w projekcie System R prowadzonym w IBM Research. Ostatecznym wynikiem tej pracy była ścisła definicja właściwości „ACID” transakcji elektronicznych. Za opisanie tych technologii Jim Gray otrzymał w 1999 roku Nagrodę Turinga. Te właściwości transakcji były obsługiwane w Systemie R (oraz innych relacyjnych systemach baz danych) za pośrednictwem systemu blokad i dzienników, które w większości były przezroczyste dla użytkowników SQL. Równoległy dostęp do współdzielonych danych w języku SQL odzwierciedlają głównie pojęcia transakcji oraz stopni izolacji (litera „I” w akronimie ACID oznacza izolację). Poziomy izolacji umożliwiają projektantom aplikacji decydowanie o równowadze pom iędzy zabezpieczaniem użytkow ników przed sobą a maksymalizacją liczby użytkowników, którzy mogą być obsłużeni równolegle. Na przykład w celu uniknięcia zablokow ania dużych fragm entów bazy danych aplikacja realizująca analizę statystyczną może prezentować niski poziom izolacji. Z kolei transakcja bankowa może określać wysoki poziom izolacji. Chodzi o zapewnienie, że wszystkie transakcje dotyczące określonego konta będą mogły być serializowane.

SQ L

289

Dla program istów jednoczesne aktualizacje również są widoczne — w postaci potencjalnych zakleszczeń. W pewnych okolicznościach dwie równoległe transakcje SQL mogą napotkać zakleszczenie. W takim przypadku jedna z transakcji otrzyma kod wyniku wskazujący na to, że jej efekty zostały cofnięte. Czytałem o interesującej historii związanej z Halloween, która wydarzyła się w czasach Systemu R i dotyczyła Pata Selingera i Mortona Astrahana.

Don: W okolicach święta Halloween, około 1975 roku, Pat Selinger i Morton Astrahan pracowali nad mechanizmem optymalizacji dla pierwszej implementacji SQL. Jego zadaniem był wybór ścieżki dostępu do wykorzystania podczas realizacji masowej aktualizacji — na przykład realizacji podwyżki płacy dla wszystkich pracowników o zbyt niskiej pensji. Początkowo Pat i M orton sądzili, że wyszukiwanie pracowników zarabiających mniej od określonej kwoty najlepiej będzie zrealizować przy użyciu indeksu do atrybutu wynagrodzenia. W związku z tym optymalizator używał indeksu do skanow ania pracow ników w kolejności rosnących pensji. Przy okazji tego przeglądania aktualizow ał w ynagrodzenia. Zaobserwowaliśmy jednak, że kiedy zmieniała się pensja pracownika, zmieniało się jego miejsce w indeksie. Mogło się więc zdarzyć, że podczas kontynuacji przeglądania aplikacja napotka tego samego pracow nika jeszcze raz i ponow nie da m u podwyżkę. Opisany sposób działania algorytmu doprowadził do uzyskania nieprawidłowych i trudnych do przewidzenia wyników. Pat i M orton odkryli ten problem w piątek po południu, w dniu amerykańskiego święta Halloween. Pat przyszedł do mojego biura i zapytał: „Co z tym zrobimy?” . Odpowiedziałem: „Pat, jest piątek po południu, nie możemy rozwiązać tego problemu dziś; zapamiętajmy go jako problem Halloween i spróbujmy rozwiązać w przyszłym tygodniu” . Od tamtej pory nazwa „problem Halloween” czasami pojawia się jako określenie sytuacji, w której nie można uzyskać dostępu do danych za pomocą indeksu na atrybut, który jest modyfikowany. Ponieważ jest to problem , który muszą rozwiązać m echanizm y optymalizacji wszystkich baz danych, nazwa „problem Halloween” zyskała sporą popularność w branży. Jakie wnioski z lekcji na temat powstania, rozwoju i przystosowania się pańskiego języka do warunków współczesnych mogą wyciągnąć programiści, którzy tworzą systemy komputerowe dziś oraz będą je tworzyć w najbliższej przyszłości?

Don: Myślę, że historia języka SQL pokazuje, jak ważne jest posiadanie konkretnego zbioru zasad rządzących procesem projektowania języka. Sporządziłem listę pewnych zasad, które w mojej opinii (z perspektywy czasu) m ają istotne znaczenie podczas projektowania języka komputerowego. Nie twierdzę, że wszystkie te zasady były ściśle

290

ROZDZIAŁ

DZIESIĄTY

przestrzegane podczas projektowania języka SQL. W rzeczywistości, jak zauważyło wiele osób, niektórych z nich brakow ało w pierw otnym projekcie języka SQL. Do pewnego stopnia te pierwsze braki zostały usunięte w miarę rozwoju języka. Oto m oja lista zasad projektow ych. Wiele spośród nich wydaje się oczywistych, ale stosowanie ich w praktyce jest trudniejsze, niż może się z pozoru wydawać. Hermetyczność Język pow inien być zdefiniowany w kategoriach m odelu danych składającego się ze zbioru obiektów o dobrze zdefiniowanych właściwościach. Każdy operator w języku powinien być zdefiniowany w kategoriach jego operandów, a jego wynik w postaci obiektów m odelu danych. Semantyka każdego operatora pow inna określać efekty działania operatora na właściwościach obiektów wykorzystywanych w działaniu. Kompletność W szystkie rodzaje obiektów w m odelu danych pow inny posiadać operatory pozwalające na ich konstruowanie, dekompozycję na bardziej prymitywne części (jeśli takie są) oraz porównywanie z innymi obiektami tego samego typu. Ortogonalność Pojęcia języka powinny być definiowane niezależnie i nie powinny być przedmiotem specjalnych reguł ograniczających ich użycie. Jeśli na przykład wartość skalarna jest pojęciem języka, to powinna istnieć możliwość użycia dowolnego wyrażenia zwracającego wartość skalarną w dowolnym kontekście, w którym jest oczekiwana wartość skalarna. Spójność Często wykonywane zadania, jak na przykład wyodrębnienie komponentu z obiektu o określonej strukturze, pow inny być spójnie obsługiwane w każdym miejscu, w którym pojawią się w różnych częściach języka. Prostota Język pow inien być zdefiniowany za pom ocą niewielkiego zbioru stosunkowo prostych pojęć. Projektanci pow inni unikać pokusy dodaw ania własności specjalnego przeznaczenia. Jeśli język odniesie sukces, utrzymanie jego prostoty wymaga dyscypliny i determinacji w opieraniu się wielu prośbom o usprawnienia. Wyzwaniom tym łatwiej będzie sprostać, jeśli język będzie miał dobry mechanizm rozszerzalności (patrz następny punkt). Rozszerzalność Język powinien posiadać dobrze zdefiniowany ogólny mechanizm rozszerzalności pozwalający na dodawanie nowych funkcjonalności w taki sposób, by nie miały one wpływu (lub miały niewielki wpływ) na składnię języka. Na przykład język do formułowania zapytań do bazy danych może zawierać mechanizm dodawania funkcji zdefiniowanych przez użytkownika, napisanych w innym języku program ow ania kompletnym w sensie Turinga.

SQ L

291

Abstrakcja Język nie pow inien ujawniać szczegółów konkretnej implementacji ani też nie powinien od niej zależeć. Na przykład eliminacja duplikatów ze zbioru wartości pow inna być określona w kategoriach pojęć abstrakcyjnych, takich jak „klucz główny”, a nie w kategoriach fizycznej strategii — na przykład „unikatowy indeks” (była to wada niektórych pierwszych wersji języka SQL). W świecie baz danych to pojęcie jest czasami określane terminem niezależność danych. Możliwości optymalizacji Język nie pow inien nakładać zbędnych ograniczeń na algorytm y w celu urucham iania jego wyrażeń. Na przykład definicja języka powinna zapewniać pewną elastyczność kolejności wartościowania predykatów. Tam, gdzie to możliwe, semantyka specyfikacji języka pow inna być deklaracyjna, a nie proceduralna. Zapewnia to możliwość automatycznej optymalizacji. W pewnych przypadkach m ożna tolerować pewną nieokreśloność (na przykład podczas przetwarzania określonego zapytania błąd może być zgłoszony lub nie, w zależności od kolejności wartościowania predykatów). Odporność na błędy Nie wszystkie programy są poprawne. Język należy zaprojektować w taki sposób, aby wiele błędów programistycznych można było wykryć i wyraźnie zidentyfikować w fazie kompilacji (tzn. w w arunkach braku fizycznych danych wejściowych). Język powinien również dostarczać mechanizmu pozwalającego programistom na obsługę wyjątków w fazie wykonywania programu.

Uwagi i ewolucja języka Pierwotny artykuł Teda Codda na tem at relacyjnego modelu danych został opublikowany w otwartej literaturze, a wpływ na jego kształt miały osoby spoza firmy IBM, na przykład Larry Ellison oraz grupa Mike'a Stonebrakera z UC Berkeley. Czy ten proces przypom inał model tworzenia oprogramowania open source? W ja k i sposób owa widoczność na zewnątrz wpłynęła na rozwój języka SQL?

Don: W latach siedemdziesiątych relacyjny model danych był nowym pomysłem. Stanowił on przedm iot zaaw ansow anych badań i tw orzenia prototypów . Ogólnie rzecz biorąc, nie był dostępny na rynku. Język SQL pow stał w ram ach eksperymentalnego projektu badawczego o nazwie System R, który był niezależny od standardowego procesu rozwoju produktów w firmie IBM. Dział badawczy firmy IBM zwykle publikuje wyniki badań w otwartej literaturze. Tak samo postąpiliśmy w przypadku języka SQL i innych fragmentów Systemu R. Nie opublikowaliśm y kodu źródłowego im plem entacji SQL. Pod tym względem postąpiliśm y więc inaczej niż w przypadku dzisiejszego m odelu oprogram ow ania open source. Nie publikowaliśmy żadnego oprogramowania, ale opisaliśmy pewne

292

ROZDZIAŁ

DZIESIĄTY

interfejsy i techniki, które były używane w naszej eksperymentalnej implementacji języka SQL. Na przykład pewne nasze techniki optymalizacji zostały opisane w otwartej literaturze. Jak wiadomo, niektóre z tych dokumentów wywarły wpływ na inne osoby w branży zajmujące się projektowaniem podobnego rodzaju oprogramowania. Ten proces współdzielenia pomysłów nie był drogą jednokierunkową. We wczesnych latach badań nad relacyjnymi bazami danych firmy i instytucje, takie jak IBM, UC Berkeley i inne, swobodnie przekazywały pomiędzy sobą myśl techniczną i uzyskiwały z tego względu wzajemne korzyści. Dlaczego język SQL zyskał popularność?

Don: Myślę, że główny pow ód popularności języka SQL wynika z siły i prostoty relacyjnego m odelu danych Teda Codda. Codd był odpowiedzialny za pojęciowy przełom, który zrewolucjonizował zarządzanie bazami danych — język SQL był jedynie próbą hermetyzacji pojęć opracowanych przez Codda w dostępnym formacie. W porównaniu z innymi istniejącymi technikami relacyjne bazy danych zapewniały znaczną poprawę produktywności użytkowników w zakresie tworzenia i utrzymywania aplikacji bazodanowych. Oczywiście SQL nie był jedynym zaproponow anym w latach siedemdziesiątych językiem bazującym na pom ysłach Codda. Uważam, że o sukcesie języka SQL zadecydowały przede wszystkim następujące powody: •

To, że język SQL obsługiwał kom pletny zbiór zadań administracyjnych, miało istotne znaczenie dla akceptacji języka. Używając języka SQL, każdy uprawniony użytkownik mógł w dowolnym czasie stworzyć lub usunąć tabele, perspektywy i indeksy. Aby to zrobić, wystarczyło użyć prostych poleceń. Takie zadania tradycyjnie wymagały interwencji adm inistratora bazy danych, co wiązało się z zamknięciem bazy danych oraz z poniesieniem znaczących kosztów i opóźnieniami. Zastosowanie języka SQL uwolniło użytkowników od adm inistratorów baz danych i pozwoliło na dowolne eksperymentowanie z alternatywnymi projektami baz danych.



Język SQL był stosunkowo łatwy do nauki. Podzbioru języka SQL wystarczającego do wykonania prostych zadań można było się nauczyć w kilka godzin. Jeśli była taka potrzeba, użytkownicy mogli uczyć się bardziej złożonych i rozbudowanych aspektów języka.



Język SQL był dostępny w rozbudow anych im plem entacjach systemów w ielodostępnych dostarczanych przez co najm niej dwóch producentów (IBM i Oracle) oraz na wielu platform ach włącznie z OS/370 i Unix. W miarę wzrostu popularności języka pow staw ały dodatkow e implementacje, co doprowadziło do efektu kuli śnieżnej.

SQ L

2 93



Język SQL obsługiwał interfejsy do popularnych języków programowania. Razem z językami-hostami można było skalować język SQL w taki sposób, by obsługiwał złożone aplikacje.



Wczesne prace w instytucie ANSI i ISO nad standaryzacją języka SQL dały użytkownikom przekonanie, że aplikacje SQL będą przenośne z jednej implementacji do innej. To przekonanie zostało wzmocnione przez powstanie zestawu testów zgodności ze specyfikacją SQL, stworzonego przez National Institute of Standards and Technology (NIST). Niektóre agencje rządowe w Stanach Zjednoczonych wymagały od dostawców baz danych zgodności z bazującym na SQL standardem FIPS-127 (Federal Information Processing Standard).



Rozwój języka SQL następował w sprzyjającym czasie, kiedy wiele przedsiębiorstw tworzyło lub przystosowywało swoje najważniejsze aplikacje do wykorzystania zintegrowanych, korporacyjnych baz danych. Bardzo w tedy brakow ało projektantów aplikacji oraz administratorów baz danych. Wzrost produktywności, możliwy dzięki zastosowaniu języka SQL, pozwolił firmom skutecznie sprostać zadaniom w zakresie rozwoju aplikacji.

Dlaczego język SQL pozostał popularny?

Don: W ielu języków, które były popularne 25 lat tem u, dziś już się nie używa. W tej grupie znalazły się języki wspierane przez duże korporacje. Można by wskazać sporo powodów, dla których język SQL w dalszym ciągu jest powszechnie używany: •

Standard ISO SQL określił możliwy sposób kontrolow anego rozwoju języka, tak aby m ożna było sprostać zmieniającym się w ym aganiom użytkowników. Standard jest utrzymywany przez komitet składający się zarówno z użytkowników, jak i producentów. Producenci angażują zasoby pozwalające na utrzymanie im plem entacji w zgodzie z rozwijającymi się standardam i. Z biegiem lat w standardzie SQL poprawiono niedociągnięcia w pierwotnym projekcie języka oraz dodano ważne now e funkcje, takie jak złączenia zewnętrzne, zapytania rekurencyjne, procedury składowane, funkcje obiektowo-relacyjne, a także własności OLAP (ang. Online Analytical Processing). Standard SQL pełni także rolę elementu skupiającego uwagę i zasoby. Dostarcza wspólnego frameworku, za pomocą którego osoby indywidualne i firmy mogą tworzyć narzędzia, pisać książki, prowadzić kursy oraz świadczyć usługi doradcze.



Język SQL zarządza trw ałym i danym i o długim czasie życia. Firmy, które zainwestowały w bazy danych SQL, mogą na ich bazie tworzyć kolejne rozwiązania. Nie muszą zaczynać wszystkiego od początku, by wdrożyć nowe podejście.



294

Język SQL jest wystarczająco rozbudowany, by pozwalał na rozwiązywanie realnych problem ów . Ma szeroki zakres zastosow ań — od przechow yw ania informacji biznesowych do przetwarzania transakcji. Jest dostępny na wielu platform ach i w wielu środowiskach przetwarzania. Pomimo zarzutów dotyczących braku

ROZDZIAŁ

DZIESIĄTY

elegancji język SQL jest pomyślnie wykorzystywany przez wiele firm do tworzenia aplikacji o kluczowym znaczeniu. Sądzę, że ten sukces odpowiada sposobowi, w jaki rozwijał się eksperymentalny prototyp języka — od początków swojego rozwoju zawierał odpowiedź na potrzeby realnych użytkow ników . Język odzwierciedla również praktyczne decyzje, które podejm ow ano z biegiem lat — w miarę jak język rozwijał się w celu sprostania zmieniającym się wymaganiom. Systemy napisane w języku C są dziś o kilka rzędów wielkości większe od systemów napisanych w języku C w latach siedemdziesiątych, ale i zbiory danych są dziś o wiele większe. Kilka wierszy kodu SQL w dalszym ciągu wystarczy do obsługi bardzo dużego zbioru danych. Wydaje się, że język SQL znacznie lepiej skaluje się z danymi niż język C. Czy to prawda? Jeśli tak, to dlaczego?

Don: Być może ma to związek z dodatkow ym i zaletami języków deklaracyjnych — tym, że są one bardziej wrażliwe na przetw arzanie równoległe niż języki proceduralne. Jeśli wykonujemy na dużym zbiorze danych operację, która jest opisana w sposób nieproceduralny, system ma więcej okazji do podzielenia swojej pracy pomiędzy wiele procesorów. Relacyjny model danych oraz obsługiwany przez niego wysoki poziom abstrakcji są bardzo pomocne w tego rodzaju skalowaniu. SQL jako język deklaracyjny umożliwia kompilatorom niejawne wykorzystanie mechanizmów współbieżności. Czy przez lata otrzymywał pan uwagi od użytkowników produktów bazujących na pańskich badaniach?

Don: Ponieważ w latach osiemdziesiątych główne produkty bazodanowe firmy IBM zaczęły wspierać język SQL, firma IBM przeprowadzała okresowe przeglądy opinii klientów (tzw. customer advisory councils — dosł. doradcza komisja klientów), podczas których zbierano uwagi użytkowników dotyczące języka SQL oraz innych aspektów produktów bazodanowych. Istnieje również niezależna grupa użytkowników pod nazwą IDUG (ang. International DB2 User Group), która raz do roku organizuje spotkania w Ameryce, Europie i Azji. Na konferencjach IDUG następuje intensywna w ym iana informacji pomiędzy użytkownikam i DB2 a firmą IBM. Większość tych informacji trafia do zespołów badawczo-rozwojowych w firmie IBM, gdzie wykorzystuje się je do planowania dalszych usprawnień w naszych produktach. Jest to źródło wielu nowych własności, na przykład rozszerzeń obiektowo-relacyjnych oraz rozszerzeń OLAP. Innym źródłem pomysłów są komisje ANSI i ISO, które rozwijają standardy języka SQL. Komisje te składają się z reprezentantów zarówno użytkowników, jak i zespołów implementujących język. W ymienione źródła uwag pomogły z czasem rozwinąć się językowi, tak by sprostać zmieniającym się potrzebom społeczności użytkowników.

SQL

295

l/l/raz z psychologiem Phyllisem Reisnerem przeprowadzał pan również testy użyteczności dwóch języków: SQUARE i SEQUEL. Czego pan się nauczył w czasie prowadzenia tych testów?

Don: Tak. Phyllis Reisner był psychologiem, który pracował z zespołem Systemu R. Zajm ow ał się testow aniem na studentach college’u opracowyw anych przez nas propozycji konstrukcji języka. SQUARE był jedną z pierwszych prób stworzenia języka obsługi relacyjnych baz danych opartego na notacji m atem atycznej, natom iast SEQUEL był podobnym językiem, w którym wykorzystywano notację słów kluczowych w języku angielskim. Phyllis przeprowadził eksperyment, w którym uczył studentów college’u obu tych języków. Celem eksperymentu było przekonanie się, które z podejść jest łatwiejsze do nauczenia się i wiąże się z popełnianiem mniejszej liczby błędów. O ile pamiętam, notacja bazująca na słowach kluczowych w języku angielskim okazała się łatwiejsza do nauczenia niż notacja matematyczna. Interesujące było jednak to, że większość błędów popełnionych przez studentów college’u nie m iało wiele wspólnego ze strukturą języka. Dotyczyły one takich problemów, jak zapominanie o ujmowaniu ciągów znaków w apostrofy, nieprawidłowe stosowanie wielkich bądź małych liter. Zwykle były to rzeczy, które można było uznać za trywialne lub wynikające z braku konsekwencji, a nie za błędy związane ze strukturą języka lub danych. Niemniej jednak dbanie o takie szczegóły sprawiało użytkownikom sporo problemów. Obecnie często zdarzają się a taki wstrzykiwania kodu SQL przeciwko serwisom sieciowym. Ich przyczyną jest niewłaściwe filtrowanie danych wejściowych przed umieszczeniem ich w zapytaniach do baz danych. Co może pan powiedzieć na ten temat?

Don: Ataki wstrzykiwania kodu SQL są dobrym przykładem czegoś, o czym nawet nam się nie śniło w czasach powstawania języka SQL. Nie przewidzieliśmy, że zapytania będą konstruow ane na podstaw ie danych w prow adzanych przez użytkowników w przeglądarkach WWW. Sądzę, że wypływa z tego nauka, że oprogram ow anie zawsze powinno uważnie sprawdzać dane wprowadzane przez użytkowników, zanim zaakceptuje je do przetwarzania. Czy język SQL rozwinął się w sposób, którego nie oczekiwano, gdy powstaw ał jego pierwotny projekt?

Don: SQL miał być językiem deklaracyjnym, nieproceduralnym i w dalszym ciągu zachowuje ten charakter. Z biegiem lat język rozwinął się jednak do takiego stopnia, że stał się znacznie bardziej złożony, niż początkowo to sobie wyobrażaliśmy. Jest wykorzystywany w wielu zastosowaniach, o których w pierwszych latach nawet nie myśleliśmy. Takie własności, jak kostki danych (ang. data cubes) oraz analiza OLAP,

296

ROZDZIAŁ

DZIESIĄTY

zostały dodane do języka. SQL przeistoczył się w język obiektowo-relacyjny zawierający typy i metody definiowane przez użytkownika. Nie przewidywaliśmy tych wszystkich aplikacji. Dzisiejsi użytkownicy języka SQL muszą radzić sobie ze znacznie bardziej złożonymi zagadnieniam i i potrzebują znacznie bardziej zaaw ansow anych m echanizm ów, niż spodziewaliśmy się w początkowych latach. Ray Boyce i ja mieliśmy nadzieję, że język SQL będzie miał wpływ na branżę baz danych, ale nie oczekiwaliśmy, że ten wpływ będzie tak duży. Sądziliśmy, że tworzymy język, który będzie używany głównie przez okazjonalnych użytkow ników w celu formułowania zapytań ad hoc w aplikacjach wspomagających podejmowanie decyzji. Próbowaliśmy umożliwić dostęp do baz danych nowej klasie użytkowników — osobom, które nie były wykształconymi informatykami. Oczekiwaliśmy, że język SQL będzie bezpośrednio używany przez analityków finansowych, inżynierów urbanistów oraz innych profesjonalistów, którzy nie będą chcieli pisać programów komputerowych. Te oczekiwania okazały się zbyt optymistyczne. Od samego początku język SQL był używany głównie przez wyszkolonych program istów kom puterow ych. W rzeczywistości przez wiele lat duża część kodu została w ygenerowana za pom ocą narzędzi autom atycznych — był to kierunek rozwoju, którego nie przewidywano w początkowych latach. Profesjonaliści niebędący programistami, którzy zgodnie z oczekiwaniami Raya i moimi mieli bezpośrednio używać języka SQL, chętniej używają interfejsów bazujących na form ularzach obsługiw anych przez aplikacje, które w tle m ają dostęp do baz danych za pośrednictw em SQL. Bezpośredni dostęp do danych przez okazjonalnych użytkowników musiał poczekać na powstanie arkuszy kalkulacyjnych i wyszukiwarek. Pracował pan nad systemami relacyjnych baz danych, ale także nad narzędziem do przetwarzania dokumentów o nazwie Quill, które izoluje użytkowników od fizycznej reprezentacji dokumentów. Arkusze kalkulacyjne, takie ja k Excel, również prezentują dane w sposób bardzo intuicyjny — zorientowany na wygodę użytkownika. Czy systemy te mają ze sobą coś wspólnego? Czy ten rodzaj niezależności od danych można rozszerzyć także o środowisko sieci WWW?

Don: Z arów no Quill, jak i Excel są w yposażone w interfejsy pozwalające na bezpośrednie manipulowanie wizualną reprezentacją danych o określonej strukturze logicznej. Taka organizacja okazała się bardzo przydatna. Istnieje tu analogia z relacyjnymi bazami danych: użytkownik operuje na danych na wysokim poziomie abstrakcji, niezależnym od ich struktury. Interfejsy, które bezpośrednio manipulują danym i, są łatwe do nauki i używania, ale na pewnym poziomie muszą być obsługiwane przez specjalizowane struktury danych. Potrzebny jest rodzaj kompilatora lub interpretera, który odwzoruje intencje użytkownika na dane. Jeśli chodzi o internet jako całość — jest on taki, jaki jest — nie ma możliwości zmiany projektu internetu, ale wszystkie popularne wyszukiwarki izolują swoich użytkowników

SQL

2 97

od szczegółów przetwarzania żądań o informacje. Jestem pewien, że wyszukiwarki będą rozwijały się w kierunku wspierania wyższych poziomów abstrakcji, dzięki czemu będą pozwalały na wykorzystywanie semantyki informacji z internetu. Próbował pan stworzyć narzędzie, które będzie przydatne dla laików, tymczasem w większości używają go tylko programiści.

Don: Myślę, że byliśmy nieco zbyt naiwnie optymistyczni, gdy stawialiśmy sobie cele w pierwotnym projekcie języka. W początkowym okresie powstawania relacyjnego modelu danych pracowałem z Tedem Coddem. W tamtym czasie Ted pracował nad projektem Rendezvous — systemem pytań i odpowiedzi bazującym na modelu relacyjnym wykorzystującym język naturalny. Nie sądziłem w tamtych czasach, że uda się zrealizować projekt w stu procentach bazujący na języku naturalnym . Miałem jednak nadzieję, że można stworzyć interfejs na tyle zrozumiały dla ludzi, że będzie się nim można sprawnie posługiwać już po krótkim szkoleniu. Myślę, że w większości celu tego nie udało się osiągnąć. SQL szybko osiągnął poziom złożoności właściwy dla języka program ow ania i wymagał podobnego szkolenia, jak inne języki program ow ania. Z tego pow odu jest używ any głównie przez profesjonalistów. Czuję wielki respekt przed now oczesnym i aplikacjami internetow ym i, takimi jak Google, które można wykorzystywać do wyszukiwania przydatnych informacji prawie bez żadnego szkolenia. W latach siedemdziesiątych po prostu nie było takich technologii. Czy trudność polega na wyjaśnieniu tego, ja k działa język SQL, czy też na zaprezentowaniu koncepcji modelu relacyjnego, na co użytkownicy są nieprzygotowani?

Don: Myślę, że obydwa te czynniki doprowadziły do powstania takiego wymogu, by użytkownicy języka SQL posiadali pewien poziom wiedzy technicznej. Inny czynnik ma związek z różnicą pomiędzy zapytaniami precyzyjnymi i nieprecyzyjnymi. Jeśli wpiszemy kilka wyszukiwanych pojęć w Google, godzimy się na zaakceptowanie niedokładnych wyników. Innymi słowy, Google stara się, najlepiej jak potrafi, znaleźć dokumenty, które najbardziej odpowiadają liście wprowadzonych fraz wyszukiwania. To proces niedeterm inistyczny, a wynik w większości przypadków jest bardzo przydatny. W przypadku języka SWL mieliśmy do czynienia z problem em innego rodzaju — odpowiedzi były w nim deterministyczne. Dla deterministycznych odpowiedzi potrzebny jest język zapytań o wysokim stopniu dokładności. Na przykład trzeba dokładnie rozumieć różnicę pomiędzy słowami kluczowymi and i or. W Google można sobie pozwolić na to, aby sem antyka zapytań była nieco bardziej rozm yta niż w dziedzinie zapytań strukturalnych, w której stosuje się język SQL.

298

ROZDZIAŁ

DZIESIĄTY

Koszty popełnienia błędu lub koszty uzyskania niedokładnych odpowiedzi przy wyszukiwaniu w internecie sq znacznie niższe od kosztów wyliczenia nieprawidłowej wysokości wynagrodzenia pracowników.

Don: To prawda. Jeśli popełnim y gdzieś literówkę lub nie zapamiętamy, jaka była nazwa kolum ny tabeli, dla której chcemy zdefiniować złączenie, to zapytanie SQL może nie zadziałać. Natomiast mniej deterministyczne interfejsy, takie jak Google, tolerują niewielkie pomyłki tego rodzaju. Podkreśla pan rolę determinizmu. Kiedy piszę wiersz kodu, muszę rozumieć, co on ma robić.

Don: Istnieją aplikacje, w których determinizm ma znaczenie, oraz takie, w których nie ma on znaczenia. Zawsze istniała linia podziału pomiędzy tym, co można określić bazą danych, a tym, co określamy ekstrakcją danych. Obie dziedziny rozkwitają i mają swoje zastosowania.

XQuery i XML Czy język XML wpłynie na sposób, w ja k i będziemy korzystali z wyszukiwarek w przyszłości?

Don: Myślę, że to możliwe. Wyszukiwarki już wykorzystują m etadane zawarte w znacznikach HTML, na przykład hiperłączach. Jak w iadom o, XML jest w porównaniu z HTML bardziej rozszerzalnym językiem znaczników. Myślę, że kiedy powstanie więcej standardów bazujących na XML do oznaczania specjalizowanych dokum entów , takich jak dokum enty medyczne i biznesowe, wyszukiwarki będą częściej korzystały z informacji semantycznych zapisanych w znacznikach. Obecnie pracuje pan nad nowym językiem XQuery, który umożliwia dostęp do danych XML. Język XML różni się od relacyjnych baz danych, ponieważ zawiera metadane. Jakie wyzwania napotkał pan podczas projektowania języka zapytań dla formatu XML?

Don: Jedną z największych zalet formatu XML jest to, że dokumenty XML same się opisują. Dzięki temu dokumenty XML mogą mieć różną strukturę. Różnice te można obserwować poprzez czytanie metadanych w postaci znaczników XML zapisanych w samych dokum entach To sprawia, że XML jest bardzo bogatym i elastycznym formatem reprezentowania informacji. We współczesnych aplikacjach biznesowych, w których wymieniamy dokumenty niepodobne pod względem struktury, wewnętrzne metadane zawarte w formacie XML są bardzo ważne. Jednym z głównych celów języka X Query jest w ykorzystanie tej elastyczności, tak aby zapytania mogły operować zarówno na danych, jak i na metadanych. Jednym z problemów, które napotkaliśmy podczas projektowania języka Xquery, jest różnorodność środowisk, w jakich język będzie używany. Istnieją pewne zastosowania,

SQL

299

w których typy mają bardzo duże znaczenie. W tych aplikacjach potrzebne są języki 0 ścisłej kontroli typów. Języki te wykonują wiele testów, a jeśli obiekt okazuje się niezgodny z oczekiwanym typem, zgłaszany jest błąd. Istnieją jednak także inne środowiska, czasami określane term inem Schema Chaos, w których typy danych są mniej ważne. W tych środowiskach można akceptować dane o nieznanym typie bądź typie heterogenicznym. Od języków oczekuje się tego, by były bardzo elastyczne 1 działały na danych wielu różnych typów. T rudno było zaprojektow ać język, który m ógłby spełnić oczekiwania środowisk wymagających zarówno ścisłej, jak i luźnej kontroli typów. Poza tym system typów schematu XML jest znacznie bardziej złożony od systemu typów relacyjnego modelu danych. W związku z tym zaprojektowanie języka do wykorzystania z tym złożonym systemem typów było wielkim wyzwaniem. W efekcie powstał język, który jest bardziej złożony niż SQL. Myślę, że XQuery jest trudniejszy do nauki niż SQL, ale zapłatą za tę złożoność jest możliwość korzystania ze znacznie bogatszego i bardziej elastycznego formatu danych oferowanego przez XML. Uczestniczył pan w standaryzacji dwóch języków zapytań: SQL i XQuery. Czego przy tej okazji nauczył się pan o procesie standaryzacji?

Don: Po pierwsze, nauczyłem się, że standardy odgrywają wielką rolę w zapewnieniu formalnej definicji języka, pozwalają skoncentrować się na uwagach użytkownika oraz dają mechanizm kontrolowanej ewolucji języka ułatwiający sprostanie zmieniającym się wymaganiom. Proces standaryzacji skupia ludzi o różnych punktach widzenia oraz różnym doświadczeniu. W efekcie współpraca przebiega bardzo wolno, ale zazwyczaj prowadzi do powstania stosunkowo rozbudowanej definicji języka. Z mojego doświadczenia wynika, że dla zapew nienia skuteczności komisji standaryzacyjnej języka istotne znaczenie ma stosowanie poniższych praktyk: •

Na każdym etapie prac nad rozwojem języka komisja pow inna posługiwać się parserem referencyjnym w celu walidacji wszystkich przykładów oraz przypadków użycia używ anych w specyfikacji języka i pow iązanych z nią dokum entach. Ten prosty proces umożliwia ujawnienie zaskakująco wielu błędów. Utrzymywanie parsera referencyjnego pozwala ujawnić problem y związane z implementacją i użytkow aniem i daje pewność, że do gramatyki języka nie przedostaną się niejednoznaczności lub inne anomalie.

300



Komisja powinna utrzymywać formalny zbiór przypadków użycia, które ilustrują zamierzone wykorzystanie języka. Te przypadki użycia są pom ocne w eksplorow aniu alternatywnego podejścia do procesu projektowania i mogą służyć jako przykłady najlepszych praktyk używania języka.



Definicja języka powinna być poparta zestawem testów zgodności. Przed przyjęciem standardu należy opracować co najmniej jedną implementację referencyjną, która dem onstruje zgodność ze standardem . Ta praktyka zwykle ujaw nia w arunki

ROZDZIAŁ

DZIESIĄTY

brzegowe oraz pozwala zapewnić kompletność i jednoznaczność semantycznego opisu języka. Standard bez możliwości sprawdzenia poziomu zgodności docelowej implementacji jest niewiele wart. Jakie są różnice między opracowaniem języka SQL a XQuery?

Don: Istnieją pewne różnice. Podczas projektowania języka XQuery było znacznie więcej ograniczeń niż podczas projektowania SQL. Istniało ku temu wiele powodów. Jednym z nich było to, że językiem XQuery od samego początku interesowało się wiele osób. Projektowaliśmy język w kontekście wymagań międzynarodowej organizacji standaryzacyjnej, która miała przedstawicieli z 25 różnych firm. Wszystkie te osoby miały swoją wizję tego, jaki kształt powinien przyjąć język. Pracę tę wykonywaliśmy w pełnym blasku reflektorów — wszystkie kopie robocze były publikow ane w internecie. W efekcie otrzymaliśmy m nóstw o uwag. Większość z nich okazała się bardzo przydatna. Z językiem SQL było zupełnie inaczej. Pracowaliśmy nad językiem w bardzo małej grupie. Poza firmą IBM nikt nie był zainteresowany tymi pracami, a i wewnątrz firmy niewielu się nimi interesowało. W związku z tym mieliśmy wiele swobody — mogliśmy podejmować autonomiczne decyzje bez konieczności ich uzasadniania lub wyjaśniania osobom o różnych poglądach. Praca nad projektem niezwracającym na siebie zbytniej uwagi daje poczucie wolności.

Don: Tak. Ma wiele zalet. Czy rozmiar zespołu ma wpływ na osiągane wyniki?

Don: Tak. Z moich doświadczeń wynika, że idealny zespół składa się z 8 - 10 osób. Zespół o takich rozmiarach może wiele osiągnąć, a jednocześnie jest na tyle mały, że każdy może zrozumieć, co druga osoba robi. W takim zespole informacje mogą się szybko rozchodzić bez większych tarć i kosztów. Mniej więcej z tylu osób składał się zespół, który pracował nad Systemem R i który stworzył pierwszą, eksperymentalną implementację SQL. Jaki jest najlepszy sposób stymulacji zespołu badawczo-rozwojowego?

Don: Myślę, że najlepszym sposobem stymulacji zespołu badawczo-rozwojowego jest stworzenie w arunków do tego, aby jego praca m iała istotne znaczenie. Jeśli ludzie zobaczą, że efekty wysiłków zespołu mogą zmienić świat, badacze poczują większą motywację i będą pracować bardzo ciężko. Myślę, że małe firmy wchodzące na rynek często mają tę cechę: robią coś rewolucyjnego i nie muszą liczyć się z dziedzictwem, które wprowadziłoby ograniczenia w ich pracy. W większych firmach rzadziej zdarzają się okazje do pracy w takim stylu, ale czasami się zdarzają. Dla mnie bardzo motywujące było bycie członkiem zespołu pracującego

SQ L

301

nad technologią relacyjnych baz danych. Było to przedsięwzięcie, które według nas mogło potencjalnie wywrzeć rewolucyjny wpływ na rozwój technologii na świecie. Praca nad projektem, który ma taki potencjał, motywuje ludzi do tego, by wykonywali swoją pracę najlepiej, jak potrafią. W ja k i sposób definiuje pan sukces w swojej pracy?

Don: To bardzo dobre pytanie. Sukces w badaniach według mojej definicji osiąga się wtedy, kiedy badania wywierają trwały wpływ na technikę. Jeśli możemy stworzyć teorie, interfejsy lub metody, które będą powszechnie używane i przetrwają próbę czasu, to możemy śmiało powiedzieć, że nasze badania miały jakąś wartość. Jednym z najlepszych przykładów takich badań są prace Teda Codda. Ted wymyślił pojęcia, które były na tyle proste, że potrafił je zrozumieć każdy. Jednocześnie dawały takie możliwości, że prawie 40 lat później w dalszym ciągu dom inują w branży zarządzania informacjami. Niewielu z nas może aspirować do sukcesu na tym poziomie, ale w taki właśnie sposób zdefiniowałbym idealny efekt projektu badawczego.

302

ROZDZIAŁ

DZIESIĄTY

ROZDZIAŁ JEDENASTY

Objective-C

Objective-C jest kombinacją języków programowania C i Smalltalk. Z języka Smalltalk przejęto obsługę obiektów. Język ten zaprojektowali Tom Love i Brad Cox w latach osiemdziesiątych. Popularność języka wzrosła wraz z powstaniem w 1988 roku systemów NeXT Steve'a Jobsa. Obecnie język Objective-C jest najczęściej spotykany w komputerach Apple z systemem Mac OS X. W odróżnieniu od innych języków obiektowych wykorzystywanych w tam tych czasach język Objective-C zamiast maszyny wirtualnej wykorzystywał niewielką bibliotekę uruchomieniową. W p ływ języka Objective-C jest widoczny w języku programowania Java. Język Objective-C 2.0 jest popularny w aplikacjach Mac OS X i iPhone.

3 03

Inżynieria języka Objective-C Dlaczego zdecydowali się panowie rozszerzyć istniejący język, zamiast stworzyć nowy?

Tom Love: Było to bardzo ważne ze względu na wymóg zachowania zgodności wstecz obowiązujący w dużych firmach. Na początku projektowania podjęto ważną decyzję, że będzie można wziąć program napisany w C i skompilować go za pomocą kompilatora Objective-C. Nie trzeba przy tym będzie niczego zmieniać w programie. Nic, co jest możliwe do osiągnięcia w C, nie będzie zakazane w Objective-C i nic, co m ożna osiągnąć w Objective-C, nie będzie niezgodne z C. Było to duże ograniczenie, ale okazało się ono bardzo ważne. Pozwalało również na łatwe łączenie i dopasowywanie języków ze sobą. Dlaczego wybrał pan język C?

Tom: Prawdopodobnie dlatego, że w naszych pierwotnych środowiskach używaliśmy systemu Unix i programowaliśmy w języku C. Próbowaliśmy wykonywać operacje, które trudn o było osiągnąć w języku C. W sierpniowym w ydaniu magazynu Byte z 1981 roku po raz pierwszy opisano, co m ożna zrobić w języku Smalltalk. Brad powiedział: „Myślę, że wiem, jak dodać do języka C większość własności Smalltalka, o których tu piszą” . Pracowaliśmy jako grupa badawcza w ITT nad rozproszonym i środowiskami programowania. Systemy te miały pomóc inżynierom oprogramowania w budowaniu systemów telekomunikacyjnych. A zatem szukaliśmy odpowiednich instrumentów do stworzenia program ów , które dziś m ożna by nazwać narzędziam i CAD. Było to jednak coś więcej niż tylko narzędzia CAD. Czy z dzisiejszej perspektywy język Objective-C jest pod pewnymi względami lepszy niż Smalltalk?

Tom: Język Objective-C, który istnieje dziś, oraz biblioteki, które istnieją dziś, bardzo się różnią od tych, które istniały w okolicach roku 1984 lub 1983 — wtedy pojawiła się pierwsza wersja języka. Wcześniej mówiliśmy o zbiorze aplikacji, dla których język jest odpowiedni, oraz zbiorze aplikacji, dla których nie jest odpowiedni. Nie ulega wątpliwości, że Smalltalk jest doskonałym językiem do nauki program ow ania obiektowego. Jestem zaskoczony, że nie jest on używany znacznie częściej w środowiskach akademickich, ponieważ doskonale nadaje się do nauki podstawowych pojęć. Z drugiej strony, gdybym był odpowiedzialny za napisanie nowego systemu operacyjnego, nie wybrałbym do tego celu języka Smalltalk. Może on być natomiast doskonałym rozwiązaniem do tw orzenia modeli badawczych, prototypów lub podobnych rozwiązań. Myślę, że istnieje pewien zakres zastosowań, w którym dobrze sprawdza się język C. Istnieje też zakres zastosowań, dla których odpowiedni jest Smalltalk. M ożna także znaleźć pewien obszar zastosow ań, który jest wspólny dla języków C i Smalltalk.

304

ROZDZIAŁ

JEDENASTY

Zarówno język Objective-C, ja k i Smalltalk wywodzą się z języka C, ale rozwój tych języków potoczył się zupełnie w innych kierunkach. Które podejście preferuje pan dziś?

Tom: Istnieje kierunek właściwy oraz ten, który Bjarne obrał dla języka C++. W pierwszym przypadku uzyskujemy niewielki, prosty oraz — ośmielę się to powiedzieć — elegancki język programowania, który jest zgrabny i dobrze zdefiniowany. W drugim przypadku język jest dość brzydki, skomplikowany i trudny, obfitujący we własności sprawiające kłopoty. Myślę, że takie są różnice pomiędzy tymi dwoma kierunkami. Czy język C + + jest pod pewnymi względami zbyt skomplikowany?

Tom: O, z całą pewnością! Język C+ + w dalszym ciągu się rozwija. Ciągle są dodawane do niego nowe własności.

Tom: I cóż z tego? Ja akurat cenię w moich językach ich prostotę. APL to interesujący język programowania, ponieważ jest niezwykle prosty, a jednocześnie oferuje bardzo duże możliwości w pewnych zastosowaniach. Gdybym pisał pakiet obliczeń statystycznych, to język APL doskonale by się do tego nadawał. Pozwala on bowiem na wykonywanie obliczeń na macierzach znacznie łatwiej niż jakikolwiek inny znany mi język. Ale to tylko przykład. Dlaczego pana zdaniem język C+ + jest używany częściej niż Objective-C?

Tom: Ponieważ za nim jest znaczek AT&T. Tylko o to chodzi?

Tom: Tak myślę. Co dziś pan sądzi o języku Objective-C?

Tom: W dalszym ciągu istnieje. Co pan na to powie? W wersji Objective-C 2 .0 dodano sporo interesujących własności — firm a Apple najwyraźniej utrzymuje go przy życiu.

Tom: Wczoraj wieczorem rozmawiałem z człowiekiem piszącym programy dla systemu iPhone. Opowiadał, że ściągnął pakiet Developers Kit for iPhone i jest tam mnóstwo kodu Objective-C. A zatem ten język wciąż jest żywy. Czy rozpoczynając prace nad językiem , przypuszczał pan, że będzie on używany do pisania programów na telefony komórkowe i urządzenia przenośne?

Tom: Po raz pierwszy spotkałem się z Bradem, kiedy zatrudniłem go w grupie badającej zaawansowane technologie w branży telefonicznej firmy ITT. Naszym zadaniem było prognozowanie technologii, jakie będą w użyciu za 10 lat. Kiedy próbowaliśmy stwierdzić, co będzie za 10 lat, dostrzegliśmy, że nie jesteśmy w tym zbyt dobrzy.

OBJECTIVE-C

305

W szczególności nie byliśmy zbyt dobrzy w ocenie rozwoju oprogramowania. Okazało się, że lepiej przewidzieliśmy to, w jaki sposób rozwiną się technologie sprzętowe przez te 10 lat. Byliśmy jednak zbyt optymistyczni w ocenie rozwoju oprogramowania. Mówiąc dokładniej, sądziliśmy, że do 1990 roku wydarzą się rzeczy, które nie wydarzyły się przez następnych 10 lat. N aw et pod koniec la t dziewięćdziesiątych programiści nieufnie podchodzili do niektórych rozwiązań opracowanych przez zespoły pracujące nad językiem Lisp i innymi językam i programowania, a następnie pomyślnie używanych przez kolejne 3 0 lub 4 0 lat.

Tom: To praw da. Programiści słyną z optym izm u. Inna rzecz, że następuje zm iana pokoleń. Stworzyliśmy kom putery PC, stworzyliśmy kom putery PDA oraz program ow alne telefony. Różne rodzaje urządzeń często są program ow ane przez różnych ludzi. To nie jest tak, że te same osoby używają tej samej technologii. Dawniej inne osoby zajmowały się programowaniem komputerów mainframe, a inne — m inikom puterów . Z kolei kom putery PC i stacje robocze były program owane przez inne grupy osób. Każda z tych grup m usiała niezależnie od siebie nauczyć się tych samych lekcji. W dalszym ciągu jest tak samo. Spróbujmy posłuchać osób biorących udział w konferencji poświęconej tworzeniu aplikacji dla platformy iPhone. Ci ludzie nie wyglądają tak samo jak osoby, które pojawiłyby się dziś na konferencji poświęconej kom puterom mainframe lub nawet konferencji dotyczącej programowania w Windows. Programiści .NET to nie tylko inni ludzie, ale także inne pokolenie. Czy to samo można zaobserwować w rozwoju sprzętu?

Tom: Nie sądzę, aby proces nauki tak bardzo się różnił. Dlaczego możemy spekulować na tem at dziesięcioletniej przyszłości sprzętu, ale nie możemy tego robić w odniesieniu do oprogramowania?

Tom: W przypadku sprzętu dysponujem y dobrym i, mierzalnymi danymi. Dla oprogramowania nie mamy takich danych. Być może pan słyszał, że jestem zwolennikiem liczenia. Lubię liczby. Od 30 lat moim hobby jest udzielanie odpowiedzi na pytania w rodzaju: jak długo programiście zajmie napisanie klasy, jak długo będzie ona testowana, ile testerów potrzeba na jednego programistę oraz ile testów trzeba napisać dla każdej klasy czy też ile wierszy kodu można zmieścić na 500 arkuszach papieru: 100 000.

306

ROZDZIAŁ

JEDENASTY

Rozwój języka Czy jest pan zwolennikiem rozwoju i ewolucji języka?

Tom: To nie takie proste. Inaczej jest z językami komercyjnymi, a inaczej z językami typu public dom ain z otwartym dostępem do kodu źródłowego. Jeśli istnieje jeden decydent odpowiedzialny za wprowadzanie zmian w języku i zmiany te są wprowadzane powoli i metodycznie, to jest dobra rzecz — niektórym jednak nie podoba się konieczność płacenia za kompilatory lub płacenia rocznych opłat za pielęgnację kompilatora, który nie zmienia się zbyt często. Z takimi problemami borykaliśmy się od lat. Jest to jeden z problemów napotykanych podczas projektowania i wdrażania języka programowania — takie same trudności występują oczywiście w przypadku systemów operacyjnych. W ja k i sposób podejmuje pan decyzję o dodaniu nowej własności do języka?

Tom: Należy stworzyć jak najm niej konstrukcji, które jednocześnie zapewnią maksymalnie obszerny zbiór funkcjonalności oraz maksymalną elastyczność. Powiedział pan, że obszar zastosowań obiektowych języków programowania jest w pewnym sensie ograniczony. Czy istnieją sposoby zmniejszenia tych ograniczeń?

Tom: Dla każdego języka m ożna wskazać zakres systemów, dla których język jest odpowiedni, oraz takie systemy, do których ten język się nie nadaje. W dalszym ciągu istnieją osoby, które dla określonych, specjalnych aplikacji piszą kod w języku asemblera, ponieważ potrzebują jak najlepszej wydajności wykonania. Nie sądzę, aby przypadki zastosowań języka asemblera były częste. Istnieją jednak pewne ograniczenia, o których należy pamiętać. Uważam, że niezależnie od tego, jak obszerny jest zakres, zawsze można znaleźć przykłady elementów, które leżą poza nim. Nie chcę przez to powiedzieć, że obszar zastosowań obiektowych języków programowania jest wąski. Jeśli na przykład budujemy rodzaj systemu pokładowego dla zdalnie sterowanego urządzenia, przy czym mamy niewielki procesor, niewielki system oraz model samolotu, to jest inny problem niż projektowanie oprogramowania, które ma działać w samolocie Boeing Dreamliner. Rozumiem, dlaczego wybrał pan język Smalltalk. To był zdecydowanie najlepszy wybór. Nawet dziś jest to dobry wybór.

Tom: Smalltalk to elegancki język. Znam kilka wczesnych języków programowania, na przykład APL. Język APL, podobnie jak Smalltalk, ma jedną zasadę, wokół której został zaprojektow any i zbudow any — stosow anie jak największych uproszczeń. Było to niezwykle ważne. Objective-C m iał być językiem hybrydow ym . Zawsze podkreślaliśmy, że nie będziemy usuwać niczego z języka C. Dodam y tylko nowe elementy. Z tego względu nie tworzyliśmy języka, który byłby pochodną języka C, ale raczej język będący hybrydą powstałą na bazie języka C.

OBJECTIVE-C

3 07

N iektóre z tych wczesnych decyzji projektow ych były bardzo ważne. Dzięki nim język w dalszym ciągu jest w użyciu. Często mówię o sobie, że jestem facetem odpow iedzialnym za nawiasy kw adratow e w języku Objective-C. Brad i ja długo na ten tem at rozmawialiśmy: czy stosujemy składnię spójną z językiem C, czy też tworzym y język hybrydow y (wtedy zwykłem mówić: „Nawias kw adratow y jest przełącznikiem do świata obiektów”). Wyrażaliśmy pogląd, że gdybyśmy mieli język hybrydowy, moglibyśmy stworzyć zbiór klas podstawow ych, tak aby w pew nym momencie większość operacji była wykonywana wewnątrz nawiasów kwadratowych. To pozwala ukryć przed typowym programistą aplikacji mnóstwo szczegółów. Nawiasy kwadratowe są sygnalizacją komunikatu przesłanego do języka Objective-C. Pierwotna idea była taka, że po stworzeniu zbioru bibliotek klas większość działań będzie wykonywanych wewnątrz nawiasów kwadratowych. A zatem programowanie obiektowe jest realizowane za pośrednictwem frameworku obiektów zaprojektowanych w języku hybrydowym będącym kombinacją języków proceduralnego i obiektowego. Następnie, po rozpoczęciu tw orzenia bibliotek funkcjonalności, jest coraz mniej potrzeb wchodzenia do świata proceduralnego. W związku z tym można pozostać wewnątrz nawiasów kwadratowych. Celowo podjęliśmy decyzję o zaprojektowaniu języka, który w zasadzie składa się z dwóch poziomów — po stworzeniu odpowiednich podstaw m ożna działać na wyższym poziomie. Myślę, że to właśnie był jeden z powodów wyboru Smalltalka. Gdybyśmy wybrali składnię bardziej przypominającą język C, to nie jestem pewien, czy ktokolwiek używałby jeszcze języka. To m ało prawdopodobne. Pozostałe dwa cele projektowe to prostota i elegancja. W tamtych czasach zawodowy programista zwykle miał doświadczenie w pisaniu programów w 20 różnych językach program ow ania. Z w łasnych obserwacji wiem, że dopiero kiedy zacząłem robić poważne rzeczy w języku APL, dostrzegłem jego rzeczywistą siłę. Przekonałem się, że było to doskonałe narzędzie dla aplikacji, które tworzyłem. Moim pierwszym domowym komputerem był IBM 5100 — komputer, który właściwie był maszyną APL. Uważałem, że interesujące będzie przekonanie się, czy za pomocą języka APL m ożna zrealizować taką aplikację, jak pełnoekranow y edytor tekstu. Okazało się, że było to naprawdę trudne zadanie. Trzeba by było potraktować ekran jako matrycę znaków... to byłoby trudne.

Tom: Zgadza się. To nie było łatwe. W ielu z nas w tam tych czasach m iało doświadczenie w pracy z językami przetwarzania ciągów znaków, językiem Lisp, językami przetwarzania macierzy oraz językami obiektowymi. Osobiście czułem, że za każdym razem, gdy dodaję język do mojego przybornika, uczę się ważnych pojęć o podstawowym znaczeniu.

308

ROZDZIAŁ

JEDENASTY

Mogę sobie wyobrazić, że taka sytuacja potęguje dążenie do stworzenia dobrego, uniwersalnego języka. Czy to była pana motywacja do tego, by wyjść od języka C i dodać do niego mechanizmy języka Smalltalk?

Tom: Próbowaliśmy znaleźć język programowania, który będzie można wykorzystać do zbudowania środowisk programistycznych dla dużych międzynarodowych zespołów produkujących centrale telefoniczne. Nie byliśmy w całości zadowoleni z żadnego z rozwiązań dostępnych w tamtym czasie. Kiedy w sierpniu 1981 roku opublikowano numer magazynu Byte poświęcony językowi Smalltalk, wszyscy kilka razy przeczytaliśmy go od deski do deski. Pewnego dnia Brad przyszedł do m nie do biura i zapytał: „Czy mogę zabrać ten kom puter do dom u na jakiś tydzień? Myślę, że w ciągu tygodnia jestem w stanie pokazać, że potrafię stworzyć mechanizm zbliżony do języka Smalltalk w postaci rozszerzenia do języka C”. Pozwoliłem mu zrobić coś, co w tamtych czasach było bardzo niezwykłe — zezwoliłem, by zabrał komputer do domu. Tamten komputer miał rozmiary kartonu, do którego zmieściłaby się para kowbojskich butów . To był kom puter marki Onyx — firmy zapomnianej przez większość osób. Mam tutaj książkę na temat Smalltalka 8 0 wydaną w 1983 roku. Wygląda na trochę więcej niż tydzień pracy, ale sam kompilator nie jest tak bardzo skomplikowany.

Tom: Nie jest. Języki, które wykorzystują czytelne podstawy, same w sobie nie są zbyt skomplikowane. Dla odróżnienia m ożna sobie wyobrazić, że kom pilator C++ jest bardzo nieelegancki. Ten język nie sprawia wrażenia uporządkowanego. Jest w nim m nóstw o specjalnych i nadzwyczajnych konstrukcji, które nie są ze sobą spójne, i na tym polega problem. Kilka innych osób, z którymi rozmawiałem, powiedziało mi, że chcieliście wyjść od niewielkiego zbioru pojęć, a następnie tworzyć nowe elementy na tej podstawie. Czy potwierdza pan, że tak właśnie było?

Tom: Myślę, że to rozsądne. W yszedłbym od kilku bardzo prostych i napraw dę czytelnych języków. Smalltalk i APL to dwóch naturalnych kandydatów. Jest też kilka innych, które można by wykorzystać. Lisp jest kolejnym przykładem takiego języka. Dla odm iany m ożna by rozważyć użycie bardzo brzydkiego języka. Podam panu kandydata. To ważniejsze, niż m ożna by sądzić. Chodzi o język program ow ania MUMPS. To bardzo prosty język. Jest raczej brzydki i nieuporządkowany, i bardzo niekonwencjonalny, ale sprawdza się jako narzędzie do tworzenia takich systemów, jak elektroniczne systemy historii choroby. W rzeczywistości jest to bardziej środowisko programowania niż język programowania. Środowisko to jest bardzo pomocne do tworzenia wysoko wydajnych, elektronicznych

OBJECTIVE-C

309

systemów historii leczenia. Największy z istniejących systemów przetwarzania danych został napisany niem al w całości w języku MUMPS i jest wykorzystywany przez Departament do spraw Kombatantów (ang. Department o f Veteran Affairs). Spośród 108 aplikacji około 100 napisano w języku MUMPS. To około 11 milionów wierszy kodu, które bardzo trudno przeanalizować. Kod MUMPS nie przypom ina kodu w żadnym innym języku programowania. Ponieważ elektroniczne systemy historii choroby są bardzo ważne dla wszystkich, a największy spośród znanych systemów produkcyjnych napisano w języku MUMPS, język ten jest ważniejszy, niż wydaje się większości osób. Jeden z moich kolegów zawsze powtarza, że najpopularniejszymi językam i programowania i tak są arkusze kalkulacyjne. Czy to pana dziwi?

Tom: Podam panu przykład patologii języków program ow ania, o której prawdopodobnie pan nie słyszał. Kiedyś pracowałem w małej firmie o nazwie Morgan Stanley, która — być może pan wie — zajmowała się systemami obrotu papierami wartościowymi. Pewien człowiek pracujący w tej firmie stwierdził, że spośród wszystkich języków programowania dostępnych na świecie żaden nie nadaje się do tworzenia naprawdę wydajnych systemów obrotu papierami wartościowymi. W związku z tym zdecydował, że zaprojektuje własny język programowania. Język ten został zaprojektowany w duchu języka APL. Człowiek, o którym mówię, uważał, że do napisania każdego szanującego się kompilatora wystarczy mniej niż 10 stron kodu. Kiedy w języku i jego środowisku dodaw ano dodatkow e własności, człowiek ten starał się robić wszystko, by zmieścić je na tych 10 stronach. W pewnym momencie zaczął skracać nazwy zmiennych, tak by w każdym wierszu zmieściło się ich więcej. Po około 15 latach te 10 stron kodu przypom inało zrzut pamięci. Była połowa lat dziewięćdziesiątych. Każdy system zarządzania papierami wartościowymi powstający w firmie Morgan Stanley był programowany w tym języku — znanym pod nazwą A+. Kiedy zacząłem pracę w firmie Morgan Stanley, pracowało dla mnie 259 ludzi w trzech różnych lokalizacjach geograficznych: Tokio, Londynie i Nowym Jorku. Rozpocząłem rozmowy z tymi ludźmi — postanowiłem, że spędzę co najmniej 30 minut przy biurku każdej z tych 250 osób. Zauważyłem, że osoby te posługiwały się bardzo różnymi językami programowania. Zacząłem tworzyć sobie listę. Kiedy zakończyłem rozmowy, m iałem listę 32 proceduralnych języków program ow ania. Nie były to języki zapytań. Nie były to języki poleceń. Wszystkie one były językami programowania. Zadałem pytanie wspomnianej grupie osób: „Czy nie sądzicie, że wystarczyłoby nam 16 języków?” . Rozpocząłem kam panię redukcyjną. Nazwy wszystkich 32 języków umieściłem na wielkim schemacie na tablicy i do każdej z tych nazw przypiąłem kartkę. Na jednej stronie tej kartki znalazła się nazwa języka, a na drugiej ta sama nazwa, ale przekreślona.

310

RO Z D Z I A Ł

J E D E N A ST Y

Oznaczało to, że właśnie zdemobilizowaliśmy ten język programowania. Pewnego dnia zadzwonił do mnie mój pracownik z Londynu: „Słuchaj, Tom. Przełącz telefon na głośnom ówiący i podejdź do tablicy. M amy pewną ceremonię. Dziś właśnie zdemobilizowaliśmy następujący język program owania” . Nie pamiętam dokładnie, jaki to był język. Spojrzałem na tablicę i odpowiedziałem: „O, nie” . Mój rozmówca zapytał: „Co się stało?”. Powiedziałem: „Nie było go na tablicy. Dalej mamy 32 języki. Dobra wiadom ość jest taka, że właśnie przestaliśmy używać zbędnego języka programowania. Zła wiadomość, że w dalszym ciągu używamy 32 języków” . To zabawna historia. W yobraźmy sobie jednak 250 osób piszących w 32 różnych językach programowania. Pomyślmy o problemach zarządzania kapitałem ludzkim oraz problemach przydziału zasobów. Mamy 15 osób, które są dostępne do pracy, ale nie programują one w tych czterech językach programowania, które mają być używane w następnym projekcie. To jest po prostu niezwykle drogi sposób zarządzania organizacją. W tym biznesie więcej nie znaczy lepiej. Gdyby dziś projektował pan nowy język programowania, to ja k by on wyglądał?

Tom: Proszę pam iętać, że nie jestem zw olennikiem dodaw ania czegokolwiek do technicznego dziedzictwa, jeśli nie jest to absolutnie konieczne. Zacząłbym od postawienia sobie pytania, czy to naprawdę jest potrzebne. Nie wspomnieliśmy jeszcze o języku Ruby. To czytelny język programowania, który może być bardzo wydajny — na tyle wydajny, aby pozwalał na wykonywanie wielu działań. Język ten ma interesującą i czytelną strukturę. Nie czuję potrzeby tworzenia nowego języka. Większość mojego czasu poświęcam na planowanie działań, które należy podjąć, zanim napisze się pierwszą linijkę kodu. W tym tygodniu właśnie wręczyłem m oim kolegom duży stos znaków z napisem WYMAGANIA przekreślonym na czerwono, tak jak na znaku drogowym. Na odwrocie znaku jest 14 dozw olonych alternatyw tego słowa. Różne osoby lub grupy osób prowadzą dyskusje o następującej treści: „Nie mogę wykonać tej pracy, ponieważ jeszcze nie otrzymałem w ym agań” albo „Potrzebna jest grupa osób, która zbierze wymagania dla nowego systemu” . Termin „wymagania” jest bardzo nieprecyzyjny. Potrzebna jest alternatywa — o wiele dokładniejszy termin. W pewnym dużym projekcie, w który byłem zaangażowany, nałożyliśmy coś w rodzaju podatku od wymagań. Jeśli ktoś użył słowa „wymagania” , musiał wpłacić 2 dolary na fundusz rozrywkowy. Można było rozmawiać o przypadkach użycia, historiach użytkownika, metrykach wydajności, przypadkach biznesowych lub modelach procesów biznesowych. Wszystko to były dozwolone pojęcia, za które nie trzeba było płacić podatku. Jeśli bowiem ktoś powie: „Potrzebny jest przypadek użycia lub specyfikacja funkcjonalna albo m akieta tworzonej aplikacji”, to użyje precyzyjnych stwierdzeń.

OBJECTIVE-C

311

Jeśli w projektach ta kwestia nie będzie rozstrzygana praw idłow o, będą kłopoty. Obecnie pisanie kodu nie jest już najtrudniejszą częścią projektu. Dziś najtrudniej jest określić, jakie działania ten kod ma realizować. Czy sądzi pan, że osiągnęliśmy poziom wydajności, w którym języki, narzędzia, platformy i biblioteki nie mają tak wielkiego znaczenia dla sukcesu, ja k uważaliśmy jeszcze 2 0 lat temu?

Tom: Myślę, że tak właśnie jest. Sporo czasu minęło, zanim zdałem sobie sprawę, że kiedy piszę klasę w obiektowym języku program ow ania, to w rzeczywistości rozszerzam język programowania. W niektórych językach programowania jest to trochę bardziej oczywiste niż w innych. Dość oczywiste na przykład w języku Smalltalk i nieco mniej oczywiste w takim języku jak C++. Ponieważ możemy tworzyć specjalizowane języki poprzez opracowanie frameworków lub bibliotek klas, problem ten przechodzi do innych faz cyklu projektow ania. Podkreśliłbym tu zarówno fazę tworzenia frontonu procesu, co można opisać przez pytanie „Co powinniśmy zrobić?”, jak i zaplecze procesu, czyli testowanie. W ubiegłym tygodniu rozmawiałem z program istą tworzącym aplikację, w której spodziewano się 40 000 użytkow ników dziennie. Poprosiłem go: „Opowiedz mi o testach obciążenia, które macie zamiar przeprowadzić, aby przekonać się, czy system będzie w stanie obsłużyć te 40 000 osób, kiedy one jednocześnie wcisną klawisz Return”. Praw dopodobnie moje doświadczenia z pracy w branży telefonicznej sprawiły, że uważam obciążenie za problem inżynierii systemu. W świecie systemów medycznych baz danych znalazłem się wśród ludzi, którzy rozmawiali o przesyłaniu w ułam ku sekundy elektronicznych danych medycznych z centralnej bazy danych na duże odległości. Zapytałem: „Czy wiecie, jaki rozmiar ma plik danych medycznych z jednego dnia? Pięć gigabajtów” . Kiedy zaczniemy się zastanawiać nad tym, jaką przepustowość powinno mieć łącze, aby było w stanie przesłać 5 gigabajtów w ciągu ułamka sekundy na dużą odległość, okaże się, że jest to bardzo kosztowne. Co prawda m ożna to zrobić, ale nie jest to rozwiązanie tanie. Prawie nigdy nie ma takiej potrzeby.

Edukacja i szkolenie Co pan poleca osobom chcącym nauczyć się skomplikowanych, technicznych pojęć?

Tom: Myślę, że w ramach przykładu warto przyjrzeć się dawnym sposobom nauki zawodów rzemieślniczych. W każdej branży kariera powinna przebiegać stopniowo. Najpierw należy zrozumieć proste aspekty pracy: w jaki sposób pisać przypadki testowe, jak należy projektować specyfikacje funkcjonalne projektów itp. Dopiero

312

RO Z D Z I A Ł

J E D E N A ST Y

później m ożna przechodzić do szczegółów bardziej zaawansowanych technicznie: jak tworzyć rozwiązania, w jaki sposób należy je implementować oraz jak wykonuje się bardziej złożone operacje, na przykład testow anie obciążenia systemów lub faktyczne wdrażanie systemów wielkiej skali. Myślę, że istnieje tendencja zlecania ludziom zajęć, do których brakuje im kwalifikacji. Nie ma się co później dziwić, że wykonanie zadania się nie powiodło. Tak się składa, że wiem trochę więcej o Niemczech niż o Włoszech. Wiem, że aby w Niemczech zostać certyfikowanym architektem, najpierw trzeba spędzić pewien czas — myślę, że około sześciu miesięcy — na w ykonyw aniu różnych zawodów budowlanych. Trzeba nauczyć się tego, w jaki sposób działa hydraulika, elektryczność, jak wykonuje się ogrodzenia budynków . Dopiero później uzyskuje się certyfikat architekta i można legalnie projektować budynki. W branży tworzenia oprogramowania zapom inam y o procesie właściwego szkolenia akademickiego oraz zdobyw aniu praktycznego doświadczenia. Jednak naw et właściwe kształcenie akademickie nie zastępuje szkolenia w pracy. Jak ważne jest praktyczne doświadczenie?

Tom: Posłużę się analogią z branży lotniczej — trzeba przejść przez m etodyczny proces prawny latania małymi samolotami, nieco większymi samolotami, następnie jeszcze trochę większymi i nieco szybszymi samolotami. Dopiero po przejściu tego szkolenia m ożna usiąść na przednim siedzeniu Boeinga 757, który z 300 osobami na pokładzie leci przez Ocean Atlantycki. Oczywiście branża lotnicza wypracowała ten zbiór przepisów na podstaw ie w niosków z w ypadków lotniczych, w których z pow odu innego postępowania zginęło wielu ludzi. A zatem przepisy w lotnictwie powstały dopiero wtedy, kiedy pojawiła się potrzeba tworzenia przepisów. Które tematy studenci powinni zgłębić szczególnie?

Tom: N iedaw no byłem na spotkaniu z kilkom a w eteranam i branży inżynierii oprogram ow ania. Zadawaliśmy sobie pytania o to, które szkoły w Stanach Zjednoczonych najlepiej uczą inżynierii oprogram ow ania, jakie m ają uznanie oraz jakie fundusze otrzymują z budżetu państwa. Niestety informacje nie są najlepsze. Wyraźnie oddzielam edukację w dziedzinie inżynierii oprogramowania od edukacji w inżynierii komputerowej. Nie mówię tu o nauczaniu tego, w jaki sposób należy projektować kompilatory lub pisać systemy operacyjne, ale raczej o tym, jak zaplanować projekt i pomyślnie go zrealizować, występując w różnych rolach. Nie jestem tak dobrze zorientowany, jak byłem kiedyś na temat systemów nauczania inżynierii oprogram ow ania w Europie, ale twierdzę, że w Stanach Zjednoczonych system edukacyjny jest dość słaby. Ostatnio mój kolega ze Szwecji spytał mnie: „Mam przejąć odpowiedzialność za organizację procesu tworzenia oprogramowania. Jakie książki z zakresu inżynierii oprogram ow ania pow inienem przeczytać, aby lepiej wykonywać swoją pracę?” Dałem mu listę pięciu książek, a on wtedy zapytał: „Gdzie

OBJECTIVE-C

3 13

mogę je dostać?” . Odpowiedziałem: „Mógłbyś spróbować w serwisie Amazon, ale tak się składa, że jutro będą w księgarni uniwersyteckiej. Jeśli chcesz mogę ci je kupić i wysłać” . W ziąłem moją listę pięciu książek i udałem się na Uniwersytet Yale. Okazało się, że żadnej z pięciu książek nie było w księgarni, co naprawdę mocno mnie zaskoczyło. Uniwersytet Yale nie jest najbardziej prestiżową szkołą informatyczną ligi bluszczowej (ang. ivy league), nie mówiąc już o Stanach Zjednoczonych, ale realizuje się tu uznany program od wielu lat. Pomimo to w księgarni uniwersyteckiej nie było naw et odpowiednich książek. Nie chcę nawet myśleć, jakie były wykłady, na których studenci pow inni uczyć się m ateriału, który nieco wykracza poza to, co m ożna znaleźć w książkach. Niedawno widziałem zabawną rzecz. Nowy produkt reklam owany w ramach całej linii produktów. Producent twierdził, że jest on w 100% napisany w Objective-C. W ja k i sposób szkoliłby pan projektantów oprogramowania?

Tom: Zacząłbym od umieszczenia ich w grupach szkoleniowych i nauczenia, w jaki sposób należy testować kod i jak należy go czytać. Biznes wytwarzania oprogramowania to jedna z niewielu dziedzin, w których uczymy ludzi pisać, zanim nauczymy ich czytać. To napraw dę błąd. Nic tak dobrze nie uczy program ow ania, jak próba zrozumienia skomplikowanego kodu. To bardzo pouczające zajęcie. Zachęcałbym również do zaznajomienia się z istniejącymi produktami oprogramowania — takimi, które są dobrze zaprojektowane. W ten sposób studenci mogą zdobyć doświadczenie w zakresie budow y systemu od w ew nątrz w odróżnieniu od poznaw ania ich od zewnątrz. Mówiłbym: „O to przykład bardzo dobrze napisanego p roduktu w naszej firmie. Przyjrzyj się dobrze napisanemu produktowi i porównaj z tym, nad którym obecnie pracujesz” . Powierzałbym im coraz większe obowiązki, ale w bardzo krótkich cyklach. M ógłbym dzięki tem u oceniać ich postępy i sukcesy, a także udzielać pomocy, gdyby tego potrzebowali. W ja k i sposób zatrudnić dobrego programistę?

Tom: To trudne pytanie. Być może pan wie, że tematem mojej pracy doktorskiej była charakterystyka psychologiczna skutecznych programistów w latach siedemdziesiątych. Niektórzy z nich są nadal aktywni?

Tom: Tak, jest kilku, którzy jeszcze żyją. To dość zajmujące. Istnieją pewne cechy psychologiczno-poznawcze, które są poszukiwane: dobra pamięć, um iejętność dostrzegania detali. Według mnie ważne są także zdolności do komunikacji, zarówno pisemnej, jak i ustnej. Praca w zespole: ważna jest zdolność do skutecznej komunikacji z resztą zespołu. Ważne jest również to, aby programista w przypadku otrzymania kierowniczej roli um iał kom unikow ać się z klientam i, ekspertam i lub zespołami

314

RO Z D Z I A Ł

J E D E N A ST Y

operacyjno-eksploatacyjnymi, z którymi trzeba rozmawiać w momencie rozpoczęcia wdrażania produktów. Nie uważam tego za wymóg, ale gdybym szukał doskonałych programistów — kandydatów na stanowisko głównego projektanta bądź architekta projektu — zwracałbym uwagę na hobby. Bardzo dobrą cechą są zdolności muzyczne — jeśli ktoś studiował muzykę klasyczną i potrafi z pamięci zagrać sonatę fortepianową, to bardzo dobrze o nim świadczy. To bardzo dobry test zdolności zapamiętywania i zwracania uwagi na szczegóły. Poza tym taka sonata może być przyjemnością dla ucha.

Zarządzanie projektem i oprogramowanie odziedziczone Powiedział pan, że programista jest w stanie obsłużyć kod, który można zmieścić na około połowie ryzy papieru.

Tom: To prawda. Obecnie jestem zaangażowany w wiele projektów prowadzonych przez Rząd Federalny. Zabawne, jak przydatna jest znajomość tego prostego faktu. 100 000 wierszy kodu to karton wydruków. Wytworzenie tego kodu kosztuje 3 miliony dolarów. Utrzymanie wymaga dwóch ludzi. Przypadki testowe potrzebne do pełnego przetestowania tego kartonu kodu to kolejne dwa lub trzy kartony kodu. Czy te liczby są niezależne od języka?

Tom: Nieomalże tak. Wydaje się to być prawdziwe przynajmniej dla obiektowych języków program ow ania. W przypadku przeciętnego obiektowego języka program ow ania więcej osób zajmuje się testow aniem kodu niż pisaniem. Jest to spowodowane tym, że języki są rozbudowane. Obecnie mam do czynienia z projektem, w którym jest trzy czwarte miliona wierszy kodu, a ponad połowa tego kodu pochodzi ze źródeł zewnętrznych. Ten kod to nic specjalnego. Nie ma mowy o tym, by była to jakaś nowoczesna technologia. Jeśli spojrzeć na to z tej perspektywy, m ożna powiedzieć, że gdy ktoś powie: „Zamierzam zatrudnić jednego testera dla każdego programisty”, to drastycznie zaniża potrzebne nakłady. Programista wnosi bowiem mnóstwo nieprzetestowanego kodu z zewnątrz, który — przynajmniej w przypadku medycznej aplikacji — musi być dokładnie przetestowany. W tego rodzaju okolicznościach może być potrzebnych czterech, pięciu, a nawet sześciu testerów na każdego programistę. A na przykład we wczesnych latach osiemdziesiątych w środowisku języka C potrzebnych było sześciu programistów na jednego testera. Czy wynika to z właściwości języka C ,jego poziomu abstrakcji i możliwości wielokrotnego wykorzystywania kodu?

Tom: To zależy od rozmiaru tworzonej biblioteki. W początkowych latach języka Objective-C wykonyw ałem wiele analiz. Proszę spróbow ać odpowiedzieć: jeśli weźmiemy duży program napisany w języku C i przepiszemy go na język Objective-C, to ile wierszy kodu otrzymamy? Liczba oscyluje wokół jednej piątej — objętość kodu będzie wynosiła jedną piątą objętości kodu wyjściowego.

OBJECTIVE-C

315

To dobry współczynnik kompresji.

Tom: To duża liczba. Jakieś cztery lata tem u realizowaliśmy projekt, który składał się z kombinacji kodu w językach COBOL i C++. Wówczas 11 milionów wierszy kodu udało się skompresować do około pół miliona wierszy kodu w Javie. Roczna praca pozwoliła na kompresję w stosunku 20 do jednego. Nie trzeba zbyt dużo czasu, aby uzyskać imponujące wyniki. Częściowo ten zysk jest wynikiem lekcji wyniesionej z ponownej implementacji systemu.

Tom: To z pewnością prawda. Opowiedziałbym tę historię w następujący sposób. Faktem jest, że zawsze łatwiej buduje się drugą wersję systemu niż jego pierwszą wersję. Jednym z powodów jest to, że nie trzeba poświęcać czasu na zadawanie sobie pytania, czy to możliwe. Wiadomo, że jeśli coś istnieje, to nie jest niemożliwe. Prawdopodobnie znacie historię o Rosjanach, którzy przejęli plany bombowca B-29 — sam olotu, który zrzucił bom bę atom ow ą. Rosjanie wykonali dokładny klon. Zachowali dokładność na takim poziomie, że zadbali nawet o to, by łata na skrzydle była dokładnie odtworzona w modelu. Samolot ten zbudowali w mniej niż dwa lata i za znacznie mniej niż 3 miliardy dolarów, które Stany Zjednoczone wydały przez ponad pięć lat budow y pierwszego m odelu B-29. Projekt ten kosztow ał znacznie więcej od Projektu M anhattan1! To z całą pewnością prawda. Za drugim razem projekt realizuje się znacznie szybciej. M ów i pan o różnicy przekraczającej rząd wielkości, jeśli chodzi o liczbę wierszy kodu źródłowego. Czy programista może utrzymywać podobną liczbę wierszy kodu niezależnie od języka?

Tom: Na ten temat wielokrotnie rozmawiałem z dobrymi programistami. Istnieje wiele poglądów dotyczących liczby wierszy kodu, jaką może utrzymać programista. Myślę, że wartość średnia jest dość dobrze ugruntowana. Z drugiej strony uważam, że istnieje dość równomierny rozkład dla różnych języków. Istnieje możliwość, że kod będzie napisany tak czytelnie i przejrzyście oraz będzie tak dobrze zorganizowany, że jedna osoba będzie w stanie utrzym ać 200 000 wierszy kodu. To rzadki przypadek, ale z pewnością możliwy. Może się również zdarzyć, że jeden człowiek z ledwością będzie utrzymywał 10 000 wierszy kodu. Takie sytuacje zdarzają się znacznie częściej niż wspomniane wcześniej przypadki większej liczby wierszy kodu niż średnia. Często można zaobserwować sytuacje, że dobrze zaprojektowany system po przekazaniu go do firmy docelowej traci cały porządek podczas tw orzenia aplikacji. Projektem zaczynają zajmować się ludzie, którzy dobrze go nie znają i w bardzo krótkim okresie robią wiele szkód.

1 http://www.rb-29.net/HTML/03RelatedStories/03.03shortstories/03.03.10contss.htm.

316

RO Z D Z I A Ł

J E D E N A ST Y

Czy podczas pracy nad językiem Objective-C brał pan wraz z zespołem pod uwagę te zasady organizacyjne?

Tom: My akurat tak. Z firmy ITT została wydzielona grupa badawcza, której zadaniem było udzielenie pom ocy dużej, m iędzynarodowej firmie telekomunikacyjnej w zbudowaniu rozproszonego, obiektowego cyfrowego systemu telekomunikacyjnego. Mieli to robić we współpracy z rozproszonymi zespołami projektowymi. Byliśmy zaangażowani w te działania. Ja przeszedłem do firmy ITT z firmy General Electric, gdzie byłem zaangażowany w podobne działania. W firmie GE nazywałem swój zespół Grupą Badawczą Psychologii Oprogramowania. Interesowały nas nie tylko te cechy języków programowania, które ułatwiały czytanie, zrozumienie i utrzymywanie kodu, ale także struktury organizacyjne zespołów projektow ych oraz problem y organizacyjne związane z projektowaniem na wielką skalę. Branża oprogramowania zmieniłaby się dramatycznie, gdyby rządy powierzyły twórcom oprogramowania odpowiedzialność za problemy bezpieczeństwa.

Tom: Całkowicie się z tym zgadzam. Jedną z m oich reguł — stosuję ją podczas angażowania ludzi do projektów — jest unikanie włączania ich do projektu różniącego się więcej niż 20% od tego, który wcześniej pomyślnie zrealizowali. Oznacza to, że zanim ktoś zostanie architektem w dużym projekcie, musi zdobyć wiele doświadczenia.

Tom: To przecież jest zupełnie prawidłowe. Alternatywnie można realizować bardzo krótkie projekty. Istnieje jednak ograniczenie: realizacja niektórych projektów trwa około trzech lat. Jeśli w firmie pracujemy nawet przez 40 lat, to możemy wziąć udział w stosunkowo niewielkiej liczbie projektów. Taki sam problem występuje w branży lotniczej. Rozwiązaniem problem u w lotnictwie jest stosowanie realistycznych symulatorów. Podobny mechanizm zastosowałbym w odniesieniu do menedżerów projektu. Człowiekowi może nie starczyć życia do zrealizowania 100 projektów . Problem ten udałoby się rozwiązać, gdybyśmy potrafili symulować niektóre decyzje i doświadczenia. Dzięki tem u m ożna by było w ypełniać swoje CV dokonaniam i opartymi na projektach symulowanych zamiast rzeczywistych. Czy produktywność zależy w większym stopniu od jakości programistów, czy też od charakterystyki języka programowania?

Tom: Efekty różnic osobowych są znacznie większe niż dow olne efekty języków programowania. Badania prowadzone w latach siedemdziesiątych pokazały, że różnice pom iędzy program istam i o tym samym wykształceniu oraz tej samej liczbie lat doświadczenia w niektórych przypadkach można było wyrazić jak 26:1. Nie sądzę, aby ktoś mógł powiedzieć, że jego język programowania jest 26 razy lepszy od języka kogoś innego.

OBJECTIVE-C

3 17

Powiedział pan, że obecnie jest pan ekspertem w przeprojektowywaniu odziedziczonego oprogramowania i że czynność tę można opisać trzema słowami: zwinność, dziedzictwo i reinżynieria. Co pan miał na myśli?

Tom: Spróbujmy przeanalizow ać te słowa, począwszy od ostatniego. Pomówmy najpierw o reinżynierii. Kiedy używam słowa „reinżynieria”, m am na myśli bardzo dokładne zastąpienie określonej funkcjonalności z wykorzystaniem nowoczesnych technik projektow ych i nowoczesnych technologii. W yraźnie oddzielam projekt reinżynieryjny od projektu modernizacyjnego. Projekty modernizacyjne dotyka tzw. efekt drugiego systemu, daw no tem u opisany przez Freda Brooksa w książce The Mythical Man-Month [Addison-Wesley Professional]: spróbujmy w tej iteracji zrobić wszystko to, czego nie potrafiliśmy zrobić w pierwszej iteracji, i zróbmy to o połowę szybciej. Wie pan, co się dzieje? W projektach tych notorycznie występują problemy. Cały czas szukam projektu modernizacyjnego prow adzonego przez rząd Stanów Zjednoczonych, który by się powiódł. O wiele łatwiej znaleźć takie, które się nie powiodły. Muszę jednak znaleźć taki, który zakończył się sukcesem. Słowa „reinżynieria” używam jako głównego term inu do określenia ograniczeń zakresu projektu. Nie mówię, że trzeba spojrzeć na stary system, pomyśleć o nowym systemie, wziąć czystą kartkę papieru i zacząć wszystko od początku. Gdyby można było wykorzystać stary obieg operacji, definicje ekranu lub skorzystać z m odeli danych, albo przynajmniej z elementów modelu danych, gdyby można było wykorzystać przypadki testowe, użyć ponownie dokumentacji lub kursów szkoleniowych, to dałoby się zaoszczędzić wiele czasu i wysiłku oraz usunąć z projektu znaczną część ryzyka. Gigantyczną zaletą projektu reinżynierii jest to, że istnieje działający system, z którego od czasu do czasu możemy czerpać inspiracje. W ten sposób można znaleźć odpowiedzi, których w innym przypadku nie udałoby się znaleźć. To było jedno z tych trzech słów. Słowo „dziedzictwo” oczywiście odnosi się do istniejącego systemu — często jest nim system skali korporacji. Niekoniecznie są to 20-letnie systemy zbudowane za pomocą antycznych proceduralnych języków program ow ania. Mogę podać przykłady odziedziczonych aplikacji w Smalltalku, które trzeba było przeprojektować i napisać od nowa w Javie. System odziedziczony ma pewne cechy charakterystyczne — to system istniejący, wdrożony i działający. Ma on istotne znaczenie dla organizacji i musi być zastąpiony z uwagi na określony zbiór powodów. Powodem może być to, że system działa w antycznym systemie operacyjnym, który ma przestać być wspierany. Może być napisany w języku program ow ania, dla którego nie m ożna znaleźć żadnego żyjącego programisty — takiego, który by dobrze rozumiał kod. Mogło się zdarzyć, że zm ieniły się reguły biznesowe — potrzebne są wszystkie funkcje program u, ale upakowane w inny sposób. Może też być tak, że potrzebne są całkowicie nowe funkcjonalności i wtedy to nie będzie projekt reinżynierii. Wówczas będziemy mieć do czynienia z projektem wytwarzania nowej aplikacji.

318

RO Z D Z I A Ł

J E D E N A ST Y

Trzecie słowo to „zw inność” — oznacza ono proces, który sprawdził się w wielu w arunkach i w przedsięwzięciach dużej skali. Z tego względu menedżer projektu, który nie chce ponosić ryzyka, może pow ażnie brać go pod uwagę jako sposób realizacji działań. W ja k i sposób można przeciwdziałać problemom z oprogramowaniem, odziedziczonym w oprogramowaniu, które jest dziś wytwarzane?

Tom: Nie jestem pewien, czy da się im przeciwdziałać. Każdy produkt, który wytwarzamy, ma swój czas przydatności do użycia. W przypadku biznesu wytwarzania oprogramowania czas przydatności do użycia często jest o wiele dziesięcioleci dłuższy, niż wyobrażali sobie jego twórcy. W takim przypadku pom aga dobra struktura, dobra dokum entacja i właściwe testowanie. Obecnie pracuję dla agencji rządowej nad projektem reinżynierii systemu składającego się z 11 milionów wierszy kodu. Niektóre m oduły mają po 25 lat. Dla wielu nie stworzono przypadków testowych. Nie istnieje dokum entacja poziom u systemu. W niektórych naw et kilka lat tem u pousuw ano kom entarze w kodzie, po to, by poprawić wydajność. Konfiguracja programów nie jest zarządzana. Dla systemu wydawanych jest 50 łatek miesięcznie i robi się to od 1996 roku. To olbrzymi problem. Można by odwrócić wszystko to, co powiedziałem wyżej, i stwierdzić: nie należy robić tego, tego lub tego i to byłoby działanie prawidłowe. A co z modularnością projektu?

Tom: Im system jest lepiej zaprojektowany, tym bardziej jest modularny. Im lepiej został zaprojektowany model obiektowy dla systemu, tym dłuższy prawdopodobny czas użytkow ania systemu. Oczywiście może się zdarzyć, że m am y doskonale zaprojektowany system, a w pewnym momencie zmieniają się reguły biznesu. Nie zawsze jesteśmy w stanie przygotować się na taką ew entualność i właściwie przewidzieć zakres zmian. Czy zna pan jakąś regułę, która wskazuje, ile języków programowania powinno się używać w organizacji?

Tom: Mam regułę dla menedżerów projektu, ale to nie do końca to. Menedżer projektu powinien względnie dobrze znać każdy język programowania używany w projekcie, którym zarządza. Nawiasem mówiąc, tak nie jest prawie nigdy. Uważam, że to jeden z podstawowych powodów, dla których w tak wielu projektach występują problemy. Kiedyś przyszedł do mnie pewien menedżer projektu i powiedział: „W tym projekcie będziemy używali sześciu języków program ow ania. Czy oczekuje pan ode mnie dobrej znajomości tych wszystkich sześciu języków?” . Odpowiedziałem: „Ależ nie, to nie jest jedyny sposób, w jaki m ożna rozwiązać problem. Innym sposobem jest pozbycie się niektórych języków programowania” .

OBJECTIVE-C

319

Ostatecznie zrozumiał, że mówiłem poważnie. Brałem udział w wielu spotkaniach, podczas których programiści rozmawiali na tem at tego, jak tru dno napisać klasę, a m enedżer projektu nie m iał pojęcia, czym jest klasa w nowoczesnym języku programowania. Czy w dalszym ciągu używa pan piłek styropianowych do modelowania swoich systemów, przy czym każda piłka reprezentuje klasę?

Tom: Oczywiście, że tak. W ykonaliśmy też wersję piłek styropianowych bazującą na animacji 3D. Okazało się, że nie była nawet w przybliżeniu tak przydatna, jak piłki styropianowe. Coś jest w widocznej fizycznie strukturze zawieszonej na suficie wprost przed oczami członków zespołu projektowego. Układ ten jest regularnie aktualizowany. Dzięki temu widoczna jest nie tylko struktura budowanego systemu, ale także status każdej z klas. Metodę tę stosowaliśmy w 19 projektach, które były ostatnio prowadzone. Jeden z nich składał się z 1856 klas. Był więc duży — właściwie większy, niż powinien. Był to jednak duży komercyjny projekt, zatem musiał być rozbudowany. Czy klasa w dalszym ciągu reprezentuje podstawową jednostkę postępu w systemie?

Tom: To najbardziej stabilny param etr, jaki w edług m nie m ożna zliczać. Należy zdefiniować naturę klasy, którą piszemy. Jeśli jest to wstępny prototyp klasy, zdoła go napisać jedna osoba w ciągu tygodnia. Prawdziwa klasa wchodząca w skład aplikacji produkcyjnej to więcej niż miesiąc pracy dla jednej osoby. Klasa przystosowana do wielokrotnego użytku to miesiąc pracy dla dwóch do czterech osób. Czy to obejmuje testowanie i pisanie dokumentacji?

Tom: Wszystko — od początku do końca. Przeczytanie i zrozumienie działania klasy zajmuje około jednego dnia. To sprawia, że wiele projektów ma dziś kłopoty. Chcemy użyć jakiejś biblioteki, klasy Swing lub innej, ale nikt nie usiądzie i nie powie, że jeśli wszyscy programiści mają zrozumieć wszystkie 365 klas, to minie 365 dni roboczych, zanim będziemy gotowi do napisania pierwszej linijki kodu. Dla odróżnienia — jeśli zapom nim y o czasie potrzebnym na zrozumienie kodu na początku projektu, możemy być narażeni na znaczne opóźnienia w harmonogramie. Ten czas będzie trzeba poświęcić na przykład na debugowanie.

Tom: Albo na inne czynności. Trzeba będzie ponieść te koszty gdzieś po drodze. To duże liczby. W przypadku realizacji sześciomiesięcznego projektu potrzebne jest dwuletnie opóźnienie, aby w ogóle zacząć.

320

ROZDZIAŁ

JEDENASTY

Czy to się opłaca?

Tom: Czasami tak, a czasami nie. Istnieją różne wyjścia z takiej sytuacji. Jedną z możliwości jest zatrudnienie ludzi, którzy już znają klasy. Inna możliwość to podział biblioteki w taki sposób, aby nie wszyscy musieli rozumieć wszystko. To prawie zawsze jest dobry pomysł. Należy brać pod uwagę takie sytuacje. Nie można pozwolić, by nas zaskoczyły. Spójrzmy na niektóre nowoczesne projekty w świecie Javy. Z łatwością osiągają liczbę 2000 klas, które trzeba wykorzystać. W ciągu roku jest około 200 dni roboczych, zatem uzyskujemy 10-letnie opóźnienie w harmonogramie. Powiedział pan, że z upływem la t czas pisania kodu nie zm ienił się zbytnio, ale wspomniał pan także o tym, że istnieją inne czynniki, które dziś podnoszą wydajność programistów. Czasami osiągnięcie zysku wydajności wymaga inwestycji — trzeba poświęcić czas na naukę.

Tom: Czy nie lepiej poświęcić osobodzień na zrozumienie klasy niż osobomiesiąc na jej przepisanie? To kosztowny proces, ale otrzymujemy w ten sposób olbrzymie ilości funkcjonalności, które w dawnych czasach trzeba było napisać od podstaw. Można uzyskać 2 0 razy większą wydajność, ponieważ w miesiącu jest 2 0 dni roboczych. To dobry kompromis.

Tom: To olbrzymi wzrost wydajności. Spróbujmy wybrać pewną liczbę, którą według mnie m ożna uznać za normalną. W yobraźmy sobie, że musimy zrozumieć 500 klas w celu napisania poważnej aplikacji we współczesnym świecie. Nie jest to 500 całkowicie nowych klas za każdym razem, kiedy rozpoczynamy projekt. Zazwyczaj nie trzeba analizować klas od 0 do 500 w jednym kroku. Zwykle do tak dużej liczby klas potrzeba co najmniej pięciu kroków. W ja k i sposób rozpoznaje pan prostotę projektu?

Tom: W starych czasach miernikiem była liczba stron notacji BNF potrzebnych do opisania języka. Nie jest to zła metryka, ponieważ umożliwia odróżnienie języków skomplikowanych od prostych. Jeśli spojrzeć na program w języku APL lub Smalltalku, okazuje się, że nie ma tam zbyt dużo konstrukcji języka. Wszystko wygląda tak, jakby języka w ogóle nie było — i dobrze. Jak obszerny jest podręcznik języka? Co w nim się znajduje? Język programowania Objective-C nie jest bardzo skomplikowany, ale biblioteki klas, które budowano latami za jego pomocą, są skomplikowane. Opisanie wszystkich tych szczegółów jest trudne i czasochłonne oraz stwarza ryzyko popełnienia błędów. Trudne do przetestowania i trudne do udokumentowania.

OBJECTIVE-C

321

Nawet tak prosty język ja k C może mieć złożoną semantykę — kto jest odpowiedzialny za zarządzanie pamięcią określonej biblioteki?

Tom: Dobre pytanie. Czy nie uważa pan, że aplikacje Microsoft są coraz wolniejsze, ponieważ wadliwie zarządzają pamięcią? Czy kiedykolwiek spotkał pan trzyletni system operacyjny Microsoft i spróbował go pan używać? Osobiście korzystam z laptopa, który jest strefą w olną od program ów Microsoft. To niezwykłe, o ile wydajniejszy jest mój kom puter w porów naniu z kom puteram i innych osób siedzących w tym samym pokoju i posługujących się komputerami z systemami Microsoft. Zanim zdążą otworzyć pierwszy arkusz w Excelu, ja urucham iam kom puter, robię to, co m am do zrobienia, i zamykam aplikację. Jakiej ważnej rady, wynikającej z pańskiego doświadczenia, mógłby pan udzielić?

Tom: Mógłbym sprowadzić ją do kilku słów: w arto popełniać interesujące nowe błędy. Nie m usim y bezmyślnie pow tarzać historii, ale możemy doceniać to, skąd się wywodzimy. Ilu zna pan 25-latków, którzy właśnie ukończyli studia informatyczne, a którzy napisali program w języku APL? Ja nie znam takich i podejrzewam, że prawie takich nie ma. Jest to jednak istotne doświadczenie. W prawdzie to specjalizowany język przeznaczony do tw orzenia specjalnych aplikacji, ale uważam, że doświadczony programista APL, który napisze program statystyczny w APL, pobije każdego, kto spróbuje użyć innego narzędzia. Niedawno rozmawiałem z pewnymi ludźmi z Instytutu Inżynierii Oprogramowania na tem at sposobu szkolenia inżynierów oprogram owania. Rozmawialiśmy o tym, jak niewiele jest program ów studiów, w których podejmuje się sumienne wysiłki, by szkolić studentów w zakresie budowy systemów, a nie projektowania algorytmów. Nie sądzę, abyśmy byli w tym zbyt dobrzy. Czyż nie byłoby doskonale — ja to dziś robię — gdyby podczas zatrudniania menedżera projektu można było poprosić go o potwierdzony rejestr z informacjami o projektach, którym i zarządzał, danym i osób, z którym i m ożna się skontaktować, aby uzyskać szczegółowe informacje na tem at każdego projektu, a także policzalnymi metrykami na tem at każdego z tych projektów. Liczba wierszy kodu, liczba klas, przypadków testowych, dotrzymywanie harm onogram u — informacje tego rodzaju. Tak się składa, że od wielu lat jestem pilotem awionetki. W lotnictwie obowiązują reguły, które powstały w wyniku wniosków wyciągniętych z katastrof. Analiza tych przypadków doprowadziła do powstania reguł obowiązujących w lotnictwie w Stanach Zjednoczonych i na świecie. W Stanach Zjednoczonych każdego dnia i w każdym momencie odbywa się około 56 000 lotów. Zdarzało się, że przez cały rok nie zdarzył się ani jeden wypadek. To niezwykły rekord.

322

ROZDZIAŁ

JEDENASTY

W ciągu lat opracow ano różnorodne reguły: na przykład taką, że pilot, siadając za sterami samolotu, powinien być trzeźwy. Powinien lecieć z pilotem, który wcześniej leciał tym samolotem raz lub dwa razy. Kiedy branża wytwarzania oprogramowania dojrzeje, będziemy dysponowali regułami podobnego rodzaju. Jednym z powodów, dla których w instytucjach poświęca się tak mało czasu na budowanie nowych aplikacji lub nawet reinżynierię istniejących, jest strach. Zakłada się, że każdy rozpoczęty projekt się nie powiedzie. Nikt nie lubi porażek. Gdyby można było mieć pewność, że istnieje 90%, 95% czy też — broń Boże — 99% szans na sukces przedsięwzięcia, a także gdyby można było oszacować koszty projektu, zanim się go rozpocznie, to branża wytwarzania oprogram ow ania byłaby znacznie silniejsza. Czym dla pana jest sukces?

Tom: W rozmowie z panem podawałem różne liczby. Wyobraźmy sobie projekt, który składa się z m iliona lub większej liczby wierszy kodu. W Stanach Zjednoczonych praw dopodobieństw o zakończenia sukcesem takiego projektu jest bardzo niskie — wynosi znacznie poniżej 50%. To dyskusyjne twierdzenie. Nie wiem, skąd ludzie biorą w iarygodne dane na ten tem at, poniew aż nikt nie lubi rozgłaszać takich informacji. Wiele kompetentnych osób starało się poznać te informacje. Ja twierdzę, że osiągnięcie sukcesu w takim projekcie jest trudne, ale nie jest niemożliwe. Po prostu trudne.

Język Objective-C i inne języki Dlaczego pana zespół zdecydował się rozszerzyć istniejący język, zamiast stworzyć nowy?

Brad Cox: Ja byłem dość zadowolony z języka C — no może z wyjątkiem pewnych, dobrze znanych ograniczeń, ale m ożna było z nimi wytrzymać. Tworzenie języka bazowego tylko po to, by wprowadzić obsługę technik programowania obiektowego, byłoby marnotrawstwem czasu. Dlaczego wybrał pan język C?

Brad: Do tego języka mieliśmy dostęp. Ada nie wchodziła w grę. Pascal był uznawany za zabawkę dla badaczy. Pozostawały języki COBOL i FORTRAN. To chyba wszystko wyjaśnia. Ach tak! Był jeszcze Chill (język telefoniczny). Jedyną sensowną alternatywą dla języka C był Smalltalk, ale Xerox by go nie sprzedał. Naszym celem było przeniesienie technik programowania obiektowego z laboratorium do produkcji. Język C był jedyną sensowną opcją.

OBJECTIVE-C

3 23

Dlaczego postanowił pan emulować Smalltalka?

Brad: Olśnił mnie jak objawienie. Wystarczyło zaledwie 15 minut. Przytłoczył mnie jak ciężarówka cegieł. Kiedy podejm ow ałem próby budow ania dużych projektów w języku C, denerwował mnie brak hermetyzacji oraz konieczność opakowywania danych i procedur w bloki, które przypominały mi metody. To wszystko. Kiedy spotkał się pan po raz pierwszy z językiem C + + i co pan o nim wówczas pomyślał?

Brad: Bjarne usłyszał o mojej pracy i zaprosił do Bell Labs, zanim pojaw iły się oba języki. On koncentrował się całkowicie na używaniu statycznego wiązania i na aktualizowaniu języka C. Mnie interesowało wprowadzenie dynamicznego wiązania do starego, prostego C w najprostszej, najm niej kłopotliwej formie, jaką m ożna było sobie wyobrazić. Celem Bjarne’a było stworzenie ambitnego języka: złożonej linii produkcyjnej do tworzenia oprogram ow ania skoncentrowanej na tworzeniu produktów na poziomie bramek. M oim celem było stworzenie czegoś znacznie prostszego: lutow nicy do tw orzenia oprogram ow ania, zdolnej do łączenia programowych układów scalonych tworzonych w czystym C. Czym można wytłumaczyć różnice w popularności obu języków?

Brad: Objective-C był pierwszym produktem małej firmy, która nie miała innych źródeł dochodów niż kompilator i jego biblioteki pomocnicze. Firma AT&T stworzyła język C++ z innych pow odów niż dochody i mogła sobie pozwolić, by go oddać. Wolne atuty zawsze są m ocną bronią. Czyjest pan zaangażowany w projekt ogłoszony przez firmę Apple jako Objective-C 2.0?

Brad: Nie łączy mnie z firmą Apple nic poza tym, że podobają mi się jej produkty. Jaka jest pańska opinia o mechanizmach odśmiecania?

Brad: Uważam je za doskonałe. Zawsze tak uważałem. Musiałem walczyć z osobami od marketingu, którzy uważali je za własność języka, którą można niewielkim kosztem „namalować” na języku C bez wpływu na jego wydajność. Dlaczego w języku Objective-C wielokrotne dziedziczenie jest niedozwolone?

Brad: Historyczny pow ód jest taki: Objective-C to bezpośredni potom ek języka Smalltalk, który również nie obsługuje dziedziczenia. Gdybym dziś jeszcze raz miał podejmować tę decyzję, poszedłbym jeszcze dalej i usunąłbym również pojedyncze dziedziczenie. Dziedziczenie po prostu nie jest aż tak ważne. Najważniejszą zaletą programowania obiektowego jest enkapsulacja Dlaczego język Objective-C nie obsługuje przestrzeni nazw?

Brad: Kiedy ja pracowałem nad projektem, postaw iłem sobie za cel skopiowanie Smalltalka, a tam nie było przestrzeni nazw.

324

ROZDZIAŁ

JEDENASTY

Język, który dziś zna pan jako Objective-C, jest bardziej produktem firmy Apple niż moim. Większość mojej pracy jest dziś w języku XML i Javie. Czy pojęcie protokołów było unikatowe dla języka Objective-C?

Brad: C hciałbym mieć praw o przypisyw ania sobie takiej zasługi. To był jeden z pomysłów dodanych do prostego szkieletu języka Objective-C — rozumiem przez to najmniejszy zbiór własności pobrany z języka Smalltalk. W Smalltalku nie istniało w tedy coś takiego, jak protokół. Pojęcie to zostało dodane przez Steve’a Naroffa, który dziś jest odpowiedzialny za język Objective-C w firmie Apple. O ile dobrze pamiętam, przejął je z języka SAIL. Wydaje się, że pański projekt m iał wpływ na język Java, ponieważ w Javie wprowadzono pojedyncze dziedziczenie. Czy pojedyncze dziedziczenie można by usunąć takżezjavy?

Brad: Prawdopodobnie tak. Ale tak się nie stanie i nie powinno się stać. Własność jest dostępna w języku i działa tak, jak powinna. Można ją wykorzystywać nieprawidłowo, jak każdą własność języka, i nie jest tak istotna jak enkapsulacja. Początkowo bardzo często korzystałem z dziedziczenia. Eksperymentowałem w celu znalezienia granic jego możliwości. Następnie zdałem sobie sprawę, że największą zaletą technik obiektowych jest enkapsulacja oraz to, że m ożna ją wykorzystać do osiągnięcia prawie wszystkiego, do czego używałem dziedziczenia, ale w bardziej czytelny sposób. Odtąd zacząłem interesować się obiektami o większym poziomie rozdrobnienia (techniki OOP razem z JBI/SCA), w których dziedziczenie w ogóle nie występuje. W ja k i sposób podejmuje pan decyzję o dodaniu własności do projektu? Na przykład mechanizm odśmiecania może spowolnić niektóre aplikacje w języku C, ale oferuje wiele zalet.

Brad: Całkowicie się z panem zgadzam. W rzeczywistości stworzyliśmy mechanizm odśmiecania w Stepstone. Był podobny do tego, który obecnie jest wykorzystywany w firmie Apple. Miał nawet interpreter języka Objective-C. Specjaliści od marketingu chcieli jednak mechanizmu bardziej automatycznego — czegoś, co byłoby w stanie konkurow ać łeb w łeb ze Smalltalkiem, a nie czegoś przypominającego tak bardzo język C. Czy je st pan zwolennikiem ustawień domyślnych i ograniczonych możliwości konfiguracji?

Brad: Mogliśmy używać takich mechanizmów i ich używaliśmy. Ja byłem jedynie przeciwnikiem naginania języka C do listy życzeń specjalistów od marketingu.

OBJECTIVE-C

325

Niektórzy projektanci języków programowania rozpoczynają tworzenie języka od niewielkiego, sformalizowanego rdzenia. Na jego bazie budują pozostałe elementy. Czy w ten sposób postępował pan w przypadku języka Objective-C, czy też zdecydował pan zapożyczyć mechanizmy języków Smalltalk i C?

Brad: Z całą pewnością nie wychodziłem od formalnej definicji. Myślałem o krzemie. Intensywnie konsultow aliśm y to z przedstawicielami producentów układów i odwiedzaliśmy ich fabryki. Starałem się odzwierciedlić to, co robili oni, w tym, co my robiliśmy. Zauważyłem, że wszystko, co robili, opierało się na wykorzystywaniu krzemowych komponentów. Wszyscy myśleli długo i intensywnie o komponentach, a zupełnie nie zwracali uwagi na lutownice. Dla nas, co oczywiste, język jest lutownicą. Widziałem, że wszyscy koncentrują się na językach, czyli lutow nicach, a nikt nie koncentruje się na komponentach. Takie myślenie wydawało mi się nieprawidłowe. Okazało się, że perspektywa, z której patrzyli producenci układów, jest bardzo ważna, poniew aż kom ponenty krzemowe są zbudow ane z atom ów oraz istnieje model biznesowy ich kupow ania i sprzedawania. Ten model biznesowy w przypadku kom ponentów programowych jest bardzo ulotny. Samo oprogramowanie jest ulotne.

Brad: To praw da, ale dlatego właśnie koncentrujem y się na językach, a nie na komponentach. W zasadzie nie mamy kom ponentów w żadnym poważnym sensie. Jeśli porów nać tę sytuację do budowy domów, to tak, jakbyśmy byli z powrotem w epoce jaskiń, gdy budowa dom u polegała na znalezieniu stosu monolitycznego materiału — na przykład pyłu wulkanicznego. Obawiałem się, że to doprowadzi nas do m echanizm u ładow ania klas Javy. Sposób budow y jaskini był następujący: rozpoczynamy od stosu materiału, usuwamy to, co jest niepotrzebne. Tak właściwie działa model m echanizm u ładow ania klas. Jeśli chodzi o mnie, to teraz skupiam swoją energię na niewielkich kom ponentach — zaczyna się od niczego, a następnie dodaje to, co jest potrzebne. Tak właśnie buduje się domy dziś. Biologię lub chemię opisujemy w języku naturalnym. Być może problem polega na tym, że języki programowania, których używamy, nie mają takich możliwości ja k język naturalny?

Brad: Gdyby kom putery były równie inteligentne jak ludzie, byłbym przekonany do takiego podejścia. Mówimy o czymś, co jest głupie jak cegła — o komputerach, które nie zmieniły się w żaden zasadniczy sposób od czasu, kiedy zacząłem się nimi posługiwać, czyli od lat siedemdziesiątych. Z drugiej strony istnieją dziś nowe, fantastyczne języki programowania — na przykład języki funkcyjne — które m ogą pom óc w program ow aniu takich urządzeń, jak kom putery wielordzeniowe. Coraz częściej słyszę, że zastosowanie języków funkcyjnych będzie pomocne. Nie m a powodów, by w to wątpić.

326

ROZDZIAŁ

JEDENASTY

W ja k i sposób koncentracja na współbieżności wpływa na paradygmat obiektowy? Czy podejście obiektowe wymaga wprowadzenia zmian?

Brad: Mimo że mechanizmy obiektowe zostały odziedziczone z języka Simula, zawsze uważałem, że języki program ow ania pow inny oferować obsługę wątków , które wystarczają do tego, by obsłużyć tworzenie komponentów wyższego poziomu, i które wykazują własności współbieżne w sposób ograniczony, ale możliwy do kontrolowania. Na przykład filtry Uniksa obsługują współbieżność im plem entow aną w czystym języku C w zarządzany sposób. Ja poświęciłem sporo czasu na zastosowanie podobnego podejścia dla języka Objective-C: biblioteki obsługi wielozadaniowości o nazwie TaskMaster bazującej na mechanizmie setjmp(). Innym przykładem jest język HLA (opracowany przez Wojskowe Biuro Modelowania i Symulacji — Defense Modeling and Simulation Office), który jest powszechnie wykorzystywany do symulacji w zastosow aniach wojskowych. Język ten zaim plem entow ano w kilku językach — dwa z nich to C++ i Java. Język obsługuje model współbieżności sterowany zdarzeniami, który — o ile wiem — nie wykorzystuje obsługi wątków z żadnego języka. Ostatni przykład dotyczy języka, w który obecnie jestem najbardziej zaangażowany. SOA obsługuje rozbudowane rezydujące w sieci obiekty, które są wewnętrznie ze sobą współbieżne, ponieważ zwykle rezydują na różnych maszynach. Technologie JBI firmy Sun oraz SCA firmy OASIS ulepszają ten model poprzez dołożenie dokładniejszych komponentów, które wykorzystuje się do budowania obiektów SOA. Jest to pierwszy sygnał nadejścia ery podejścia granularnego w konstrukcji oprogramowania — podejście to jest norm ą w inżynierii sprzętu: najdrobniejsze elementy (bramki) są łączone w elementy średniej wielkości (układy — podobne do słynnych programowych układów scalonych), a te ostatecznie są łączone w jeszcze większe obiekty (karty) itd. Główna różnica polega na tym, że w materialnych dziedzinach system ma strukturę rzeczywiście m odułow ą i składa się ze znacznie większej liczby poziom ów niż te, które wymieniliśmy. My mamy tylko te trzy. Dziś. Z całą pewnością istnieją aplikacje, które wymagają ściślejszej integracji. Kiedy tworzyłem język, takiego problem u po prostu nie było. Ten brak był przynajmniej częściowo spowodowany moim własnym brakiem umiejętności zarządzania wysoce w spółbieżnych systemów oraz tym, że w ątpiłem w to, czy ktokolwiek napraw dę potrafi nimi zarządzać. Wydaje się, że złożoność i rozmiary aplikacji coraz bardziej rosną. Czy stosowanie technik obiektowych pomaga, czy szkodzi? Z mojego doświadczenia wynika, że idea tworzenia obiektów wielokrotnego użytku dodaje złożoności i podwaja nakłady pracy. Najpierw trzeba napisać obiekt nadający się do wielokrotnego użytku. Następnie trzeba go zmodyfikować i dopasować coś innego, co można zamontować w tym samym gnieżdzie, które ten obiekt pozostawił.

OBJECTIVE-C

3 27

Brad: Ma pan rację — jeżeli przez techniki obiektowe rozumie pan styl enkapsulacji Objective-C/Java, który ja w mojej drugiej książce Superdistribution [Addison-Wesley Professional] określiłem jako integrację na poziomie układów. Z kolei nie ma pan racji, jeśli integrację na poziomie układów uzna pan za jeden poziom w wielopoziomowym zestawie narzędzi integracyjnych — podejściu, które jest normą w inżynierii sprzętowej, gdzie obiekty poziomu bramek współistnieją w doskonałej harmonii w modularnym świecie bramek, układów, kart i opcji enkapsulacji wyższego poziomu. W łaśnie z tego pow odu koncentruję się dziś na technologiach SOA (poziom płyty głównej) oraz JBI (poziom magistrali). Technologie te obsługują enkapsulację w sposób zbliżony do tradycyjnych technik obiektowych. Co więcej, pozwalają one na enkapsulację nie tylko danych+procedur, ale także całego w ątku sterowania. To sprawia, że mają one jeszcze większe możliwości. Najlepsze jest to, że wielopoziomowa integracja nic nie kosztuje. Przy tym wiadomo, że sprawdza się w dowolnej skali w innych branżach. Jedynym problemem jest trudność komunikacji ze zwolennikami jednopoziomowych technik obiektowych. Skądś to znam — najpierw był konflikt pomiędzy zwolennikam i program ow ania obiektowego a zwolennikami tradycyjnego programowania proceduralnego, a dziś są zwolennicy SOA, JBI oraz Javy. Wiele osób wierzy w mit, że nowe technologie wypierają stare. Tak nigdy nie było i nigdy nie będzie. Nowe technologie zawsze są tworzone na bazie starych. Wydaje się, że wchodzimy w nową erę eksperymentowania z językami, a programiści m ają ochotę próbowania paradygm atów, do których nie są przyzwyczajeni — na przykład Rails oraz programowania funkcyjnego. Jakich lekcji — na podstawie swoich doświadczeń z językiem Objective-C — chciałby pan udzielić projektantom języków aktualizującym swoje produkty?

Brad: Próbowałem zapoznać się ze składnią takich języków, jak Haskell. Niestety nie na tyle skutecznie, abym miał na ich temat jakieś konkretne zdanie. Często korzystam z XQuery, który jest językiem funkcyjnym. Uważam, że XQuery jest znacznie bardziej przyjazny, gdy się go czyta, niż XSLT. Myślę, że jestem w stanie stosować nowe technologie, ale jeśli chodzi o Haskella, to nie potrafię nauczyć się jego składni. Podobny problem miałem z językiem Lisp. Jestem pod wrażeniem projektu Navy, w którym złożone strategie autoryzacji i uwierzytelniania wyrażono za pomocą reguł Haskella łatwych do udowodnienia za pomocą oględzin (ang. by inspection). M oim zdaniem w przyszłości nie pow inno się m nożyć coraz to now ych notacji służących do wykonywania dokładnie tych samych czynności, które wykonujemy od dziesięcioleci — pisania kodu proceduralnego. Nie chcę przez to powiedzieć, że praca na tym poziomie nie jest ważna. To etap, w którego w yniku pow stają

328

ROZDZIAŁ

JEDENASTY

kom ponenty wyższego poziomu. Wydaje się, że to się nie zmieni. Mówię jedynie, że nowe sposoby pisania kodu proceduralnego nie dadzą początku innowacjom. Moim zdaniem ekscytujące jest w prowadzanie nowych notacji do wykonywania całkowicie now ych rzeczy. Dokładniej: tw orzenia systemów wyższego poziom u na podstawie bibliotek istniejących komponentów. Specyficznym przykładem jest język BPEL, który można stosować na dwóch poziomach integracji: SOA dla największych obiektów oraz JBI (Sun) lub SCA (OASYS). W arto przyjrzeć się edytorowi BPEL w projekcie NetBeans. Grupa OMG, stosując architekturę sterow aną m odelem, wykonała świetną robotę w tej dziedzinie.

Składniki, piasek i cegły Jakie wnioski z lekcji na tem at powstania, rozwoju i przystosowania się języka Objective-C do warunków współczesnych mogą wyciągnąć programiści, którzy tworzą systemy komputerowe dziś oraz będą je tworzyć w najbliższej przyszłości?

Brad: Od zawsze interesuje mnie wielopoziomowa integracja (znana również jako enkapsulacja). Zastanawia mnie to, w jaki sposób można tak podzielić pracę, aby można ją było przydzielić specjalistom. Dzięki tem u m ożna by używać produktów pracy specjalistów bez konieczności naruszania hermetyczności. Nie trzeba zaglądać do środka, aby skutecznie korzystać z produktów. W roli przykładu użyłem kiedyś zwykłego, drewnianego ołówka. Kiedy zapytałem słuchaczy, co jest prostsze — cyfrowy ołówek, taki jak Microsoft Word, czy drewniany — wszyscy zgodzili się ze mną, że ten drewniany. Kiedy powiedziałem, że Microsoft W ord napisało ośmiu programistów, natomiast w produkcję drewnianej odmiany są zaangażowane tysiące osób, żaden z moich słuchaczy nie potrafił docenić pełnej złożoności takich czynności, jak wycinanie drewna, wydobywanie grafitu, wytapianie metalu, produkcji lakieru, uprawy roślin oleistych potrzebnych do wyprodukowania farby itd. W ołówku są zaszyte złożone procesy, ale zostały one ukryte przed użytkownikiem. Moja największa uwaga do języka C (oraz podobnych m u języków) jest taka: cała złożoność stoi otworem przed programistami. Wyjątkiem jest niewielka część złożoności zamknięta w funkcjach. Jedyna rzeczywista granica hermetyzacji to cała przestrzeń procesów. Jest to niezwykle skuteczna wysokopoziom ow a kapsuła intensywnie wykorzystywana w Uniksie w postaci dw upoziomowej hermetyzacji bazującej na skryptach powłoki, potokach i filtrach. Podczas prób wyjaśniania zalet programowania obiektowego często używałem terminu „program ow y układ scalony” . W ten sposób odnosiłem się do nowego poziom u integracji, elem entów większych od funkcji C (poziom bramek) i mniejszych od programów Uniksa (poziom kart). Moją główną motywacją podczas definiowania języka Objective-C było stworzenie prostej programowej „lutownicy” do m ontażu

OBJECTIVE-C

329

większych m odułów funkcjonalności (poziom płyty) z program ow ych układów scalonych wielokrotnego użytku. Dla odróżnienia język C++ wywodzi się z całkowicie odmiennej wizji: dążenia do stworzenia dużej zautomatyzowanej fabryki zdolnej do produkcji gęsto upakowanych komponentów złożonych z modułów na poziomie bramek. Gdybyśmy chcieli zinterpretować tę metaforę sprzętową dla programowania, doszlibyśmy do wniosku, że integracja na poziomie układów następuje głównie w czasie linkowania, natomiast integracja na poziomie bramek w czasie kompilacji. W tamtych czasach (środek lat osiemdziesiątych) lekkie wątki nie były powszechnie znane. Nie było niczego mniejszego od procesów w stylu Uniksa (ciężkie wątki). Poświęciłem wtedy trochę czasu na zbudowanie biblioteki języka Objective-C o nazwie Taskmaster. Biblioteka ta dostarczała lekkich wątków jako podstawy do integracji na poziomie kart. Powstanie kom puterów o architekturze RISC było końcem wykorzystywania wątków do integracji na poziomie kart. Takie niskopoziomowe „lutowanie” po prostu utrudniało przenośność. Wielką zmianą od tamtej pory jest powszechność sieci. O tw orzyło to nowe poziom y integracji — znacznie szersze od przestrzeni procesów Uniksa. Na przykład w architekturze zorientowanej na usługi (ang. Service-Oriented Architecture — SOA) internet można porównać do okablowania w ew nątrz kom ponentów systemu HiFi. Zawiera usługi (programy) na osobnych serwerach. Serwery te same spełniają rolę komponentów. Według mnie to niezwykle ekscytujące, ponieważ jest to pierwszy poziom integracji aproksymujący separację aspektów które w codziennym życiu bierzemy za pewnik. Nie ma sposobu ani potrzeby przeglądania kodu źródłowego każdej używanej usługi, ponieważ usługa ta działa na zdalnym serwerze należącym do kogoś innego. W ażne jest także, że jest to pierwszy poziom integracji pozwalający na rozwiązanie fatalnej w ady idei program ow ych układów scalonych: dostarcza bowiem innym producentom bodźca do produkow ania kom ponentów . Ołówki (oraz wiele kom ponentów pomocniczych wykorzystywanych do ich produkcji) produkuje się dlatego, że dla środków trw ałych działa zasada zachow ania masy. W świecie przedmiotów cyfrowych nie istniała porównywalna zasada do czasu, kiedy architektura SOA umożliwiła tworzenie użytecznych usług i pozwoliła na pobieranie opłat za ich używanie. W ciągu ostatnich dwóch lat poznałem zasadnicze problemy związane z architekturą SOA. Obecnie koncentruję się głównie na tym. W dużych instalacjach SOA (na przykład NCES Agencji DISA oraz systemie FCS używanym w armii) istnieje silny opór przed osiągnięciem konsensusu potrzebnego do zbudow ania w pełni hom ogenicznych systemów SOA wykorzystujących zgodne mechanizmy komunikacyjne dla pojazdów poruszających się po lądzie, morzu, pod wodą i w powietrzu (nie wspominam nawet o zgodnych definicjach poufności, integralności, niezaprzeczalności itp.). Nawet jeśli korporacja rozszerzyłaby się do tego stopnia, że objęłaby wszystkie systemy wykorzystywane w Departamencie Obrony, to co zrobić z systemami sojuszników? Co ze zgodnością z innymi agencjami rządowymi? Co z innymi państwami? Można

330

ROZDZIAŁ

JEDENASTY

by zastosować regułę pierwszej linii obrony. Niezależnie od sposobu zdefiniowania korporacji należałoby pozostawić coś, co pewnego dnia może się przydać do komunikacji. Zrzucanie odpowiedzialności za wszystkie te działania na każdego z projektantów usług po prostu nie daje gwarancji skalowalności. W związku z tym obecnie analizuję technologię JBI (ang. Java Business Integration) firmy Sun oraz jej następcę, technologię SCA (ang. Software Component Architecture), jako wydajniejszy sposób obsługi niższego poziomu integracji niż ten, który można wykorzystać do budowania usług SOA. Czy do zbudowania takiego systemu potrzebujemy specjalnego sprzętu?

Brad: Sprzęt komputerowy to jeden z wielu przykładów doskonałości inżynieryjnej w 200 lat od rozpoczęcia rewolucji przemysłowej. Ludzie osiągnęli niezwykle wysoki poziom w budow aniu sprzętu. W związku z tym sprzęt może posłużyć jako model, z którego wiele się nauczymy. Nie istnieją jednak żadne proste lekcje, żadne przyciski, które można by nacisnąć w celu poprawy jakości. Ostatnio użyłem innego przykładu w celu zilustrowania tego, co mam na myśli, gdy mówiłem, że oprogramowanie jest komponentem pierwotnym. Weźmy pod uwagę budownictwo. Ludzie budują domy od tysięcy lat. W niektórych miejscach robią to w pierwotny, prosty sposób: ekipa budowlana nakłada glinę do form, aby wykonać własne cegły, które następnie posłużą do budowy domu. Pomimo postępu technologii systemy oprogramowania tworzy się mniej więcej w taki sam sposób. Na przykład w celu stworzenia usługi SOA każdy zespół projektowy zaczyna od pobrania m ateriałów z kam ieniołom u java.net. Są one podstawowym surowcem do budowy dowolnej usługi SOA. Rozważmy dla przykładu zabezpieczenia — w gruncie rzeczy m echanizm y te gw arantują to samo, co zapewniają ściany domu. W D epartam encie O brony obowiązują niezwykle ścisłe wymagania bezpieczeństwa uregulowane przez politykę bezpieczeństwa. Zainwestowano również bardzo wiele w standardy bezpieczeństwa — na przykład zestaw WS-Security. O statnio w drożono procesy Certyfikacji i Akredytacji (C&A), przez które muszą przejść wszystkie usługi sieciowe. C&A to pracochłonny i kosztowny proces. Musi przez niego przejść niezależnie od siebie każda usługa sieciowa. Dzięki temu uzyskuje się pewność, że usługa ta jest wystarczająco bezpieczna do zainstalowania we wrażliwych sieciach. Usługi sieciowe są jak domy z cegieł. Strategie i standardy sprowadzają się do instrukcji, których musi przestrzegać każdy projektant podczas tw orzenia własnych cegieł. Tym cegłom w dalszym ciągu nie mogą jednak ufać inni, ponieważ ich jakość zależy od osób, które je wyprodukowały. Czy podczas ich produkcji były przestrzegane standardy? Czy zostały prawidłowo zaimplementowane? Cegły są co prawda za darmo, ale nie m ożna im ufać, poniew aż ich jakość jest znana tylko zespołowi, który je wyprodukował.

OBJECTIVE-C

331

Branża budow lana od dawna stosuje stosunkowo zaawansowaną formę produkcji, którą dziś bierzemy za pewnik. Brygady budowlane już nie produkują własnych cegieł, ponieważ nikt by im nie zaufał. Standardy i strategie pozostają w mocy, podobnie zresztą jak procesy C&A (branżowe laboratoria do testowania cegieł). Niewielu z nas widzi jednak, że te procesy zachodzą. M ożemy polegać na tych niewidzialnych procesach, aby uzyskać pewność, że każda cegła na rynku zachowa swoje właściwości i utrzyma dach. Ten ewolucyjny proces przechodzenia od glinianych do fabrycznie wytwarzanych cegieł nie był nawet ściśle techniczny. Polegał przede wszystkim na budowaniu zaufania. Dorośliśmy do tego, by zrozumieć, że istnieją niezależne standardy definiujące, jakie warunki powinna spełniać cegła, jak trwała powinna być oraz w jaki sposób powinna reagować na warunki atmosferyczne. Istnieją niezależne laboratoria testowe, którym niejawnie ufamy. Większość z nas nie zdaje sobie sprawy z istnienia laboratoriów testujących jakość cegieł — nie musimy o nich wiedzieć. Wiemy, że można ufać cegłom, i to jest wszystko, czego potrzebujemy! To pozwala nam dowieść, że systemy ekonomiczne są ważne. Zaufane cegły istnieją, ponieważ istnieje system zachęcający do ich dostarczania: zapłata za cegły. Wszyscy oprócz wąskiej grupy specjalistów produkujących cegły mogą zapomnieć o złożoności ich wytwarzania i przejść do bardziej twórczej części budowy domów. Więcej informacji na tem at różnic pom iędzy architekturą bazującą na cegłach z gliny a architekturą opartą na cegłach prawdziwych można znaleźć pod adresem: http://bradjcox.blogspot.com/. Aby zakończyć tę wypowiedź nieco bardziej optym istycznym akcentem, w arto zauważyć, że sytuacja zmienia się na lepsze. Powstało kilka firm, które zaczęły produkować kom ponenty zabezpieczeń SOA. Jest to początek długiej drogi, która prowadzi do uznania ich przez D epartam ent O brony za kom ponenty zaufane. Najbardziej dojrzałym przykładem, jaki znam, jest OpenSSL. Jednak takie firmy, jak Sun, Boeing i kilka innych, podjęły ostatnio inne inicjatywy zmierzające do wyprodukowania zabezpieczeń SOA. Czy enkapsulacja i separacja problemów to najważniejsze aspekty wytwarzania oprogramowania?

Brad: Tak sądzę. W głównej mierze bazuje to na sposobie zarządzania złożonością w innych branżach. W ydaje się, że użycie enkapsulacji do walki ze złożonością jest charakterystycznym sposobem działania ludzi. Czy architektura SOA i pańskie inicjatywy stanowią próby komponentyzacji oprogramowania?

Brad: Tak. To jest właśnie cel, do którego dążę od początku mojej kariery.

332

ROZDZIAŁ

JEDENASTY

Czy chodzi o to — o czym zawsze pan myślał czy też co pan zauważył — że popularne podejścia do tworzenia oprogramowania nie sprawdzają się w rzeczywistym świecie? Brad: Kiedy zdałem sobie z tego sprawę, zacząłem się przyglądać, jak inni radzą sobie

z tym problemem. Najpierw dotarło do mnie, że większość oprogramowania działa wadliwie. Czy praktyczne podejście bazujące na dużych komponentach pozostaje w sprzeczności ze ściśle matematycznym spojrzeniem na oprogramowanie? Brad: Prawdopodobnie tak. Nie chcę skupiać się na krytyce informatyki. Zauważyłem

tylko, że problemy w informatyce nie są rozwiązywane w sposób, w jaki ludzie zwykle rozwiązują problemy. Wynika to z charakterystycznych różnic w punkcie widzenia. Specjaliści informatycy wychodzą z założenia, że istnieje nauka o oprogramowaniu. Ich celem jest jej udokum entow anie i nauczanie. Zawsze wyrażałem pogląd, że nie istnieje coś takiego. Istnieje nauka o budowie domów oraz nauka o produkcji układów elektronicznych. Naszym zadaniem jest nauczenie się tego, czego uczono się od tysięcy lat, i rozpoczęcie tw orzenia nauki o oprogram ow aniu. Dla m nie szklanka jest całkowicie pusta, dla wielu informatyków jej zawartość jest godna uwagi. Wspomniał pan kilka razy, że załam ał się ekonomiczny model oprogramowania. B rad: Nie uważam, że się załamał. To stwierdzenie oznaczałoby, że istniał model,

który teraz podlega dekompozycji. Nigdy nie było takiego modelu. Nie m ożna go stworzyć, ponieważ oprogramowanie rozprzestrzenia się jak mgła. Ludzie nie wymyślili jeszcze sposobu zbudowania zasad ekonomicznych rządzących oprogramowaniem. W przypadku usług SOA m ożna sobie wyobrazić model ekonomiczny, ponieważ oprogramowanie jest przywiązane do jakiegoś odległego serwera i istnieje możliwość naliczania opłat za dostęp do tego serwera. Jeśli chodzi o obiekty o niskiej ziarnistości — miałem nadzieję, że istnieje sposób obsługi niewielkich detali: obiektów programowych będących odpowiednikami piasku i żwiru w budownictwie, czyli obiektów java.net. Okazało się jednak, że złożoność tworzenia rozwiązań przez ludzi jest przytłaczająca. Myślę, że nadzieją dla nas są obiekty średniego poziomu. Jest nadzieja, że rolę tę spełni architektura SOA. Być może uda się również zejść o jeden poziom niżej. Świat w dalszym ciągu nad tym pracuje. Myślę jednak, że nie ma nadziei na powstanie obiektów o naprawdę małej ziarnistości. Czy ten pośredni poziom to poziom frameworków? Brad: Są to większe obiekty. Monolityczne komponenty — rozpoczynamy od czegoś

dużego i włączamy potrzebne funkcje. Myślę, że istnieje dla nich model. Przykładem może być technologia JBoss. Całkowicie odrzucam y bity i sprzedajemy zaufanie. To bardzo skomplikowane.

OBJECTIVE-C

3 33

Co oznacza zaufanie w kontekście oprogramowania?

Brad: Cóż, D epartam ent Obrony potrafi odpowiedzieć na to pytanie. To bardzo pracochłonny i niezwykle kosztowny proces, w pewnych przypadkach niezadowalający, ale pozwalający na udzielenie odpowiedzi. Departament Obrony jednak tak bardzo skupił się na architekturze SOA, że nie zwrócił uwagi na to, że sama architektura SOA jest niewystarczająca. Istnieje również zapotrzebow anie na mniejsze kom ponenty. Takie, które m ożna wykorzystać do rozwiązywania powtarzających się problemów. Sama architektura SOA do tego nie wystarczy. Chodzi na przykład o kom ponenty umożliwiające zapewnienie bezpieczeństwa i interoperacyjności. Celem mojej obecnej pracy jest zatem wydzielenie podzespołów usług SOA pozwalających na realizację nudnych, pow tarzających się zadań budow ania serwisów SOA. M am nadzieję, że pewnego dnia uda mi się przeprowadzić te gliniane cegły przez procesy C&A, dzięki czemu staną się one prawdziwymi cegłami — takimi, którym można ufać tak, jak ufamy cegłom w naszych domach. Przykładem m echanizm u, który trzeba zastosować w każdej aplikacji SOA wykorzystywanej przez Departament Obrony, są zabezpieczenia. To wymaganie wynika ze stosowanej strategii. Twórca aplikacji SOA musi jedynie przestrzegać standardów i strategii. D epartam ent O brony nie ma dostawcy cegieł. Nie istnieją zaufane kom ponenty — prawdziwe cegły, jeśli ktoś woli — które można by wziąć czy kupić i używać bez konieczności pełnej znajomości wszystkich standardów SOA, polityki bezpieczeństwa, szczegółów używania kom ponentów java.net. Słowem wszystkiego, co jest potrzebne do spełnienia wymagań bezpieczeństwa Departamentu Obrony. Jak wyglądają te różne poziomy abstrakcji z punktu widzenia bezpieczeństwa?

Brad: Nie jestem pewien, czy dobrze rozumiem pańskie pytanie. To tak, jakby zapytać: „W jaki sposób specjalizacja pracy w branży motoryzacyjnej wpływa na bezpieczeństwo jazdy?” . Uważam, że w przypadku specjalizacji każdy produkt zależy od większej liczby osób, a pewna część tych osób to ludzie o złych zamiarach. Jednak produkty tych ludzi na każdym poziomie pow inny być poddawane testom akceptacji. W ten sposób istnieje mniejsze prawdopodobieństwo przedostania się złych efektów przez monolityczną konstrukcję. W arto zwrócić uwagę, że wielopoziomowa integracja nie oznacza wielu poziomów abstrakcji. W rzeczywistości jest to wielopoziomowa konkretyzacja: kom ponenty wyższego poziom u są m ontow ane z gotowych, przetestow anych kom ponentów wielokrotnego użytku. Dla przykładu rozważmy bezpieczne usługi SOA montowane z podzespołów JBI poprzez opakow anie zasadniczej funkcjonalności usług SOA w kom ponenty JBI. Każdy z tych kom ponentów dostarcza atrybutów zabezpieczeń SOA: uwierzytelniania, autoryzacji, poufności, niezaprzeczalności, integralności itp. W efekcie uzyskujemy większe bezpieczeństwo, po prostu dlatego, że możemy sobie pozwolić na dobre wykonanie zabezpieczeń.

334

ROZDZIAŁ

JEDENASTY

Czy istnieją rozwiązania, które pozostali z nas mogą uznać za satysfakcjonujące?

Brad: Tak sądzę. Popatrzmy na różnice pomiędzy cegłami glinianymi a prawdziwymi. Dlaczego nie buduje się dom ów z cegieł glinianych? Okazuje się, że nie m ożna im ufać. Podobnie jest z oprogram ow aniem . Jakość cegły glinianej zależy w całości od tego, kto ją tworzył, oraz od umiejętności budowniczego. Zignorujmy na chwilę techniczne różnice pomiędzy prawdziwymi cegłami a cegłami z gliny. Podstawowa różnica, poza koniecznością wypalania prawdziwych cegieł w wysokiej temperaturze, polega na tym, że prawdziwe cegły są poddaw ane testom w laboratoriach certyfikujących. Dostawca prawdziwych cegieł musi przejść przez gęsty labirynt. Tak w uproszczeniu wygląda model zaufania w ykorzystywany w Departam encie Obrony. Certyfikacja i autoryzacja są w istocie bardzo pracochłonnymi procedurami testowymi. Określa się je między innymi terminem „wspólne kryteria” (ang. common criteria). Obecnie są one stosowane jako norma. Prawdopodobnie jest to miejsce, do którego dotarliśmy po kilku tysiącach lat narzekań na cegły z gliny. „My” oznacza w tym przypadku branżę jako całość. Obecnie można usłyszeć narzekania na bezpieczeństwo oprogram ow ania i nie ma zbyt wielu rozwiązań tego problem u. Ludzie nie ufają oprogram owaniu. Nigdy nie będą m u ufać. Zaufanie do oprogram ow ania zależy od tego, czy darzymy zaufaniem producentów oprogramowania. Przyjrzyjmy się firmie Sun. Firma ta stara się stworzyć zupełnie nowy model biznesowy. O statnio zaczęto stosować w tej firmie podejście właściwe dla oprogram ow ania open source. Sytuację w firmie Sun dobrze ilustruje analogia, o której wspominałem wcześniej: stary model biznesowy polegający na sprzedaży bitów i now y model obejmujący sprzedaż zaufania. Myślę, że w firmie Sun dobrze zrozum iano tę ideę. To pokazuje dokładnie, w jaki sposób teraz tam postępują: bity są za darmo. Prawie każde oprogram ow anie, które tworzy firma Sun, m ożna pobrać bez opłat i wykorzystywać w dowolny sposób. Zakłada się jednak, że ludzie, jeśli będą mieć wybór, będą woleli kupić bity razem z rekompensatą w postaci wsparcia i pewnych innych rzeczy, o których tu nie będę mówił. Czas pokaże, czy to podejście się sprawdzi. Myślę jednak, że istnieje na to realna szansa. Prawdopodobnie słyszał pan o problemach, ja kie m iał ostatnio stan Kalifornia z systemami napisanymi w COBOL-u. Czy posiadanie systemu zbudowanego z niewielkich cegieł pomoże nam w przyszłości uniknąć problemów z oprogramowaniem odziedziczonym?

Brad: Bardzo mocno wierzę w komponenty, ale nie chcę przeceniać tego mechanizmu — kom ponenty nie rozwiążą wszystkich problemów. Odzwierciedlają za to sposób, w jaki ludzie rozwiązują problemy o ponadprzeciętnej skali. To jedna z cech, które odróżniają nas od szympansów. Ludzie wynaleźli sposób rozwiązywania problemów polegający na przekazywaniu problemów innym ludziom. Nazywa się to specjalizacją pracy. To bardzo proste. Pod tym względem ludzie różnią się od szympansów: one

OBJECTIVE-C

335

nigdy tego nie wynalazły. Wiedzą, jak robi się narzędzia, mają swój język. Pod względem większości oczywistych cech nie m a różnic pom iędzy ludźmi a szympansami. My odkryliśmy jednak sposób rozwiązywania problemów polegający na powierzaniu ich innym ludziom — zrobiliśmy to dzięki systemowi ekonomicznemu. Jeśli znajdujemy rozwiązanie problemu, czy jest to coś, co bazuje na przeszłości, czy coś całkowicie nowego?

Brad: Ewolucja jest pow olną rewolucją. Nie ma pomiędzy nimi binarnej różnicy. We w spom nianym przez pana artykule na tem at COBOL-a napisano również, że najnowszy standard tego języka obsługuje rozszerzenia obiektowe. Dziś nie śledzę, co dzieje się z COBOL-em, ale w latach osiemdziesiątych byłem zwolennikiem następującego podejścia: weź język COBOL i dodaj do niego własności obiektowe — podobnie zrobiłem w przypadku języka C. W jaki sposób odbywa się rozwój? Oto przykład. Nie wyrzucono COBOL-a ani nie zastąpiono go nowym językiem. Dodano do niego tylko to, czego brakowało, i w dalszym ciągu się go wykorzystuje. Czy nadal uważa pan, że superdystrybucja jest właściwym sposobem rozprowadzania produktów cyfrowych? A co z aplikacjami internetowymi?

Brad: Superdystrybucja, w mojej interpretacji, dotyczy obiektów drobnoziarnistych. W przypadku większych obiektów, na przykład serwisów SOA, sprawdzają się prostsze metody. Uważam, że potrzebne są właściwe bodźce. Moje podejście łatwiej jednak zastosować obecnie niż wtedy, gdy istniały tylko obiekty skali OOP — w szczególności mam tu na myśli usługi SOA. Kiedyś one nie istniały (były tylko tzw. cienkie klienty). Problem super dystrybucji można porównać do stwierdzenia, że nauka życia w zgodzie jest rozwiązaniem konfliktu palestyńskiego. Jest to oczywiście prawda, ponieważ rozwiązanie to było wykorzystywane i okazało się skuteczne w Stanach Zjednoczonych czy Afryce Południowej. Ale porównanie to jest kompletnie nie na miejscu, gdy mowa o wojnie pomiędzy właścicielami a użytkownikami dóbr cyfrowych. Żadna ze stron nie chce przyjąć kom prom isu i po prostu nauczyć się żyć w zgodzie. Zapewnianie, jakoby superdystrybucja była rozwiązaniem takiego konfliktu, może spowodować jedynie to, że staniemy się obiektem ataku z obu stron konfliktu. Czy zgodnie ze stosowaną przez pana analogią do branży budowlanej wszechobecność sieci jest warunkiem wstępnym budowania miast?

Brad: Jeśli usługi SOA są dom am i, to sieci z pew nością są w arunkiem wstępnym tworzenia miast. Usługa SOA nie może istnieć bez sieci. W praktyce jednak bez sieci nie da się nawet zbudować szałasu.

336

ROZDZIAŁ

JEDENASTY

Robin M ilner, twórca języka ML, uważa, że dobrym rozwiązaniem je st wiele nieinteligentnych maszyn działających równolegle. Czy to podejście jest podobne do pańskiego?

Brad: To interesujący pomysł. Poświęciłem wiele czasu na symulacje wojskowe. Dla tego rodzaju problem ów jest to bardzo atrakcyjna konfiguracja. Być może tę koncepcję da się również zastosować do rozwiązania podobnych problemów, o których nie pomyślałem.

Jakość jako zjawisko ekonomiczne W ja k i sposób można poprawić jakość oprogramowania?

Brad: Jednym ze sposobów jest utrzym ywanie oprogram ow ania na serwerach, tak jak w technologii SaaS (ang. Software as a Service). M ożna mieć nadzieję na ekonomiczną odpowiedź, choć z takim podejściem są także związane oczywiste koszty (prywatność, bezpieczeństwo, wydajność itp.). Kilka lat poświęciłem na pracę nad innym rozwiązaniem — tworzeniem systemów ekonomicznych wokół komponentów działających lokalnie na komputerze końcowego użytkownika. Takie podejście nie wróży jednak sukcesu ze względu na problemy z zarządzaniem prawami cyfrowymi. Dyskusja na ten temat trwa od długiego czasu i nie widać jej końca. Nie dostrzegam żadnego sposobu pogodzenia tych, którzy chcą produkować dobra cyfrowe i mieć do nich prawa własności, z tymi, którzy chcą je sobie przywłaszczyć. Bez fizycznego prawa opisującego, co oznacza własność, pozostają przepisy, sądy i prawnicy, co ostatecznie przybiera rozm iary stosow ania taktyk właściwych dla państwa policyjnego. Wyobraźmy sobie banki, które rezygnują z sejfów i zamków, pozostawiają pieniądze nocą na ulicy i pozywają do sądu te osoby, które dokonają kradzieży. Nie jest to zbyt piękny obrazek. Innym rozwiązaniem ekonomicznym, które się dziś powszechnie stosuje, są reklamy. W tym przypadku użytkow nicy nie są ani rybakam i, ani rybami, tylko przynętą. Pomimo sukcesów firmy Google nie rozumiem, jak może to prowadzić do czegokolwiek dobrego. Wszyscy się przekonaliśmy, co stosowanie tego modelu przyniosło dla radia i telewizji. Dokładnie to samo dzieje się w przypadku internetu. O statni sposób to stosowanie m odelu open source w wielu jego odm ianach i ulepszeniach — od freeware, poprzez shareware, do beerware. Ogólnie rzecz biorąc, to jest model, z którym dzisiaj pracuję. Nie wybrałem go z tego powodu, że jest to najlepszy model, jaki m ożna sobie wyobrazić, ale dlatego, że to jedyny model, jaki pozostał, oraz jedyny, którego nie dotyczy w ada systemów militarnych: wszechobecne blokady.

OBJECTIVE-C

3 37

Czego nam brakuje do tego, byśmy mogli tworzyć obiekty programowe o takiej samej jakości, ja k ą mają obiekty rzeczywiste?

Brad: Systemu ekonomicznego do nagradzania usprawnień. Tylko tego?

Brad: To jest silnik, który napędza statek. Potrzebne jednak będą inne innowacje, kiedy powstanie odpowiedni system zasilania (system ekonomiczny). Dobre przykłady można również znaleźć w biologii. Na przykład w Javie występują prymitywne pojęcia enkapsulacji, które m ożna porównać do biologicznych m itochondriów , komórek, organów itd. W Javie kapsułą o największej ziarnistości jest pełna m aszyna JVM, a najmniejszą — klasa Javy. Jedyny poziom pośredni pomiędzy nimi stanowią pakiety Javy (.jar). Kilka innych poziomów (na przykład serwlety używane dla usług SOA) m ożna uzyskać dzięki pewnym działaniom w ykonyw anym z m echanizm am i ładowania klas. Ostatnio zainteresowała mnie technologia OSGI, która stanowi pierwszą poważną próbę stworzenia dojrzałego poziomu enkapsulacji pomiędzy klasami Javy a maszyną JVM. Na przykład moje podzespoły SOA oraz kom ponenty zapewniające interoperacyjność są zebrane w pakunkach OSGI. Czy sądzi pan, że do tworzenia lepszego oprogramowania potrzebujemy lepszych bodźców?

Brad: Jeśli spojrzeć na mechanizmy, które prowadzą do ulepszania takich produktów, jak skarpety, swetry czy słodycze, to m ożna zauważyć, że silnikiem, który napędza ten system, jest ekonomia. To ekonomia sprawia, że oprogramowanie pozostaje cały czas w swoim prymitywnym stadium ewolucji. Firmy produkujące leki wydają miliardy dolarów na badania, ponieważ uzyskują dziesiątki lub setki miliardów dolarów ze sprzedaży leków wyprodukowanych dzięki tym badaniom. W tej branży rolę odgrywa pieniądz, mimo że na zewnątrz widoczne są badania.

Brad: Tak, to jest sposób na poskromienie bitów. W taki sposób m ożna nad nimi zapanować. W przypadku cegieł i układów scalonych polegamy na prawach natury, które są chronione przez ekonomię. Takie podejście jest zgodne z praw am i ustanow ionym i przez ludzkość. Innym i słowy, trzeba brać praw ników , wytaczać procesy sądowe, wykorzystywać patenty, chronić sekrety handlowe i temu podobne rzeczy. Kiedy zrobi się to wszystko, model ten może pewnego dnia zadziałać. Tyle że jest to droga bardzo nieprzyjemna. Obecny stan można przyrównać do sytuacji, w której banki, zamiast zamknąć pieniądze w sejfach, pozostaw iają je na ulicy i pozywają do sądu te osoby, które dokonają

338

ROZDZIAŁ

JEDENASTY

kradzieży. Obraz ten jest niezgodny z ludzką naturą. Istnieją szanse, że stan ten wkrótce się zakończy dzięki w prow adzeniu ustaw y DMCA. DMCA, RIAA — wszystkie te przepisy zaczynają odgrywać swoje role. Nie znoszę brać w tym udziału. Czy swobodny dostęp do oprogramowania znany z modelu open source może poprawić tę sytuację?

Brad: Model open source to najlepszy model ekonomiczny, jaki dziś jest w użyciu. Większość projektów, w które jestem dziś zaangażowany, to projekty open source. To najlepszy dostępny model, dopóki nie dojrzeje idea usług SOA. Ogólnie rzecz biorąc, kiedy mówię o biznesie bazującym na cegłach z gliny, mam na myśli właśnie oprogramowanie open source. Materiały do robienia cegieł z gliny są dostępne za darmo, ale efekt jest taki, że każdy, kto zrobi cokolwiek za pomocą tych cegieł, będzie cierpiał z pow odu problem u dokum entacji, charakterystycznego dla oprogramowania open source. Sens analogii do cegieł z gliny jest taki, że można je wykorzystywać wtedy, kiedy nie istnieją inne możliwości: cegły z gliny są lepsze niż nic. Na razie to jednak nasze jedyne wyjście — mimo że prymitywne. Jaką rolę spełniają internet i sieci w procesie projektowania oprogramowania?

Brad: Współcześnie nie da się tworzyć oprogram ow ania bez internetu. To jest nie do pomyślenia. Aby jednak pokazać ten tem at z kontrowersyjnej strony, można powiedzieć, że bez ekonom icznego m odelu dla internetu lub oprogram ow ania jakość produktów program ow ych będzie znacznie niższa w porów naniu z rynkiem przedm iotów namacalnych. Czy postrzeganie oprogramowania w kategoriach usług i komponentów na wyższym poziomie niż język zmienia ekonomię pisania oprogramowania? Czy przez sprzedaż zaufania niszczymy rynek sprzedaży bitów?

Brad: Moja szklana kula nie pozwala tak dokładnie przewidzieć przyszłości. Nie wiem. Może za tysiąc lat będzie m ożna udzielić odpowiedzi na to pytanie... Tyle czasu zajęło znalezienie takiej odpowiedzi w branży budowlanej. Dlaczego my mielibyśmy to zrobić szybciej? Myślę, że tak to będzie wyglądało za tysiące lat. Ale wtedy będę już od dawna martwy i pogrzebany. Trudno przewidzieć tak daleką przyszłość. Niektórzy mogą zaprotestować i stwierdzić, że nie mamy praw fizyki, które by nam przeszkadzały.

Brad: Zgadza się. To powinna być zaleta. Wydaje się, że to bardziej nam przeszkadza, niż pomaga. Niszczy model ekonom iczny na samym początku. To bardzo wielka szkoda.

OBJECTIVE-C

339

Gdyby dziś tworzył pan język Objective-C, czy byłby to projekt open source?

Brad: Istnieje zbyt dużo kontrargumentów, bym m ógł dobrze odpowiedzieć na to pytanie. W tedy open source nie w chodziło w grę, a my jakoś musieliśmy spłacać kredyty hipoteczne. Gdybym dziś m iał bezpieczne zajęcie, które nie byłoby zbyt wymagające, prawdopodobnie zdecydowałbym się na opcję open source. Ostatecznie jednak zapłata jest jak grawitacja. Najsłabsze siły są łatwo pokonywane przez siły silniejsze. Zapłata jest również najbardziej ogólnym kryterium i dlatego w dłuższej perspektywie nie da się od niej uciec. Czy ma pan na myśli to, że projekt open source bez wsparcia finansowego nie jest poważną alternatywą dla oprogramowania komercyjnego?

Brad: Nie, mam na myśli dokładnie to, co powiedziałem. Zapłata ma wiele form: dla wielu z nas czasami wystarczy dobra opinia. Czy aplikacje internetowe to pożyteczne zjawisko?

Brad: Usługi SOA pow odują umieszczenie oprogram ow ania na serwerach — tam nie jest ono pod kontrolą użytkow ników . Pewnego dnia może to doprow adzić do powstania systemu ekonomicznego dla architektury SaaS. Na razie niewiele da się powiedzieć o tym podejściu, ponieważ architektura SaaS dopiero raczkuje. Jednak moim zdaniem to podejście może doprowadzić do powstania systemu ekonomicznego, który uspraw ni oprogram ow anie w taki sposób, w jaki są ulepszane przedmioty namacalne — za pośrednictwem sił ekonomicznych.

Edukacja Posiada pan tytuł licencjata w dziedzinie chemii organicznej i matematyki oraz doktora w biologii matematycznej. Jak to się stało, że przeszedł pan od tych dziedzin do tworzenia języków programowania?

Brad: Po obronie pracy doktorskiej rozejrzałem się wokół i doszedłem do wniosku, że bardziej interesują mnie komputery niż kariera, którą mogłem wtedy zrobić. Czy wykształcenie uniwersyteckie ma wpływ na pańską wizję projektowania oprogramowania?

Brad: Absolutnie. Przez cały czas. Jeśli przeanalizujemy systemy ekologiczne, zobaczymy, że oprogramowanie przypomina system ekologiczny we wszystkich aspektach, poza tym, że w systemie brakuje praw obowiązujących w fizyce — nie ma prawa zachowania masy czy też prawa zachowania energii. Produktem naszej pracy są bity, które można tak łatwo skopiować, że trudno je kupować, sprzedawać, trudno także być ich właścicielem. System ekonomiczny, ekologia są naruszone.

340

ROZDZIAŁ

JEDENASTY

Gdyby lew potrafił replikować swoją żywność w taki sam sposób, w jaki my replikujemy oprogramowanie, nie byłoby postępu ani w świecie lwów, ani ich ofiar. To jest ciągły problem w oprogramowaniu: w jaki sposób być właścicielem i uzyskiwać wynagrodzenie za swoje produkty i wysiłki? Czy akcent w agendzie badań informatycznych przesuwa się z akademickich dywagacji na zastosowania praktyczne?

Brad: Nigdy nie uważałem siebie za badacza informatycznego czy też naukowca, chociaż spędziłem trochę czasu w tym środowisku. Pozostała część mojej kariery przebiegała w środowisku branżowym, zatem moje sympatie w naturalny sposób są po tej stronie. Nigdy specjalnie nie fascynowały mnie zdobycze badań naukowych. Było tak aż do niedawna, kiedy zauważyłem zwiększone zaangażowanie środowisk uniwersyteckich w działalność kom itetów standaryzacyjnych, takich jak W3C, Oasis itp. To według mnie bardzo interesujące. Podoba mi się poniższy cytat pochodzący z witryny www.virtualschool.edu: „Informatyka jako nauka nie umarła. Informatyka nigdy nie była nauką. Zasadnicza treść nowego paradygmatu, który próbuję wprowadzić w mojej nowej książce, sprowadza się do stwierdzenia, że wirusem powodującym chorobę nazywaną przez nas kryzysem oprogramowania jest to, że mamy do czynienia z substancją, która składa się z bitów, a nie atomów. Bity natomiast pochodzą w całości od ludzi, a nie z natury. Ponieważ jednak substancja ta nie podlega praw u zachow ania masy, m echanizm y komercyjne, które dają ludziom bodźce do wspólnej pracy przy produkcji ołówków lub pasów do noszenia bagaży, całkowicie się załamują. Bez handlu nie mogą się rozwinąć zaawansowane mechanizmy społeczne. W związku z tym pozostajemy w prymitywnym stanie, w którym każdy komputerowy maniak tworzy wszystko od podstaw. A zatem wszystko jest unikatow e. Nie istnieje nic poza poziom em bitów, które gwarantują jedynie eksperymentalne badania. Dlatego właśnie nie istnieje taka nauka, jak informatyka” . Czy dziesięć lat później informatyka jako nauka w dalszym ciągu jest martwa?

Brad: Nie m am zbyt wiele do d odania do wypowiedzi, którą pan zacytował. Odpowiedź na to pytanie zależy od definicji słów „inform atyka” i „m artw a” ... oraz od tego, jak bardzo chce się zantagonizow ać różne grupy osób — uczciwie lub nie — ja zupełnie nie mam na to ochoty. Mogę dodać jedynie, że stan ten będzie trwał tak długo, jak długo oprogramowanie będzie unikatow e i nie będzie zarządzane przez praw a fizyczne, które uczynią informatykę nauką społeczną (zwróćmy jednak uwagę, że coraz częstsze stosowanie standardów łagodzi stan opisany kilka lat temu).

OBJECTIVE-C

341

W ja k i sposób zm ienił się pański sposób myślenia o technikach obiektowych, superdystrybucji i usługach SOA?

Brad: Sedno wątku, który zdaje się pan rozwijać, leży w tym, że przedmiotem mojego zainteresowania w ogóle nie są języki program ow ania, ale odpowiedź na pytanie, dlaczego oprogramowanie jest tak trudne do zarządzania w porów naniu z innymi dziedzinami, z którymi ludzie radzą sobie bez wysiłku. Na przykład przygotowywanie lunchu dla m ilionów nowojorczyków, zachowywanie prawa M oore’a, niszczenie planety w celu zachowania poziomu produkcji samochodów itp. To naprawdę ciekawe zjawisko — coś podobnego nie istnieje w innych dziedzinach. Próbuję zrozumieć, jak te mechanizmy działają, aby mogły zadziałać dla oprogramowania. W rzeczywistości zdolność zarządzania takimi rodzajami złożoności jest tak powszechna, że nazwałbym ją wrodzoną, gdyby nie ślady społeczności myśliwych i zbieraczy, którzy nie znając tych praw, wykonywali wszystko sami — sami uprawiali swoje ogrody, polowali na zwierzynę, budow ali domy. Ale naw et w tam tych czasach m ożna było znaleźć ślady enkapsulacji i specjalizacji pracy: żony gotują, mężowie polują, dzieci pomagają, starcy doradzają. We współczesnym świecie specjalizacja przyjęła szalone rozmiary: to jest ta wyróżniająca ludzi zdolność do wycinania fragmentu własnych problemów i czynienia z nich problem ów kogoś innego. Ja będę gotow ał obiady, jeśli ktoś inny będzie prowadził sklep, a jeszcze ktoś inny będzie dostarczał towary. Jeden człowiek uprawia zboże, a inny produkuje nawozy. M ożna tak ciągnąć ten łańcuch specjalizacji aż do poziomu wydobywania rud w kopalniach. Specjalizacja jest tak powszechna, że uznajemy ją za pewnik. Dowodem może być brak słownictwa na opisanie wielu poziom ów produkcji, zaangażowanych w coś tak powszechnego, jak postawienie obiadu na stole. Zwróćmy uwagę na dwie części: 1) zdolność do wydzielenia, tzn. zdefiniowania fragm entu problem u, który da się wydzielić, oraz 2) zdolność do uczynienia tego problemu problemem kogoś innego. Do tych kwestii powrócę trochę później. Przed pow staniem technik obiektowych dzielenie problem u było bardzo trudne. Im więcej osób pracowało nad problemem, tym bardziej problem ten wymykał się spod kontroli. Wiele plików o mylących nazw ach (notacja węgierska), konflikty zmiennych zewnętrznych, niemal całkowity brak enkapsulacji. Jest to niemal dokładnie taki sam problem, przed jakim stają projektanci układu na poziomie bramek podczas projektow ania dużych układów . Ich rozwiązanie? Enkapsulacja kilku bram ek wewnątrz układu. Ja zajmuję się projektowaniem układu. Ty go lutujesz. W języku Objective-C staraliśmy się zamodelować dokładnie taki schemat. Oczywiście to był dopiero początek, a nie koniec (dlatego właśnie czasami tracę cierpliwość, kiedy ktoś koncentruje się wyłącznie na językach). Języki są narzędziami, które należy wybrać w zależności od problem u do rozwiązania. Ten sam problem pow tarza się na każdym poziomie. Na przykład w 20 lat od pow stania języka

342

ROZDZIAŁ

JEDENASTY

Objective-C ten sam problem pojawia się w odniesieniu do bibliotek Javy, które rozrosły się do takiego stopnia, że nikt nie jest w stanie ich zrozumieć i skutecznie używać (J2EE, mówię o Tobie). Powróćmy do super dystrybucji, która wywodzi się ze zdolności czynienia moich problem ów problem am i innych w odniesieniu do tow arów w ykonanych z bitów. Jak wiemy, bity nie podlegają fizycznym prawom zachowania — prawom, na których od czasów antycznych bazow ał system wynagradzania. Dlaczego ktoś m iałby podejm ow ać wysiłki, by rozwiązywać moje problemy? Co jest w nich takiego, co mogliby wziąć inni, jeśli nie m ożna wziąć tego, co produkuje ktoś inny, i użyć do własnych celów? W mojej książce na tem at super dystrybucji wyjaśniłem jedną kwestię w kontekście obiektów OOP o małej ziarnistości. Wynagradzanie bazowałoby na zmierzeniu wykorzystania bitów, a nie pozyskiwania bitów, jak to ma miejsce dziś. Mierzenie wykorzystania jest jednak żmudnym i trudnym problemem w odniesieniu do towarów tak narażonych na zakłócenia ze strony użytkowników pozbawionych wszelkich skrupułów. Dla odróżnienia usługi SOA nie są trudne do zmierzenia. Działają na serwerze zarządzanym przez właściciela, a nie użytkownika — w miejscu, w którym m ożna m onitorow ać wykorzystanie usługi, znacznie mniej narażonym na oszustwa. W dalszym ciągu występuje problem obsługi sprawiedliwej zapłaty za wszystkie poziomy struktury produkcji, ale to jest problem księgowości, a nie sprawa tworzenia na produkcie pancerza ochronnego zapobiegającego jego uszkodzeniu. Co ciekawe, przy przechodzeniu w górę hierarchii — od technik OOP, poprzez JBI/SCA, do SOA — występują wszystkie stare problemy informatyki. Jedyna różnica polega na tym, że standardowe reprezentacje danych (schematy XML) eliminują potrzebę tworzenia własnych parser ów dla każdej reprezentacji. To sprawia, że możliwe staje się generowanie kodu na podstawie drzew DOM zbudow anych za pom ocą standardowych parserów XML zarządzanych za pomocą coraz bardziej graficznych języków, takich jak UML, DODAF itp. Zagadnienia te są interesujące. Na pewno o wiele ciekawsze od nieskończonych debat na tem at tego, który język obiektowy jest najlepszy. Skądś to znam — języki C, Objective-C, Perl, Pascal, Java, Ruby itd. — informatyka stała się nudna, dlatego zacząłem zajmować się ciekawszymi problemami. Czy brakuje czegoś w sposobie, w ja k i nauczamy tworzenia oprogramowania?

Brad: Moje nauczanie skupia się na uświadamianiu braku silnych ekonomicznych zasad dotyczących oprogramowania. Problemy ekonomiczne z zasady są całkowicie pomijane w programach nauczania informatyki i inżynierii oprogramowania.

OBJECTIVE-C

3 43

Dlaczego informatyka nie jest prawdziwą nauką?

Brad: Za każdym razem, kiedy spotykam y now y program , stykamy się z czymś całkowicie nowym i unikatowym. Jak m ożna mówić o nauce, kiedy wszystko jest unikatowe? W przypadku badania złota lub ołowiu można zmierzyć ich właściwości i zastosować naukow e m etody do ich analizy. W przypadku oprogram ow ania nie m a takiej możliwości.

344

ROZDZIAŁ

JEDENASTY

ROZDZIAŁ

DWUNASTY

Java

Język Java wywodzi się z projektu języka przeznaczonego do działania na małych urządzeniach. Popularność zyskał wraz z pojawieniem się przeglądarek WWW i apletów, ale dzięki automatycznemu zarządzaniu pamięcią, maszynie wirtualnej, rozbudowanemu zbiorowi bibliotek oraz w pewnym stopniu urzeczywistnieniu sloganu „Napisane raz, działa wszędzie" (ang. Write Once, Run Anywhere), rozwinął się do postaci języka program owania ogólnego przeznaczenia. Chociaż firm a Sun Microsystems opublikowała kod źródłowy i udostępniała go jako darmowe oprogram owanie, zachowała pewien stopień kontroli nad ewolucją języka i bibliotekami za pośrednictwem procesu JCP (ang. Java Community Process).

345

Siła prostoty Powiedział pan, że prostota i siła są diabelskimi bliźniakami. Czy mógłby pan rozwinąć tę myśl?

James Gosling: Często się zdarza, że systemy, które mają naprawdę duże możliwości, są złożone. W eźmy na przykład specyfikację Java EE. W ystępuje w niej obsługa transakcji i utrw alania — są to mechanizm y dające napraw dę duże możliwości. Jednak w początkowym okresie języka Java nie działały takie mechanizmy. System był faktycznie bardzo prosty. M ożna było usiąść i bez trudu go zrozumieć. Jeśli ograniczymy się do samego języka Java i podstawowych interfejsów API, język ten w dalszym ciągu będzie prosty. Kiedy jednak zaczniemy używać bardziej zaawansowanych podsystemów, takich jak biblioteka Swing i Java EE, oraz całej reszty, możemy odnieść wrażenie, że toniem y w informacjach. Przyjrzyjmy się bibliotece OpenGL. Za jej pomocą można wykonać wiele interesujących działań. Z całą pewnością jednak biblioteka ta nie jest prosta. Zwłaszcza w bibliotece OpenGL trzeba doskonale rozumieć sposób działania sprzętu.

James: Zgadza się. To chyba Einstein powiedział, że systemy pow inny być jak najprostsze, ale nie powinny być prostsze, niż to konieczne. Czy prostota lub złożoność są wielkościami stałymi dla systemu? Larry Wall mówi 0 teorii złożoności, wykorzystując porównanie do łóżka wodnego. Jeśli naciśniemy złożoność w jednej częścijęzyka, w innym miejscu powstanie wybrzuszenie. Czy dlatego, że rdzeń języka Java jest bardzo prosty, złożoność ujawnia się w takich miejscach ja k biblioteki?

James: Często, kiedy ktoś mówi: „O! W łaśnie rozwiązałem problem ”, okazuje się, że właściwie problem nie został rozwiązany, a jedynie przesunięty w inne miejsce. W przypadku języka problem często polega na tym, że język jest wykorzystywany przez wszystkich. Jeśli dołączymy do niego obsługę specjalizowanych transakcji, to być może nieco ułatwimy życie ludziom korzystającym z transakcji, ale prawdopodobnie utrudnim y je wszystkim tym, którzy z nich nie korzystają. Wszyscy zapłacą koszty związane z dodatkową złożonością.

James: Z pewnością poniosą koszty związane z dodatkową złożonością, mogą także — w zależności od sposobu realizacji wybranej funkcji — ponieść i inne koszty. Java jes t obecnie dojrzałą platform ą, powszechnie wykorzystywaną od ponad dziesięciu lat. Czy istnieje sposób, aby zmodyfikować projekt języka, tak by na nowo stał się prosty?

James: Nie wiem. Myślę, że odpowiedź na to pytanie powinna brzmieć: trochę tak 1 trochę nie.

346

ROZDZIAŁ

DWUNASTY

Trochę nie, poniew aż istnieje m nóstw o kodu, który korzysta ze wszystkich wprowadzonych usprawnień. Chętnie pozbylibyśmy się pewnych elementów złożoności zaszytych w języku. Problem polega jednak na tym, że konstrukcje te są bardzo często używane. Wystarczy tylko trochę przyjrzeć się aplikacjom, aby to stwierdzić. Podjął pan wraz z zespołem jedną próbę zastąpienia biblioteki AWT biblioteką Swing. Okazuje się jednak, że biblioteka AWT w dalszym ciągu jest używana.

James: Zgadza się. To niesamowite, jak wiele osób w dalszym ciągu korzysta z AWT. Częściowo wynika to z tego, że biblioteka AWT była używana w telefonach komórkowych. Z drugiej jednak strony, m im o że raporty prasowe tw ierdzą inaczej, Java jest niew iarygodnie często używana w aplikacjach desktop. Istnieją dziesiątki tysięcy aplikacji zbudowanych z wykorzystaniem bibliotek beta, które działają niezwykle sprawnie. W związku z tym brakuje motywacji, by się ich pozbyć. Biblioteka AWT jest używana między innymi z pow odu jednej fascynującej cechy systemu obiektowego o odpow iednim poziomie abstrakcji: kiedy zmienia się rzeczywistość i znajdą się lepsze sposoby realizacji pewnych funkcji, to możemy je wykorzystać bez obawy o konflikt ze starymi elementami. W Javie są przestrzenie nazw, abstrakcje i enkapsulacja.

James: Owszem. W Javie EE w wersji EE5 przeprowadziliśmy poważną rewolucję, jeśli chodzi o prostotę. Gdy spojrzymy dziś na platformę Java EE i weźmiemy do ręki podręcznik, okaże się, że nie jest taka zła pod względem złożoności. Jeśli jednak weźmiemy stare podręczniki EE i spróbujemy znaleźć sens w obydwu, okaże się, że Java jest po prostu odrażająca. W ykonaliśmy dobrą robotę, jeśli chodzi o usprawnienie platformy EE, dla wszystkich, którzy nie muszą być jedną nogą w obu obozach. Życie jest ciężkie. Zapewnienie zgodności wstecz zawsze jest trudne. Czy to, że bardzo stare programy w Javie 1.1 można uruchamiać na najnowocześniejszej maszynie JVM, wynika z celowego działania inżynierów firmy Sun?

James: Zabawne jest to, że sama maszyna wirtualna JVM pozostaje właściwie poza wszelką dyskusją, ponieważ jest niezwykle stabilna. Wszystkie kłopoty i całe zło leżą w jej bibliotekach. Biblioteki są znacznie łatwiejsze do zarządzania w rdzeniu maszyny w irtualnej. Z aprojektow ano je tak, aby były m odularne. Pojawiają się i znikają. Można zbudować takie mechanizmy ładowania klas, które dzielą przestrzeń nazw. Dzięki tem u istnieje możliwość posługiwania się dwiema wersjami biblioteki Java AWT. Jest wiele narzędzi służących do wykonywania podobnych działań. Kiedy już dojdziemy do samej maszyny wirtualnej, zrobi się znacznie trudniej. Ale w samej maszynie wirtualnej nie ma wielkich problemów.

JAVA

3 47

Kod bajtow y był w zasadzie dość stabilny. Cała trudność w budowie maszyny wirtualnej polegała na stworzeniu optymalizatorów. Słyszałem, że w świecie Javy są w zasadzie dwa kompilatory: kompilator kodu bajtowego Javy oraz kompilator JIT. Ten ostatni w gruncie rzeczy przeprowadza kompilację od podstaw. Cała optymalizacja odbywa się w kompilatorze JIT.

James: Dokładnie tak jest. Obecnie bijemy na głowę naprawdę dobre kompilatory C i C++. W przypadku kompilatorów dynamicznych — ponieważ kompilator działa w ostatnim momencie — uzyskujemy dwie korzyści. Po pierwsze, wiadomo dokładnie, jaki chipset jest wykorzystywany. Wiele razy zdarza się, że podczas kompilowania kodu w języku C trzeba skompilować go tak, by działał na sprzęcie o określonej, ogólnej architekturze x86. Kod binarny prawie nigdy nie jest dostosow any do działania w jakiejś konkretnej architekturze. Pobieramy najnowszą kopię Mozilli i będzie ona działać na dow olnym procesorze o architekturze Intel. Dla Linuksa w zasadzie istnieje jedna wersja binariów . Są dosyć ogólne — skom pilowane za pom ocą kom pilatora CCC, który nie jest najlepszym kom pilatorem języka C. Kiedy działa maszyna wirtualna HotSpot, to dokładnie zna chipset, na którym działa. Wie, w jaki sposób działa pamięć podręczna. Wie, jak wygląda hierarchia pamięci. Jak działają wszystkie blokady potoku wewnątrz procesora. Wie, jakie rozszerzenia zestawu instrukcji zastosow ano dla wybranego układu. Maszyna w irtualna jest zoptymalizowana dokładnie pod kątem komputera, na którym działa. Druga korzyść polega na tym, że maszyna w irtualna widzi aplikację podczas jej działania. Może wykorzystać statystyki, z których wiadomo, co jest istotne, a co nie. Może wstawiać nowe elementy, co w przypadku kompilatora C jest niemożliwe. Ilość wstawianego kodu w świecie Javy jest niebywała. Oprócz tego zarządzanie pamięcią działa na bazie nowoczesnych m echanizm ów odśm iecania (ang. garbage collection). Dzięki ich zastosowaniu alokacja pamięci przebiega bardzo sprawnie. Stosujecie technikę ciągłej alokacji.

James: Tak. To jest zwykła ciągła alokacja. Jest to nowość w świecie Javy, a jej koszty są niem al takie same jak koszty funkcji mai lo c () w języku C. W edług niektórych pom iarów technika ta jest dziesięciokrotnie wydajniejsza od funkcji mallocO. Dla konstrukcji, które wykorzystują dużą liczbę niewielkich obiektów, funkcja mai loc() nie jest najlepszym rozwiązaniem. Skoro jesteśmy przy języku C: w ja k i sposób należy projektować systemowy język programowania? Co projektant powinien brać pod uwagę, gdy tworzy język, który może stać się językiem programowania systemowego?

James: Staram się nie myśleć zbyt dużo na temat języków i ich własności. W czasach, kiedy zajmowałem się projektowaniem języków, co zdarzało się o wiele za często, zawsze m oją motywacją był konkretny problem . Trzeba było wziąć pod uwagę

348

ROZDZIAŁ

DWUNASTY

kontekst, w jakim język miał działać, oraz to, jakie miało być jego przeznaczenie. Trzeba się było zastanowić, co wyróżnia środowisko, w jakim język będzie działać. W przypadku Javy cechą charakterystyczną była sieć. W szechobecna sieć sprawia, że trzeba myśleć o wszystkim nieco inaczej, ponieważ istnieje wiele uwarunkow ań ubocznych. Na przykład trzeba przewidzieć, że kom putery mogą się znaleźć także w pokoju babci. Albo że język będzie używany w urządzeniu przenośnym, które nie przypomina komputera.

James: Na długiej liście rzeczy, które się zmieniły, trzeba także uwzględnić to, że nikt nie chce widzieć niebieskiego ekranu śmierci. N ikt nie chce, aby było konieczne przeprowadzanie złożonych procedur instalacji. W związku z tym język Java został wyposażony w naprawdę silne mechanizmy izolacji błędów. Większość osób nie myśli o nich w ten sposób, ale one tam są: takie mechanizmy jak obsługa wskaźników do pamięci, odśmiecanie i obsługa wyjątków dotyczą w istocie izolacji awarii. Sprowadza się to do zapewnienia kontynuow ania działania program ów pom im o wystąpienia drobnych problemów. Jeśli jedziemy autostradą i urwie się nam klamka od drzwi, to samochód będzie jechał dalej. Jeden z problemów z większością programów w C polega na tym, że robi się coś zupełnie niewinnego, a staje się to przyczyną niewłaściwego odw ołania do w skaźników i następuje awaria. Nieprawidłowe odwołania do pamięci to szrapnele, które rozpryskują się na bardzo dużym obszarze. Nie ma absolutnie żadnego sposobu na to, by przewidzieć, co ulegnie uszkodzeniu oraz gdzie i kiedy wystąpi awaria.

James: Zgadza się. Zawsze uważałem, że jest to całkowicie nie do przyjęcia. Kiedy pow staw ał język C, a także w początkowych latach działania firmy Sun przede wszystkim w ażna była wydajność. Takie operacje, jak sprawdzanie zakresu tablic lub inne działania podobnego rodzaju, były całkowicie nie do zaakceptowania. Kiedy pojawiła się specyfikacja Javy, był w niej zapis mówiący o tym, że „nie można wyłączyć funkcji sprawdzania indeksów tablic” . Nie ma czegoś takiego, jak brak sprawdzania indeksów. Z jednej strony było to radykalne odejście od technik stosowanych w języku C w tym sensie, że w języku C w ogóle nie występuje sprawdzanie indeksów. Jest to rodzaj wrodzonej cechy specyfikacji języka. Same tablice nie są standardowe.

James: Zgadza się. Jest dodawanie oraz dosyć dziwna składnia dodawania. Jedna z magicznych cech nowoczesnych kompilatorów to ich zdolność do matematycznego udowadniania wszystkich operacji sprawdzania indeksów. Chociaż można by sądzić, że brak możliwości wyłączenia sprawdzania wskaźników stanowi niekorzystną cechę, w rzeczywistości nie jest to takie złe. W istocie jest to naw et bardzo dobra cecha. Nie wywiera żadnego ujemnego wpływ u na wydajność. Na zewnątrz pętli m ożna przeprowadzić pewne testy, ale w jej wnętrzu jest to niedopuszczalne.

JAVA

349

Gdyby w języku C poprawiono problem z ciągami znaków, wynikający z braku możliwości oszacowania długości ciągu (ponieważ ciągi znaków są zakończone bajtem zerowym), to prawdopodobnie od 4 0 lat mielibyśmy szybszy język C. Z mojego punktu widzenia jest to największy problem języka C.

James: Zgadza się. Uwielbiam język C. Zajmowałem się zawodowo programowaniem w tym języku przez wiele lat. Przerzuciłem się na C, zanim ktokolwiek używał tego języka. Pierwsze kompilatory języka C działały na maszynach wyposażonych w 32 kB RAM. Dla program ów , które mogły działać w 32 kB pamięci RAM, pierwsze kompilatory języka C były niesamowite. Teraz jednak trudno znaleźć nawet zegarek, w którym byłoby tylko 32 kB RAM. Przeciętna karta kredytowa ma więcej pamięci niż 32 kB.

Rzecz gustu \N ja k i sposób powszechność połączeń z internetem zmieniła podejście do języków programowania?

James: Problem tego, jak bardzo obecność sieci wpływa na projektowanie języków program ow ania, jest bardzo złożony. Kiedy istnieje sieć, trzeba brać pod uwagę różne elementy. Trzeba obsługiwać komunikację, myśleć o tym, że awarie wpływają na inne elementy. Trzeba zwracać baczną uwagę na niezawodność. W szczególności trzeba martwić się tym, w jaki sposób budować systemy odporne na awarie. Takie, które nie przestaną działać w w arunkach drobnych awarii. Jest to bardzo ważne z tego względu, że w większości systemów budow anych przez ludzi zawsze jakaś część jest uszkodzona. Tradycyjnie na oprogramowanie patrzono jak na urządzenia typu „wszystko albo nic” : działały lub nie. Dlatego w Javie wprowadzono takie elementy, jak mechanizm obsługi wyjątków, system silnej typizacji, mechanizm odśmiecania, maszynę w irtualną itp. Tak więc sieć wywiera ogrom ny wpływ na projekt Javy — zarów no języka, jak i maszyny wirtualnej. Na jakie elementy projektowania i programowania pańskie poglądy wywarły największy wpływ? Czy jest coś, na co może pan wskazać w stworzonym przez siebie systemie i powiedzieć o tym: „To jest znak firmowy Goslinga"?

James: Chciałbym, żeby życie było takie proste. Spotkałem w życiu kilku architektów oprogram ow ania, którzy podchodzili do rozwiązywania problem ów , mówiąc: „M am swój znak firm ow y” . Osobiście staram się być wolny od wszelkich znaków firmowych. Jeśli w ogóle można mówić o moim znaku firmowym, to powiem, że doprowadzam do szaleństwa osoby, które muszą utrzymywać na pisany przeze m nie kod. Powodem jest m oja obsesja na punkcie wydajności.

350

ROZDZIAŁ

DWUNASTY

Nie stosuję zbyt często wstawek asemblerowych, ale używam bardzo złożonych algorytmów w miejscach, w których można by zastosować proste algorytmy. Poruszam niebo i ziemię, aby osiągnąć większą w ydajność — w większym stopniu niż inni korzystam z pamięci podręcznej. Zawsze umieszczam pamięć podręczną gdzieś z boku, ponieważ jeśli nie mam pamięci podręcznej, staję się nerwowy. To przypomina mi pewną anegdotę. Kiedyś ktoś zapytał: „Dlaczego wykorzystujesz w tablicy liniowe wyszukiwanie? Przecież algorytm quicksort jest szybszy!". Na to padła odpowiedź: „W tablicy będzie co najwyżej siedem elementów; implementacja algorytmu quicksort to zbyt dużo pracy".

James: Ja również nie stosowałbym wyszukanych algorytmów dla siedmiu elementów. Wielu programistów nigdy nie zwraca uwagi na praktycznq stronę algorytmu.

James: Do szaleństwa doprowadza mnie sytuacja, kiedy ludzie tworzą system, który ma być wykorzystywany w wielu miejscach i mówią sobie: „Wystarczy, jeśli będzie to wyszukiwanie liniowe” . Wiedzą, że kiedy system zostanie wdrożony, to struktura może rozrosnąć się do 100 000 elementów. Myślą sobie, że jeśli przetestowali algorytm dla 10 elementów, to liniowe wyszukiwanie wystarczy także w innych sytuacjach. Zawsze też mówią: „Pewnego dnia wprowadzę usprawnienia wydajności” . To jedna z tych sytuacji, które m ożna opisać słowami Roberta Frosta: „Wiedząc, jak droga potrafi być daleka, wątpiłem jednak, czy pow inienem powrócić” . M nóstwo kodu można tak opisać. Często mam takie wrażenie, że zazwyczaj nie wraca się do miejsc, które miały być popraw ione. W efekcie świat jest pełen systemów, które działają bardzo niewydajnie. Czy wynika to z ograniczeń czasowych, lenistwa, czy też z tego, że programowanie staje się coraz łatwiejsze dla osób, które nie sq programistami?

James: Myślę, że ze wszystkiego po trochu. Pewien wpływ ma również to, że komputery mają zegary o częstotliwości trzech gigaherców, dzięki czemu m ożna wykonywać pętle nieskończone w skończonym czasie. © Dlaczego na poczqtku zdecydował się pan wykorzystywać dla Javy maszynę wirtualną?

James: Ponieważ jej zastosowanie doskonale rozwiązuje problem przenośności. Maszyna wirtualna bardzo poprawia niezawodność i — co może wydawać się dziwne — znacząco popraw ia wydajność. Znacznie łatwiej osiągnąć wysoką wydajność, jeśli wykorzystuje się kompilację typu „dokładnie na czas” (ang. just-in-time). Tak więc stosowanie maszyny wirtualnej jest pomocne w bardzo wielu miejscach. Jest ona bardzo przydatna w debugowaniu.

JAVA

351

Czy jest coś, co zrobiłby pan inaczej, jeśli chodzi o projekt maszyny JVM, czy też jest pan zadowolony ze sposobu je j działania?

James: Właściwie jestem z niej zadowolony. Projekt maszyny JVM to prawdopodobnie najbardziej stabilna część całej architektury. Gdybym m iał znaleźć problem do rozwiązania, nie szukałbym go w maszynie JVM. Jest ona bowiem w dobrej formie. Nie ma w niej niczego, co by stanowiło na tyle poważny problem, aby trzeba było się nim zajmować. Przez niemal dekadę pracowało nad nią wielu bardzo inteligentnych ludzi. Wielu z nich to profesorowie budowy kompilatorów. Czy ze względu na popularność maszyny wirtualnej JVM w innych językach zaprojektowałby pan ją jakoś inaczej?

James: Jest kilka rzeczy, które bym trochę dostroił. W łaśnie teraz pracujemy nad odpowiedziam i na kilka pytań dotyczących projektu języka. Zaskakujące jest to, jak trudno znaleźć elementy, które można by zaimplementować inaczej w maszynie wirtualnej, a które rzeczywiście pomogłyby w innych językach. Największe problemy miały raczej charakter filozoficznych dywagacji związanych z maszyną wirtualną. Na przykład niezwykle trudno zaimplementować na wirtualnej maszynie Javy takie języki, jak C i C++, poniew aż maszyna JVM nie pozwala na stosowanie nagich wskaźników. Zezwolenie na w ystępowanie nagich w skaźników w iązałoby się z olbrzymim problemem niezawodności. Z tego względu zdecydowaliśmy, że nigdy nie pozwolimy na występowanie takich wskaźników. Problemy niezawodności i bezpieczeństwa związane z zastosowaniem tego rodzaju wskaźników byłyby zbyt poważne. A zatem C lub C++ na maszynie JVM? Nigdy. Użył pan porównania, w którym powiedział pan, że samochód nie zatrzym a się, gdy na przykład przestanie działać radio. Czy powinniśmy budować nowe bloki dla oprogramowania, tak by uniknąć zasadniczego problemu, że jeśli coś się zepsuje, to cała reszta przestanie działać?

James: Wiele z tych problem ów wynika ze sposobu, w jaki jest zbudow any niskopoziomowy system naczyń połączonych w tych językach. „Wszystko zmierza do zawieszenia się systemu” — to jeden z większych problemów języka C. Przyczyną jest sposób postępowania ze wskaźnikami w tym języku. Wystarczy, że wystąpi dowolny błąd z powodu nieprawidłowego użycia wskaźników, a cały system przestaje działać. N atom iast w Javie z jednej strony jest mniejsze prawdopodobieństwo wystąpienia błędu wskaźnika, a z drugiej strony, jeśli zdarzy się błąd, można go obsłużyć. System obsługi wyjątków bardzo dobrze sprawdza się jako mechanizm ograniczania skutków szkód. Zatem kiedy radio ulegnie uszkodzeniu, można po prostu je odłączyć.

352

ROZDZIAŁ

DWUNASTY

Poza tym ludzie, którzy budują wielkie systemy korporacyjne w Javie, poświęcają sporo czasu na projektowanie ich w taki sposób, aby poszczególne komponenty były stosunkow o dobrze zabezpieczone przed skutkami awarii innych kom ponentów . W związku z tym pojedyncze elementy mogą ulegać awarii bez szkody dla pozostałych. Jeśli zajrzymy do specyfikacji Java Enterprise, znajdziemy w niej wiele miejsca poświęconego działaniu tego frameworku oraz obsłudze błędów. Czy w związku z tym paradygmat obiektowy w Javie jest w dalszym ciągu kompletny i prawidłowy?

James: Program ow anie obiektowe bardzo dobrze sprawdza się współcześnie. Prowadzone są liczne dyskusje na tem at w arunków brzegowych. Ludzie prowadzą teoretyczne debaty o możliwych zmianach. Jednak podstawowe pojęcie programowania obiektowego bardzo dobrze się sprawdziło i nie wykazuje żadnych istotnych wad. Czasami wydaje się, że z powodu obiektów programiści muszą pracować podwójnie ciężko. Najpierw muszą zaprojektować komponent, który będzie można wielokrotnie wykorzystać. Kiedy później wprowadzą jakąś zmianę, muszą napisać coś, co pasuje dokładnie do miejsca pozostawionego przez poprzedni komponent. Ogólnie rzecz biorąc, uważam, że jest bardzo cienka linia pomiędzy wykorzystywaniem obiektów w celu uzyskania dobrego projektu a stosowaniem obiektów, które wszystko komplikują.

James: Z całą pewnością projekt obiektowy wymaga pewnej dozy dobrego smaku. Świat jest pełen ludzi, którym wszystko wymknęło się spod kontroli, a uzyskane efekty były szalone, ale liczba takich przypadków jest ograniczona. Interfejsy i obiekty bardzo dobrze się sprawdziły. Stosowanie obiektów zmusza do myślenia o relacjach, które zachodzą pomiędzy podsystemami, a to jest niezwykle istotne. Problem projektow ania systemów w taki sposób, aby zapewnić możliwość dekompozycji ich części, niezwykle pom aga w ewolucji, debugow aniu, obsłudze błędów oraz całym szeregu innych problemów. Projektowanie wymaga też pewnej dozy dobrego smaku, aby m ożna było używać systemów prawidłowo, ale to nie jest takie trudne. Obiektowe projektowanie systemów udow odniło swoją przydatność. Jest to o wiele bardziej w artościow y sposób projektowania oprogramowania w porównaniu z kodem, który przypomina spaghetti i w którym wszystko jest bezpośrednio zintegrowane ze wszystkim. Jeśli w takim systemie zmieni się jeden element, trzeba zmieniać w nim wszystko. Sprawia to olbrzymie kłopoty. Świat, w którym systemy nie są zaprojektowane obiektowo, jest po prostu okropny. Podejście obiektowe sprawdza się najlepiej wtedy, gdy system osiąga duże rozmiary, kiedy tworzy go zespół złożony z wielu osób oraz kiedy system podlega ewolucji w czasie.

JAVA

3 53

Im bardziej modularny projekt systemu oraz im skuteczniej są od siebie odizolowane zadania programistów, tym mniejsze zmiany trzeba wprowadzać w przypadku, gdy coś się zmieni w systemie. To niezwykle przydatna własność.

Współbieżność Coraz częściej można usłyszeć o końcu praw a M oore'a w tym sensie, że systemy są co prawda coraz bardziej rozbudowane, ale niekoniecznie szybsze. Czy zgadza się pan z tą opinią?

James: Tak. Prawo Moore’a dotyczyło liczby bramek w układzie scalonym. Z łatwością można zaobserwować, jak przez wiele lat zgodnie z prawem Moore’a wzrastała liczba bramek stosowanych w układach. Jednak interpretowanie tego prawa w odniesieniu do szybkości zegara wygląda na przypadkową zbieżność. Czy potrzeba lepszej obsługi współbieżności zmienia sposób implementacji, czy także wpływa na projekt?

James: Z pewnością znacznie zmienia projekt, ponieważ przy przechodzeniu z jednej dziedziny do innej występują duże różnice. A zatem tak jak w większości oprogramowania matematycznego, aby jakiś system matematyczny mógł działać w środowisku wielowątkowym, trzeba znacznie zmienić algorytm. Jednak większość oprogramowania korporacyjnego często działa wewnątrz frameworków. Na przykład w świecie Javy są frameworki serwerów aplikacji, Java EE, natom iast w fram ew orku EE aplikacje nie m uszą zdawać sobie sprawy z tego, że działają w środowisku wielowątkowym. To kontener — serwer aplikacji — wie wszystko na temat wielowątkowości, zna sposób postępowania z klastrami czy obsługi wielu procesów itp. Od tych problemów można po prostu abstrahować, nie trzeba się nimi martwić. Jednak schemat ten dobrze się sprawdza w przypadku oprogram ow ania korporacyjnego, w którym występuje wiele niewielkich transakcji niemających ze sobą wiele wspólnego. Po prostu są wykonywane i tyle. N atom iast w przypadku oprogram ow ania z w ielom a obliczeniami, w którym poszczególne operacje są ze sobą wzajemnie powiązane, współdzielą dane itp., sytuacja znacznie się komplikuje. W tej sytuacji nie istnieje jednoznaczna odpowiedź. Czy potrzebne są nowe języki, czy też wystarczy stworzyć nowe narzędzia i biblioteki rozwiązujące problemy współbieżności? Czy występowanie współbieżności nie powinno zmusić wszystkich programistów do zmiany sposobu myślenia?

James: Cóż, to zależy od sytuacji. Uważam, że wszystko zależy od dziedziny zastosowań. W większości aplikacji korporacyjnych bazujących na transakcjach

354

ROZDZIAŁ

DWUNASTY

można wykorzystywać frameworki, takie jak Java EE, które w bardzo prosty sposób obsługują współbieżność. Jeśli program działa na kom puterze Sun z procesorem 128-rdzeniowym, który o dziwo istnieje naprawdę, programiści nie muszą wcale o tym wiedzieć, a komputer doskonale wykorzysta wszystkie rdzenie. Umiejętność programowania w wielu wątkach niekoniecznie zależy od talentu programisty. Może się zdarzyć, że ktoś będzie doskonałym programistą, ale nie będzie ekspertem w wielowątkowości. W takim przypadku z pomocą przyjdzie Java.

James: Zgadza się. Frameworki pozwalają abstrahować od wszystkich problemów wielowątkowości. Trudniej jest wtedy, gdy rozwiązujemy problem y obliczeniowe — na przykład wykonujem y symulacje lub tego rodzaju działania. Algorytmy korzystające z teorii grafów i metody numeryczne. Podzielenie ich na wiele wątków jest niezwykle trudne. Źródłem niektórych problem ów jest konieczność dostępu do struktur danych, stosowanie blokad i tym podobnych mechanizmów. Często niezwykle trudne jest korzystanie ze struktur danych oraz prawidłowa implementacja algorytmu. Niełatwe jest też zaimplementowanie problemu komiwojażera. Niektóre algorytmy są łatwiejsze do implementacji, na przykład renderowanie obrazu za pom ocą techniki śledzenia promieni (ang. ray tracing). Jednak w tym przypadku sytuacja okazuje się specyficzna, ponieważ można wziąć poszczególne piksele, które są od siebie całkowicie niezależne. Problem renderowania obrazów można zrównoleglić do poziomu pikseli, jeśli ma się do dyspozycji wystarczająco dużo sprzętu.

James: Zgadza się. To akurat działa całkiem dobrze. W większości systemów stosujących technikę śledzenia prom ieni wykorzystuje się współbieżność. Niektóre problemy, jak na przykład metody numerycznej dynamiki płynów, wykorzystywane między innym i do prognozow ania pogody oraz obliczania, czy sam olot spadnie, czy nie, są trudniejsze do rozwiązania, ponieważ istotną rolę odgrywa w nich kom unikacja pomiędzy różnymi elementami obliczeń. Stosunkowo łatw o m ożna rozdrobnić problem w systemie przestrzeni adresów współdzielonych, ale znacznie trudniej zrobić to w przypadku klastrów, gdy mamy do czynienia z przestrzeniami adresowymi, które nie są wspólne. Niepowodzenie algorytmów CFD wynika w dużej mierze z kosztów komunikacji pomiędzy węzłami należącymi do klastra. Problemy znacznie lepiej rozwiązuje się za pomocą procesorów wielordzeniowych, ale wszystko zależy w znacznym stopniu od stosowanego algorytmu. Jedną z własności, która bardzo mi się podoba w językach funkcyjnych, takich jak Scala, jest to, że jeśli w tym języku napiszemy algorytm, to kompilator ma możliwość w ywnioskow ania tego, co robi program . Może on autom atycznie odwzorować algorytm na wielowątkowy i wielordzeniowy system rozproszony.

JAVA

355

Czy wynika to z tego, że Scala jest czysto funkcyjnym językiem programowania?

James: Nie jest czysto funkcyjnym językiem. Jednym z powodów, dla których działa tak dobrze, jest to, że wykazuje cechy języka funkcyjnego i jednocześnie obiektowego. Można w nim programować tak jak w Javie, ale można również stosować techniki programowania funkcyjnego. Czy istnieją dziedziny problemów, w których wielowątkowość bazująca na wspólnej pamięci działa lepiej od technik funkcyjnych?

James: W przypadku aplikacji korporacyjnych podejście do w ielordzeniow ych systemów rozproszonych bazujące na frameworkach sprawdza się naprawdę bardzo dobrze. Nie sądzę, aby zastosowanie takiego systemu jak Scala wiązało się z jakimiś szczególnymi korzyściami. Rozwiązywanie takich złożonych problemów, jak problem komiwojażera, jest bardzo ciekawe. W projekcie Green lub projekcie Oak podjęto decyzję o wprowadzeniu prymitywów do synchronizacji oraz zapewnieniu bezpieczeństwa w ątków dla podstawowych bibliotek. Uznano to za niezbędne w języku przeznaczonym do działania w świecie wszechobecnych sieci oraz wielowątkowości.

James: Istnieje wiele m echanizm ów pozwalających zapewnić bezpieczeństwo na poziomie wątków. Jest to dość dziwny przypadek, ponieważ zwykle bezpieczeństwo wątków oznacza, że jeśli uruchamiamy program w systemach, w których jest tylko jeden procesor, to musimy zapłacić odpowiednią cenę. Jednak w tym konkretnym przypadku uważam, że abstrakcja jest bardzo pomocna. Maszyna JVM jest przecież bardziej abstrakcyjna od maszyny fizycznej. Dzięki abstrakcji można odpowiednio przystosować się do sytuacji. W przypadku wielowątkowości okazuje się, że maszyna wirtualna HotSpot w magiczny sposób rozumie, czym różnią się maszyny jednordzeniowe od wielordzeniowych. Kiedy stosujemy kompilator JIT, możemy powiedzieć: „N ie musimy przejmować się synchronizacją tego fragmentu, ponieważ wiemy, że zakleszczenie nigdy nie wystąpi; nigdy nie będzie rywalizacji wątków o ten konkretny fragment pamięci".

James: Tak, a co więcej, wszystko to odbywa się w magiczny i przezroczysty sposób. Nikt o niczym nie wie. Podobny problem występuje z 64-bitowymi wskaźnikami. W świecie języka C trzeba stosować wrappery, aby aplikacje mogły działać w trybie 64-bitowym. W przypadku Javy nie trzeba robić niczego.

Projektowanie języka Czy gdyby nie istniała Java, to pańskim językiem byłby język Scala?

James: Myślę, że tak.

356

ROZDZIAŁ

DWUNASTY

Co pan sądzi o wszystkich tych nowych językach, które nie są tylko projektam i badawczymi, ale poważnymi próbam i skonstruowania rozbudowanego języka na bazie maszyny JVM?

James: Myślę, że to ciekawe projekty. Czy nie boi się pan, że przejmą pańskie najlepsze pomysły i wyprzedzą Javę pod względem technologicznym?

James: Nie, wszystkie najważniejsze fragmenty Javy są zaszyte w maszynie w irtualnej. Dzięki tem u jest możliwa interoperacyjność. W pewnym sensie Java oraz składnia ASCII zostały tak zaprojektowane, aby kod Javy był zrozumiały dla osób programujących w C i C++. Spełniło to swoją rolę bardzo dobrze. Większości program istów C i C++ wystarczy rzut oka na kod Javy, aby stwierdzić: „O, ja to rozumiem” . Potrafią ustalić, co robi kod, mimo że nie znają szczegółów interfejsu API.

James: Taki właśnie był jeden z celów projektowych. W pewnym abstrakcyjnym świecie, gdzie wszyscy zastanawiają się, jaki jest najlepszy język program owania, to nie stanowi głównego celu. Osobiście uważam, że bardzo interesujący jest język Scala. Problem z nim polega na tym, że jest to funkcyjny język programowania, a wielu osobom sprawia trudność myślenie w kategoriach języka funkcyjnego. Gdybym zaprojektow ał język program ow ania, który m iałby służyć tylko mnie, praw dopodobnie doprow adzałby do szaleństwa wszystkie inne osoby, które próbowałyby zrozumieć kod w tym języku. A może Lisp?

James: Prawdopodobnie nie byłby to Lisp, ale pewne fragmenty języka przypominałyby analogiczne konstrukcje z tego języka. Bill Joy powiedział kiedyś, że waszym celem było przeciągnięcie krzyczących i kopiących programistów C+ + na stronę języka Common Lisp.

James: W pewnym sensie tak było, jeśli przyjrzeć się zawartości projektu maszyny JVM. Idea, że maszyna wirtualna nie musi działać wolno, albo pogląd, że powszechne mechanizmy odśmiecania zapewnią programistom dobrą wydajność...

James: Wiele osób nie doceniało wielkiej zalety odśmiecania — popraw y niezaw odności i bezpieczeństwa. Przyjrzyjmy się, co jest najczęstszym źródłem błędów w dużych systemach: zwykle są to błędy zarządzania pamięcią. Kiedyś spróbow ałem zebrać statystyki na tem at wszystkich błędów, które znalazłem, oraz przeanalizowałem liczbę godzin, jakie musiałem poświęcić na ich poprawienie.

JAVA

3 57

Jeden z problem ów związanych z błędam i uszkodzenia pamięci polega na tym, że ich śledzenie zajmuje bardzo dużo czasu. Przysiągłem sobie, że nigdy nie stracę kolejnej godziny na śledzenie błędów pamięci. Napisałem dwa pierwsze mechanizmy odśmiecania dla maszyny JVM. Mechanizmy odśmiecania są bardzo trudne do debugowania, ale kiedy się je uruchomi, to po prostu działają bez zarzutu. Obecnie mamy kilka świetnych mechanizmów odśmiecania. Jaki rodzaj mechanizmu odśmiecania napisał pan początkowo?

James: Potrzebny mi był taki m echanizm , który działałby w bardzo m ałych przestrzeniach adresowych. Koncepcyjnie działał on jako prosty mechanizm typu „oznacz i usuń, a następnie skompaktuj” . Miał również pewną zdolność do działania w trybie asynchronicznym. Nie było miejsca na to, aby zastosować jakiś bardziej nowoczesny projekt. Obsługa uchwytów bardzo pomogłaby w kompaktowaniu. Dodatkowe opakowanie wskaźnika, które pozwala na kopiowanie pamięci.

James: Ponieważ próbow ałem pracować z bibliotekam i języka C, pierwsze wersje uchw ytów były nieścisłe. Były ścisłe dla wszystkich w skaźników na stercie, natom iast w przypadku stosu były nieścisłe, poniew aż stosowanie ich wiązałoby się ze skanow aniem stosu C w celu sprawdzenia, czy jest na nim cokolwiek, co przypomina wskaźnik. To b ył jed yn y sposób na praw idłow ą obsługę stosu C, chyba żeby całkowicie zrezygnować z używania stosu, co miało zarówno swoje zalety, ja k i wady.

James: To praw da. Tak w łaśnie jest dziś w przypadku maszyny JVM. W cale nie używa stosu C. Zamiast tego wykorzystuje własny mechanizm stosu. W kodzie C wykorzystywany jest osobny stos. Jedną z kłopotliw ych rzeczy związanych z interfejsem JNI jest realizowanie przejść pomiędzy dwoma światami. Czy biorąc pod uwagę to, że Java była inspiracją dla języka C#, uważa pan, że istnieją inne własności Javy, które mogą być użyte w innych językach programowania?

James: Cóż, projektanci języka C# w zasadzie przejęli wszystko, chociaż podjęli dziwną decyzję o rezygnacji z mechanizmów bezpieczeństwa i niezawodności, dodając obsługę niebezpiecznych wskaźników. Decyzja ta uderza m nie jako groteskowa głupota. Większość własności Javy zostało jednak użytych w różnych elementach wielu języków.

358

ROZDZIAŁ

DWUNASTY

Napisał pan mechanizm odśmiecania, aby zaprzestać marnotrawienia czasu na debugowanie błędów zarządzania pamięcią. Jaka jest pańska opinia na temat sposobu implementacji wskaźników w języku C+ + w zestawieniu z referencjami w Javie?

James: Wskaźniki w języku C++ to katastrofa. To po prostu zaproszenie do popełniania błędów. Nie chodzi naw et bezpośrednio o implementację wskaźników, ale o fakt, że trzeba ręcznie zarządzać zużytą pamięcią, a co ważniejsze, że jest dozwolone rzutowanie pomiędzy wskaźnikami a danym i typu integer. Ze względu na sposób, w jaki zaprojektow ano wiele interfejsów API, często trzeba wykonywać takie rzutowanie. Czy referencje w Javie zaprojektow ał pan w celu rozwiązania wszystkich tych problemów oraz dodatkowo w celu uzyskania wszystkich korzyści wynikających ze stosowania wskaźników w języku C ++ ?

James: Tak, w Javie można zrealizować wszystkie ważne operacje, jakie są możliwe do wykonania w C++ za pomocą wskaźników. Czy dostrzega pan inny powtarzający się problem, którego można uniknąć dzięki zaimplementowaniu ogólnego rozwiązania w języku?

James: Takie rozwiązania można znaleźć w wielu miejscach Javy. Przykładem może być m echanizm obsługi wyjątków. Osoby programujące w C++ często kompletnie ignorują kody błędów otrzymywane z wielu miejsc. Java pozwala na łatwą obsługę błędów w momencie ich wystąpienia. Programy w Javie są znacznie bardziej niezawodne — po części dlatego, że programiści posiadają instrumenty, które pozwalają brać pod uwagę błędy wszędzie tam, gdzie to możliwe. Dzięki tym instrum entom mogą mieć pewność, że jeśli wystąpią błędy, zostaną obsłużone. Czy dobrze dobrane wartości domyślne mogą pomóc programistom w pisaniu lepszego kodu? Czy dzięki nim mogą oni zrezygnować z poszukiwania zewnętrznych bibliotek lub dodatków?

James: Z biegiem lat z większości języków w yeliminowano większość tych rzeczy. Jednym z obszarów języka C++, który sprawiał wiele problemów, była wielowątkowość. Wielowątkowość jest bardzo ściśle zaszyta w kodzie Javy. W konsekwencji za pomocą języka Java m ożna bardzo dobrze zarządzać kom puteram i z procesorami wielordzeniowymi. Jakie jest powiązanie pomiędzy projektem języka a projektem oprogramowania napisanego w tym języku?

James: Subtelne powiązania istnieją wszędzie. Mam tu na myśli to, że stosowanie języków obiektowych zachęca do budowania bardzo modularnych systemów. Istnienie

JAVA

359

w języku m ocnego systemu obsługi wyjątków zachęca do budow ania systemów odpornych na błędy. Ogólnie rzecz biorąc, każda własność języka, jaką można sobie wyobrazić, zawiera w sobie subtelny nacisk na odpowiedni projekt oprogramowania. Czy istnieją jakieś inne własności, które chciałby pan uwzględnić w standardowym języku programowania? Co z automatycznym sprawdzaniem kodu?

James: W Javie istnieje wiele mechanizmów do przeprowadzania takich testów. Jeśli przyjrzymy się, co robi instrukcja makeme(? ), zauważymy, że wykonuje ona test LINT w czasie rzeczywistym — w ten wysokopoziomowy sposób testowany jest kod. Dziś nie m ożna już rozpatrywać języka w odosobnieniu. Język i wykorzystywane z nim narzędzia są ze sobą nierozerwalnie związane. Czy podczas projektowania języka myślał pan o tym, w ja ki sposób będzie debugowany kod napisany w tym języku?

James: Jest wiele rzeczy, które m ają znaczenie podczas budow y niezaw odnego oprogramowania, ale nie dotyczą one samego procesu debugowania. W przypadku debugowania ma zastosowanie kilka standardów — na przykład to, w jaki sposób system komunikuje się z systemem debugowania. W języku wykonuje się wiele operacji po to, aby uniknąć konieczności debugowania. To dlatego w wielu miejscach Javy można znaleźć takie mechanizmy, jak menedżer pamięci, ścisła typizacja, model wątków. M echanizmy te pom agają programiście jeszcze przed zaistnieniem konieczności debugowania. Jaki wpływ na debugowanie ma to, że język jest niezależny od platformy?

James: Z punktu widzenia programisty debugowanie jest całkowicie przezroczyste. Można na przykład uruchomić sesję debugowania aplikacji działającej na serwerze linuksowym z poziomu kom putera Mac. W ja k i sposób debuguje pan kod w Javie?

James: Wykorzystuję narzędzie NetBeans. Czy ma pan jakieś wskazówki dla programistów Javy?

James: Używajcie środowiska NetBeans, korzystajcie z instrukcji assert w wielu miejscach kodu. Bądźcie ostrożni podczas budowania zadań — jest narzędzie JUnit, dość popularne. Stosujcie wszystkie te narzędzia razem — zapewniam, że dobrze do siebie pasują. Czego brakuje studentom informatyki?

James: Na większości uniwersytetów nacisk kładzie się na techniczną stronę problemu. Duża część działań w inżynierii program ow ania polega na rozwiązywaniu zadań w stylu „To jest program. Trzeba znaleźć w nim błąd”, podczas gdy typowe zadanie

360

ROZDZIAŁ

DWUNASTY

na studiach brzmi: „Napisz program, który realizuje to lub to ”, przy czym student ma do dyspozycji czystą kartkę papieru, zatem może napisać to, co mu się podoba. Inżynieria oprogram ow ania to także społeczna dynam ika pracy w zespole. Wielu z tych zagadnień w ogóle nie ma w programach nauczania. Jaki jest pański pogląd na dokumentację oprogramowania?

James: Im więcej, tym lepiej. Jednym z unikatowych mechanizmów, którym Java dała początek, była dokumentacja zintegrowana z kodem. Dla interfejsów API Javy istnieje narzędzie Javadoc, które wyodrębnia dokumentację z kodu źródłowego. Jeden z olbrzymich problem ów dotyczących dokum entacji oprogramowania polega na tym, że dokumentacja nie jest zgodna z rzeczywistymi interfejsami API. Dzięki autom atycznem u w yodrębnianiu dokum entacji z kodu synchronizacja dokumentacji z kodem jest znacznie lepsza. Nawet jeśli ktoś w ogóle nie stosuje kom entarzy, narzędzie Javadoc i tak potrafi wygenerować użyteczną dokumentację interfejsu API. A zatem najważniejszą rzeczą, jaką można zrobić w zakresie dokumentowania kodu, jest użycie narzędzia Javadoc oraz umieszczenie w kodzie dobrych kom entarzy. To pomaga wszystkim. Czy je s t pan zwolennikiem tworzenia pełnej form alnej specyfikacji projektu przed przystąpieniem do jego realizacji?

James: Mam mieszane uczucia na temat formalnych specyfikacji. Jest to jedna z tych rzeczy, które doskonale wyglądają w teorii, ale w praktyce nie działają najlepiej. Często się zdarza, że sprawdzają się one w przypadku niewielkich projektów, natomiast im większy projekt, tym mniejszy pożytek z formalnej specyfikacji, choćby z tego prostego powodu, że formalna specyfikacja niezbyt dobrze się skaluje. Co ważniejsze, często w ykonanie formalnej specyfikacji nie rozwiązuje samego problem u, a jedynie zmienia miejsce, w którym ten problem występuje. Zamienia problem błędów w oprogramowaniu na wyszukiwanie błędów w specyfikacji. A błędy w specyfikacji są bardzo trudne do znalezienia. Nawet jeśli nie wykonuje się formalnej specyfikacji, to zazwyczaj wykonuje się pewnego rodzaju analizę wymagań. Wiele firm stosuje taką praktykę, że jedna grupa tworzy dokument określający wymagania, a następnie wręcza go innej grupie odpowiedzialnej za faktyczną realizację projektu. W dokumencie określającym wymagania jest często mnóstwo błędów. Gdy nie istnieje ścisłe sprzężenie zwrotne pomiędzy tym i grupami, to jeśli nie zostaną znalezione błędy w wymaganiach, nie będą znalezione także w projekcie. Zatem chociaż ogólnie jestem fanem tworzenia specyfikacji i formułowania wymagań, to nie traktuję ich zbyt poważnie i nie spodziewam się, że pozwolą mi one rozwiązać poważne problemy.

JAVA

361

Przeprowadzałem również wywiady z osobami tworzącymi diagramy UML oraz projektującymi inne języki. Interesująca wydała mi się idea używania tych bardzo wysokopoziomowych języków projektowania do tworzenia logiki oprogramowania. Osoby te wspominały o możliwości wykrywania błędów w logice modelu jeszcze przed przystąpieniem do pisania kodu.

James: Zgadza się. W świecie Javy istnieje wiele w ysokopoziom owych narzędzi bazujących na m odelow aniu. M echanizm y w ysokopoziomowego m odelowania, występujące w wielu frameworkach używanych do tworzenia aplikacji internetowych, interfejsów użytkownika oraz diagramów UML, dają bardzo duże możliwości. Jeśli przyjrzymy się środowisku NetBeans, zauważymy, że jest ono wyposażone w dość zaawansowany system modelowania UML. Można go wykorzystać zarówno do w stępnego specyfikowania oprogram ow ania w kategoriach m odelu UML, a następnie do automatycznego wygenerowania programu, jak i jako rodzaju narzędzia pozwalającego na wgląd do programu. Tak więc narzędzia modelowania są pomocne, choć nie rozwiązują wszystkich problemów.

Pętla sprzężenia zwrotnego Czy pana zespół otrzymuje jakieś uwagi dotyczące samego języka, a nie implementacji?

James: Oczywiście. Otrzymujemy wiele uwag dotyczących języka. W ja k i sposób zespół z nimi postępuje?

James: Jeśli o pewną własność prosi jedna lub dwie osoby, zwykle to ignorujemy. W odniesieniu do języka istotne jest między innymi zwracanie uwagi na to, co się modyfikuje. Bariera ta okazuje się nieco łatwiejsza w przypadku interfejsów API, ale ogólnie rzecz biorąc, nie robimy niczego, jeśli nie istnieją ku temu bardzo ważne powody. A zatem jeśli wiele osób prosi o tę samą rzecz, to może oznaczać, że wprowadzenie tej zm iany ma sens. Jeśli jednak jeden na m ilion program istów pyta o coś, to prawdopodobnie wprowadzenie tej zmiany przyniesie więcej szkody niż pożytku. Jakie ma pan odczucia po udostępnieniu kodu źródłowego Javy jako darmowego?

James: M am bardzo pozytywne odczucia. Kod źródłow y Javy udostępniliśm y w 1995 roku. Od tamtej pory był on często pobierany i używany do wielu celów — począwszy od rozpraw naukowych, a skończywszy na audytach bezpieczeństwa. Kod źródłowy Javy zyskał olbrzymią popularność. Czy jest to dopracowywanie implementacji, czy też ewolucja języka jako efekt pracy grupowej?

James: Wielu współtwórców języka to dobra rzecz. To swego rodzaju konwersacja.

362

ROZDZIAŁ

DWUNASTY

Czy można projektować język w sposób demokratyczny?

James: To bardzo delikatna materia. Jeśli bowiem przesadzimy z demokracją, powstanie chaos. Z kolei jeśli projekt języka odbywa się pod dyktando centralnego dyktatora, powstały produkt może nie mieć sensu dla nikogo innego — jest bowiem wyłącznie osobistym punktem widzenia tego dyktatora. Z tego względu jest bardzo ważne, aby prowadzić konwersację z wieloma osobami oraz aby wykorzystywać stosunkowo ścisły proces podejmowania decyzji. Czy jest pan zwolennikiem idei rozwoju języka, czy też uważa pan, że skoro istnieje cel, to po stworzeniu czegoś, co pozwala na osiągnięcie tego celu, należy napisać nowy język?

James: Myślę, że po trosze i jedno, i drugie. Nie uważam, że idea rozwijania języka jest zła, ale sądzę, że powinny istnieć istotne bariery, które nie pozwalają na chaotyczne dodawanie nowych elementów do języka. Dla elementów, które potwierdziły swoją wartość, idea rozwoju języka wydaje się słuszna. Z kolei dla elem entów niezbyt wartościowych wprowadzanie losowych zmian syntaktycznych wydaje się bezcelowe. Jeśli obudzisz się pewnego ranka i stwierdzisz, że nawiasy klamrowe są przyczyną całego zła, to ich usunięcie wydaje się bezcelowe. Warto jednak wprowadzać zmiany, które wpływają na zdolność tworzenia oprogramowania przez wiele osób. Kilka lat temu podjęliśmy wysiłek związany z dodaniem typów generycznych do Javy i to posunięcie okazało się trafione. Czym się pan kieruje przy podejmowaniu decyzji o tym, co ma być umieszczone wewnątrz języka, a co w obrębie zewnętrznej biblioteki?

James: W dużej mierze decyzja zależy od tego, jak ogólne m a być zastosowanie wybranej własności. A zatem elementy, które są przydatne dla niezbyt licznych społeczności, nadają się do zaimplementowania w postaci bibliotek. Ogólnie rzecz biorąc, wszystko to, co m ożna zrealizować za pom ocą bibliotek, pow inno być zrealizowane jako biblioteka, natomiast zmiany w języku powinny być zarezerwowane dla elementów, które nie mieszczą się w zakresie żadnej z bibliotek. Jakie kryteria wykorzystuje pan podczas projektowania interfejsu API?

James: Moja zasada num er jeden to dążenie do tego, by interfejs API był jak najprostszy. Natomiast zasada numer dwa to projektowanie w oparciu o przypadki użycia. Jednym z częstych błędów popełnianych podczas projektowania interfejsu API jest traktowanie go jak worka na śmieci. Przystępując do projektowania, ktoś siada i myśli sobie: „A może ktoś użyje go w taki sposób, a ktoś inny w inny sposób” . W rezultacie interfejs API niepotrzebnie się rozrasta — staje się bardziej złożony, niż powinien być. Tymczasem pow inniśm y raczej zastanowić się nad odpowiedzią na pytanie: „Co użytkow nicy będą z tym robić?” . W arto przyjrzeć się innym systemom,

JAVA

3 63

w których ktoś rysował m enu lub nawiązywał połączenia sieciowe. W iadomo, jakie rozwiązania sprawdzały się w innych miejscach? Czego użytkownicy faktycznie używali, w przeciwieństwie do tego, co jest zaszyte w interfejsie API? Wiele interesujących wniosków na tem at projektow ania API oraz projektow ania języka można wyciągnąć dzięki statystycznej analizie oprogramowania napisanego w innych językach. Czy chciałby pan na podstawie swoich doświadczeń w projektowaniu języka Java przekazać innym projektantom jakąś ważną lekcję?

James: Projektowanie języków nie jest jakoś szczególnie trudne. Najważniejsze w tym wszystkim wydaje się nie samo projektow anie języka, ale udzielenie odpowiedzi na pytanie, do czego język ma służyć. W jakim kontekście będzie używany? Jakie zadania będą realizowali jego użytkownicy? Język Java wyróżnia się przede wszystkim sposobem wykorzystania sieci. Jakie są implikacje pracy sieciowej dla projektu języka programowania? Okazuje się, że dość poważne. W pewnym sensie decyzje projektowe dotyczące języka były dość proste, kiedy wzięliśmy pod uwagę implikacje pracy sieciowej. Czy Java wpłynęła na sposób, w ja k i zaczęto postrzegać problem niezależności od platformy?

James: Nie sądzę, aby użytkownicy komputerów zastanawiali się nad niezależnością od platformy? Uważam, że jest to jedna z tych zabawnych dziwnych rzeczy, o których z mojego p unk tu w idzenia — inżyniera budującego systemy wielkiej skali — użytkownicy nie powinni nic wiedzieć. Wsuwając kartę Visa do czytnika bankomatu, nie zdajemy sobie sprawy z tego, że w tle działa olbrzymia ilość kodu Javy na maszynach Sun, IBM, Dell, HP, kom puterach o architekturze x86 czy PowerPC. Wiele z tych m echanizm ów działa niejako za kulisami. Systemy bazujące na Javie m ożna spotkać naw et w metrze. Kiedy używamy kart zbliżeniowych, na przykład kart Oyster w londyńskim metrze, faktycznie używamy systemu napisanego w Javie. W rzeczywistości korzystam y z m echanizm ów niezależności od sprzętu. Gdyby użytkownicy byli zmuszeni do bycia świadomymi tego, jakiego języka programowania użyto do stworzenia systemu, byłaby to całkowita porażka tego systemu. Jednym z naszych celów jest zapewnienie całkowitej przezroczystości, tak by użytkownicy zupełnie nie dostrzegali obecności języka. Oczywiście to sprawia, że ludzie zajmujący się marketingiem dostają szału. Chcieliby, aby logo Javy pojawiało się przy każdym wejściu do wagonu londyńskiego metra. To jednak byłoby niedorzeczne.

364

ROZDZIAŁ

DWUNASTY

ROZDZIAŁ TRZYNASTY

C#

Kiedy firma Sun Microsystems wytoczyła proces sądowy firmie Microsoft z powodu zmian wprowadzonych w języku programowania Java, kierownictwo Microsoftu zwróciło się do weterana w dziedzinie projektowania języków — Andersa Hejlsberga — z prośbą o zaprojektow anie nowego języka obiektowego bazującego na rozbudowanej maszynie w irtualnej. W efekcie powstał język C# — następca zarówno języka Visual C + + , ja k i Visual Basic w obrębie ekosystemu Microsoft. Chociaż porównania do Javy na poziomie składni, implementacji i semantyki są wciąż nieuchronne, sam język zmienił się w stosunku do swoich korzeni — zaabsorbował własności z języków funkcyjnych, takich ja k Haskell i ML.

365

Język i jego projekt Stworzył pan i rozwijał kilka języków . Rozpoczął pan ja k o jeden z twórców implementacji Turbo Pascala. Czy to naturalna droga — od programisty tworzącego implementację do projektanta języka?

Anders Hejlsberg: Myślę, że to bardzo naturalny kierunek. Pierwszy kompilator, który napisałem, był kompilatorem dla okrojonego języka Pascal. Wtedy Turbo Pascal był pierwszą prawie kompletną implementacją Pascala. Pascal był jednak zawsze uważany za język edukacyjny. Brakowało w nim wielu własności, które są niezbędne do pisania praktycznych aplikacji. Aby język mógł zaistnieć na rynku, musieliśmy go rozszerzyć na różne sposoby. To zaskakujące, że język edukacyjny odniósł taki sukces. Stał się pomostem pomiędzy zastosowaniami edukacyjnym a komercyjnymi.

Anders: Istnieje wiele różnych języków edukacyjnych. Jeśli spojrzymy na historię Niklausa W irtha, który najpierw zaprojektow ał Pascala, a później języki M odula i Oberon, zauważymy, że zawsze cenił on prostotę. Języki edukacyjne mogą mieć taki charakter, ponieważ sprawdzają się w roli mechanizmów nauczania określonych pojęć, ale nie są to inne języki niż pozostałe. Mogą to być kom pletne języki, które uczą podstaw programowania. Taki zawsze był cel Pascala. Wydaje się, że istnieją dwie szkoły. W niektórych uczelniach — na przykład M IT —

nauka programowania zaczyna się od języka Scheme. Inne uczelnie stosują bardziej

praktyczne podejście. Przez jakiś czas nauczano w nich języka C + + . Teraz ucząjavy, a niektóre języka C#. Który język pan by zastosował?

Anders: Zawsze byłem zwolennikiem praktycznego podejścia. Jestem w większym stopniu inżynierem niż naukowcem. Uważam, że jeśli nauczamy czegoś, powinno to być coś, co może kiedyś przydać się do wykonania pewnych praktycznych zadań. Tak jak to zwykle bywa, nie istnieje jedno ekstrem alne podejście. Najlepsze są rozwiązania pośrednie. W przypadku implementacji języków programowania w ykorzystywanych w praktyce branżowej zapożyczamy idee, nad którym i są prowadzone badania naukowe. Obecnie możemy zaobserwować wielkie żniwa idei programowania funkcyjnego, rozwijanego w uczelniach od Bóg wie jakiego czasu. Myślę, że obie szkoły są właściwe. Czy pańska filozofia projektowania języków polega na pobieraniu pomysłów z różnych miejsc i realizowaniu ich w praktyce?

Anders: W pewnym sensie tak. Myślę, że trzeba wyjść od pewnej zasady przewodniej. Dobrą regułą przew odnią zawsze jest prostota. Poza tym jestem fanem ewolucji. Ewolucja zawsze jest lepsza od rozpoczynania wszystkiego od początku.

366

ROZDZIAŁ

TRZYNASTY

Zdarza się, że ktoś zakochuje się w jakimś konkretnym pomyśle, a następnie w celu jego zaimplementowania tworzy zupełnie nowy język. Następnie okazuje się, że 90% tego, co musi mieć każdy język, jest kiepskiej jakości. Takich przypadków zdarza się bardzo wiele, tymczasem gdyby zamiast tego rozwijać istniejące języki — na przykład ostatnio w języku C# wprowadziliśm y wiele zm ian w kierunku program ow ania funkcyjnego — wszyscy by na tym skorzystali. Znalazłoby się wielu użytkowników, którzy chętnie skorzystaliby z nowych własności. Co prawda trzeba zapłacić pewien podatek od złożoności, ale koszty są znacznie mniejsze niż wtedy, gdy dostosowanie się do określonego stylu programowania wymaga konieczności uczenia się od początku nowego języka i nowego środowiska wykonawczego. Czy trudno jest wyznaczyć linię pomiędzy samym językiem a jego ekosystemem?

Anders: Oczywiście, że tak, zwłaszcza współcześnie. Jeszcze 20, 30 lat tem u to język stanowił część najtrudniejszą do nauczenia się. Poznanie środowiska programowania polegało przede wszystkim na nauczeniu się języka. Każdy język zawierał niewielką bibliotekę. Było jeszcze kilka elementów systemu operacyjnego, o ile w ogóle można się było do niego dostać. Obecnie mamy do dyspozycji gigantyczne frameworki, takie jak .NET lub Java, oraz środowiska program ow ania zdom inow ane przez rozm iar interfejsów API frameworku. W takiej konfiguracji nauka samego języka wydaje się być tylko dodatkiem . Nie jest to do końca praw da, ale z całą pewnością nauka programowania w języku polega bardziej na poznaniu środowiska niż przyswojeniu języka i jego składni. Czy to sprawia, że większej wagi nabiera rola projektantów bibliotek?

Anders: Wielkiego znaczenia nabiera zadanie projektantów platform y, ponieważ platformę można wykorzystać w maksymalnym stopniu tylko wtedy, kiedy zapewni się jej długowieczność, a także zdolność do implementacji wielu różnych języków na jej bazie. Taka własność jest bardzo cenna. I tak .NET od samego początku zaprojektowano jako platformę wielojęzyczną. Dziś na jej bazie działają różne języki — statyczne, dynamiczne, funkcyjne, deklaracyjne (na przykład XAML) i co tam jeszcze m ożna sobie wyobrazić. Pod spodem jednak działa ten sam framework, to samo API, jego obszar zastosow ań jest przeogrom ny. Gdyby każdy z języków był autonom iczny, um arłyby one pow olną śmiercią z pow odu problem ów z interoperacyjnością oraz zużyciem zasobów. Czy to znaczy, że jest pan zwolennikiem maszyny wirtualnej, która jest poliglotą?

Anders: Myślę, że tak po prostu musi być. Aby wytłumaczyć mój pogląd, sięgnijmy na chwilę do czasów systemów 8-bitowych, które miały zaledwie 64 kB pamięci. Cała trudność sprowadzała się do zapełnienia tych 64 kB, a to następowało bardzo szybko. W tamtych czasach nie budowało się systemów przez kilka lat.

C#

3 67

Implementacja trwała miesiąc lub dwa i cała pamięć była zajęta. Zapełnienie 640 kB zajmowało może sześć miesięcy. Dziś pojemność pamięci to prawie studnia bez dna. Użytkownicy wymagają coraz więcej i więcej, i nie ma sposobu, aby przepisać od nowa całe istniejące oprogramowanie. Trzeba je wykorzystać i zapewnić współpracę nowego oprogramowania z istniejącym. W przeciwnym razie cały czas będziemy kręcili się w kieracie, próbując wykonywać podstawowe operacje. Dzięki stw orzeniu wspólnego podłoża m ożna uzyskać znacznie lepszy poziom interoperacyjności i wydajności. Jest to możliwe dzięki wykorzystaniu wspólnych usług systemowych. To działa w łaśnie w ten sposób. Weźmy na przykład zagadnienie zapewnienia interoperacyjności pomiędzy kodem zarządzanym i niezarządzanym. Jest z tym wiele problemów. Lepiej jednak rozwiązać je na poziomie platformy, niż miałyby być one rozwiązywane w każdym środowisku programistycznym z osobna. Najtrudniejsze do napisania są aplikacje hybrydowe, których część to kod zarządzany, a inna część to kod niezarządzany. Po jednej stronie płotu działa m echanizm odśmiecania (ang. garbage collection), a po drugiej nic takiego nie ma. Wydaje się, że celem projektowym maszyny JVM było założenie, aby nigdy nie naruszyć wstecznej zgodności z wcześniejszymi wersjami kodu bajtowego. To ogranicza pewne decyzje projektowe, jakie można podjąć. Można podjąć decyzję projektową na poziomie języka, ale na przykład we właściwej im plementacji typów generycznych trzeba skorzystać z techniki wymazywania typów (ang. type erasurej.

Anders: Myślę, że ich celem projektowym nie było wyłącznie zachowanie wstecznej zgodności. M ożna było dodawać nowy kod bajtowy i w dalszym ciągu zachować zgodność wstecz. Ich celem projektowym było uniknięcie konieczności dodawania czegokolwiek do kodu bajtowego w obrębie maszyny wirtualnej. To zupełnie coś innego. Celem projektowym było w zasadzie uniknięcie ewolucji języka. To jest bardzo duże ograniczenie. Podczas projektowania platformy .NET stawialiśmy sobie za cel projektow y zachow anie wstecznej zgodności. W związku z tym dodaliśm y nowe możliwości, nowe m etadane. W prow adziliśm y kilka now ych instrukcji, nowych bibliotek itp., ale wszystkie interfejsy API z .NET 1.0 w dalszym ciągu działały na platformie .NET 2.0. Nigdy nie mogłem zrozumieć, dlaczego projektanci JVM wybrali taką drogę. Mogę zrozumieć, że takie były cele doraźne, ale jeśli spojrzeć na historię tej branży, łatwo zauważyć, że ewolucja ma w niej olbrzymie znaczenie. Gdy ktoś rezygnuje z ewolucji, to jakby podpisał na siebie wyrok śmierci. To tylko kwestia czasu. Nasza decyzja o wprowadzeniu reifikowanych typów generycznych zamiast stosowania techniki wymazywania typów bardzo mi się podoba. Opłacało się ponieść koszty, które są z nią związane. Myślę, że cała praca, którą wykonaliśmy z technologią LINQ, nie byłaby możliwa bez reifikowanych typów generycznych. Wszystkie dynamiczne własności języka ASP.NET, wszystkie mechanizmy dynamicznego generowania kodu, które wykorzystujemy praktycznie we wszystkich produktach, opierają się na tym,

368

ROZDZIAŁ

TRZYNASTY

że typy generyczne są rzeczywiście reprezentowane w fazie wykonywania programu oraz istnieje symetria pomiędzy fazą kompilacji a środowiskiem wykonawczym. To jest niezwykle ważne. Jedną z cech, za którą krytykowano język Delphi, była silna niechęć do naruszania zgodności wstecz. To wpływało na pewne decyzje projektowe.

Anders: Spróbujmy cofnąć się o krok. Kiedy mówi pan o naruszaniu zgodności wstecz, to musi oznaczać, że mamy do czynienia z pewną ewolucją. Mówi pan o wersji N+ l pewnego produktu. Można by się spierać, że naruszanie wstecznej zgodności czasami jest dobre, ale w większości przypadków , kiedy kładłem wszystko na szali, nie potrafiłem uzasadnić konieczności takich naruszeń. Jedyne argum enty, jakie przemawiały za naruszaniem zgodności wstecz, nie były zbyt mocne. Było to coś w stylu: „W ten sposób jest czytelniej” lub „Z punktu widzenia architektury to podejście jest lepsze”, albo „To przygotuje nas lepiej na przyszłość” itp. Ja uważam, że czas życia platform y to może 10, 15 lat. Po tym czasie platform a przestaje istnieć w ten czy w inny sposób. Po 20 latach staje się przestarzała. W tym m om encie istnieje wystarczająco dużo nowych elementów bez żadnych dodatkowych nakładów. Jeśli chcemy coś złamać, najlepiej złamać to na dobre. Złamać wszystko. Przejść na pierwszą linię. Nie znoszę posuwać się o kilka stopni. To po prostu bezcelowe. To brzmi ja k zabawa w skaczące żabki, tyle że skok trwa 5 lub 10 lat.

Anders: Albo grasz w skaczące żabki, albo dbasz o zgodność wstecz i za każdym razem ciągniesz za sobą całą społeczność. Do pewnego stopnia tak właśnie jest w kodzie zarządzanym. Zawsze można korzystać z istniejących komponentów.

Anders: Od wprowadzenia platform y .NET zachowywaliśmy wsteczną zgodność przy każdym w ydaniu. Poprawiliśmy trochę błędów, co spow odow ało problem y z działaniem pewnego odsetka programów. Myślę jednak, że powinien istnieć pewien próg, do którego naruszanie zgodności wstecz jest dopuszczalne. W imię bezpieczeństwa, właściwego działania kodu lub z innych powodów czasami naruszamy zgodność wstecz. Takie przypadki są jednak rzadkie. Zwykle odkrywają one błędy projektow e w program ach użytkowników . Użytkownicy są zazwyczaj zadowoleni z możliwości poprawienia tych błędów, ponieważ nawet nie zdawali sobie sprawy z ich istnienia. W takim przypadku naruszenie zgodności wstecz jest dopuszczalne, natomiast naruszenie jej w imię ładniejszego kodu to błąd — tak myślę. Popełniałem go w początkowej fazie mojej kariery wystarczająco często, aby wiedzieć, że taki sposób postępowania prowadzi donikąd.

C#

369

Trudno dyskutować na temat dobrego smaku.

Anders: To prawda. Niestety tak jest. Mój dobry smak nie musi być pańskim dobrym smakiem. Czy w językach, w których projektowaniu pan uczestniczył — Turbo Pascalu, Delphi, J + + , Cool i C # — istnieją jakieś motywy? Kiedy posłucham jakiegoś wczesnego dzieła Mozarta, a później jego Requiem, mogę powiedzieć: „Obydwa te utwory to z pewnością Mozart".

Anders: W szystko jest obrazem czasów, w jakich żyjemy. W m oich czasach najważniejsze były techniki obiektowe. Wszystko, nad czym pracowałem, począwszy od jakiejś środkowej wersji Turbo Pascala aż do dziś, było w gruncie rzeczy językami obiektowymi. W miarę upływ u lat zachodziły ewolucyjne zmiany. W przypadku Delphi włożyliśmy sporo pracy w stworzenie modelu programowania zorientowanego na komponenty — składającego się z właściwości, zdarzeń itp. To przerodziło się w pracę, którą wykonałem w języku C#, i z całą pewnością mój styl jest rozpoznawalny. Zawsze staram się uważać na społeczność i dostarczać jej coś nowego. Turbo Pascal był nowatorskim środowiskiem program owania, natom iast Delphi było środowiskiem programowania wizualnego. C# i .NET dotyczyły środowisk zarządzanego w ykonyw ania kodu, bezpieczeństwa typów i tym podobnych m echanizm ów . Ze wszystkiego, co nas otacza, m ożna wynieść pewne lekcje — niezależnie od tego, czy z własnego ekosystemu, czy z ekosystemów konkurencyjnych. Zawsze należy starać się wydestylować to, co było dobre, oraz to, co się nie sprawdziło. W tym biznesie wszyscy stoimy na ram ionach gigantów. To fascynujące, jak powoli przebiega ewolucja języków programowania w porównaniu z ewolucją sprzętu. Postęp w sprzęcie jest zdumiewający. Od powstania języka Smalltalk-80 mieliśmy 15, a może nawet 2 0 generacji sprzętu!

Anders: Praktycznie co 18 miesięcy pow staw ała now a platform a. Co więcej, nie istnieje jakaś zasadnicza różnica pom iędzy językami program ow ania, jakich używamy dziś, a tymi, które były używane na przykład 30 lat temu. Cały czas trwa dyskusja dotycząca starych pojęć, na przykład funkcji wyższego rzędu w Javie. Ta debata prawdopodobnie potrwa jeszcze z 10 lat.

Anders: I wielka szkoda. Myślę, że ten problem należałoby rozwiązać znacznie szybciej. Nie uważam, że prawdziwym obszarem spornym jest to, czy jakieś własności są przydatne. W większym stopniu problem dotyczy zbyt skomplikowanego procesu wprowadzania zmian w społeczności Javy.

370

ROZDZIAŁ

TRZYNASTY

Przejdźmy do stylu programowania CPS (ang. Continuation Passing Style — dosł. styl przekazywania kontynuacji). Czy udostępnianie wywołania z bieżącą kontynuacją na poziomie języka daje jakieś korzyści? Czy jest pan zwolennikiem tego stylu, mimo że najwyżej 10% programistów zdoła go zrozumieć?

Anders: Tak, pod pewnymi warunkami — ale tych warunków jest sporo. Być może nie do końca jest to związane z pytaniem, ale proszę spojrzeć, co zrobiliśmy z technologią LINQ. Naprawdę wierzę, że skorzysta z niej duża część osób programujących w C#. Zdolność do pisania bardziej deklaracyjnego stylu zapytań oraz dostępność jednorodnych języków zapytań dla różnych rodzajów danych jest bardzo cenna. To tak jak uniw ersalny język oraz w pewnym sensie integracja z bazą danych. Być może nie rozwiązaliśmy tam całego problem u, ale sądzę, że zrobiliśmy wystarczające postępy, które uzasadniają konieczność dodatkowej nauki. Ponadto istnieją sposoby przedstawienia tego ludziom bez konieczności zm uszania ich do poznawania rachunku lambda. Sądzę, że to doskonały przykład praktycznego zastosow ania dla program ow ania funkcyjnego. M ożna z niego szczęśliwie korzystać i nie wiedzieć nawet, że używa się program ow ania funkcyjnego ani nie mieć pojęcia o tym, że istnieją reguły program ow ania funkcyjnego, które tym mechanizmem zarządzają. Jestem bardzo zadowolony z efektów, jakie uzyskaliśmy w tym zakresie. Użył pan słowa „praktyczne". W ja k i sposób podejmuje pan decyzję o tym, które własności będą dodane, a które usunięte? Jakie kryteria stosuje pan przy podejmowaniu decyzji o tym, co należy dodać, a co usunąć?

Anders: Nie wiem. Z czasem uzyskuje się umiejętność oceny tego, czy wprowadzenie jakichś zmian przyniesie wystarczające korzyści, aby zrównoważyć dodatkowe trudności związane z modyfikacjami. Otrzymujemy wiele interesujących propozycji od naszych użytkowników, którzy mówią: „Gdybyśmy tylko mogli zrobić to ” lub „Chciałbym zrobić ta m to ” . Często jednak są to propozycje za bardzo skoncentrow ane na rozwiązaniu jednego konkretnego problemu. W związku z tym przedstawiają niewielką wartość jako pojęcia abstrakcyjne. Z całą pewnością najlepsze języki udaje się zaprojektować m ałym grupom osób lub pojedynczym programistom. Czy istnieje różnica pomiędzy projektowaniem języka a projektowaniem biblioteki?

Anders: Bardzo duża. Interfejsy API są oczywiście znacznie bardziej specyficzne dla dziedziny zastosowań niż języki, a języki w rzeczywistości znajdują się o jeden poziom abstrakcji wyżej. Języki dają do dyspozycji framework: kwarki, atom y i m olekuły potrzebne do projektowania API. Decydują one o tym, w jaki sposób należy tworzyć interfejsy API, ale nie o tym, co API mają robić.

C#

371

Pod tym względem myślę, że istnieje duża różnica. N asunęło mi to pew ną myśl dotyczącą poprzedniego pytania. Za każdym razem, kiedy zastanaw iam się nad dodaniem nowej własności do języka, staram się, by miała ona zastosowanie dla więcej niż jednej dziedziny. Siła dobrej własności języka polega na możliwości jej użycia na więcej niż jeden sposób. Tak jak wcześniej, posłużę się przykładem technologii LINQ. Gdybyśmy rozdzielili na czynniki pracę, którą wykonaliśmy podczas opracowywania technologii LINQ, okazałoby się, że składa się ona z sześciu lub siedmiu własności języka, takich jak metody rozszerzające, rachunek lambda, wnioskowanie typów itd. Następnie można je wszystkie połączyć ze sobą w celu stworzenia nowego rodzaju interfejsu API. W szczególności można stworzyć mechanizmy zapytań implementowane jako interfejsy API, ale same własności języka są użyteczne dla całego szeregu innych rzeczy. Programiści używają metod rozszerzających do wykonywania wielu interesujących działań. W nioskow anie typu lokalnych zm iennych jest bardzo interesującą własnością itd. Praw dopodobnie moglibyśmy stworzyć coś takiego jak LINQ znacznie szybciej, gdybyśmy powiedzieli: „Spróbujmy zagnieździć tu SQL lub coś, co jest całkowicie specyficzne dla technologii SQL Server. Dzięki temu będziemy mogli komunikować się z bazą danych” . Nie jest to jednak w łasność wystarczająco ogólna do tego, by uzasadnić jej istnienie w języku program ow ania ogólnego przeznaczenia. W ten sposób bardzo szybko stworzylibyśmy specjalizowany język programowania, z którym bylibyśmy związani na śmierć i życie. Przekształcił pan język 3GL w 4GL, a to jest równoznaczne ze śmiercią języka ogólnego przeznaczenia.

Anders: To prawda. Jestem tego świadomy. Obecnie jedną z ważniejszych własności, nad którymi pracujemy jest współbieżność. Wszyscy zajmują się współbieżnością, poniew aż są do tego zmuszeni. To nie jest kwestia dobrej woli, to po prostu konieczność. Moglibyśmy spowodować, aby język dyktował określony model współbieżności, ale takie podejście nie byłoby właściwe. Trzeba wejść na wyższy poziom. Znaleźć takie własności, których brakuje w języku. Takie, które pozwolą implementować dobre biblioteki obsługi współbieżności oraz tworzyć doskonałe modele programowania współbieżności. Potrzebujemy w języku mechanizmów, które zapewnią lepszą izolację stanów. Potrzebujemy czystości funkcji. Potrzebujemy niezmienników w postaci podstawowych pojęć. Gdybyśmy mogli dodać te własności jako podstaw ow e pojęcia, moglibyśmy pozostawić je projektantom systemów operacyjnych i frameworków do eksperymentowania z różnymi modelami współbieżności, to oni bowiem potrzebują tych mechanizmów. W takim przypadku nie musielibyśmy zgadywać, kto będzie zwycięzcą. Zamiast tego moglibyśmy odrzucić jeden z modeli, gdyby się okazało, że inny jest lepszy.

372

ROZDZIAŁ

TRZYNASTY

Bylibyśmy przygotowani na wszystkie ewentualności. Brzmi to tak, jakby chciał pan dać ludziom narzędzia do tworzenia wspaniałych rzeczy, zamiast dyktować im, co powinni zrobić.

Anders: Tak właśnie chciałbym postępować. W ten sposób m ożna znacznie lepiej wykorzystać nowatorstwo społeczności. Czy widzi pan przejawy nowatorstwa w społeczności języka C#? Czy użytkownicy dostarczają panu kod? Czy odwiedza pan klientów? Czy pańscy współpracownicy przeglądają grupy dyskusyjne i mailingowe?

Anders: Myślę, że wszystkiego po trochu i jeszcze nieco więcej. W ykorzystujemy m echanizm y współdzielenia kodu, na przykład Codeplex. Istnieje wiele rodzajów społeczności. Jest społeczność użytkowników komercyjnych. Jest społeczność open source. Istnieje mnóstwo kodu open source napisanego dla platformy .NET. Kod można znaleźć wszędzie. Nie powiedziałbym, że istnieje pojedyncze źródło napływu kodu. To zróżnicowany i złożony ekosystem. Wszędzie m ożna spotkać interesujące rzeczy. Kiedy je spotkamy, zachwycamy się: „Jak im się udało to zrobić?” czy też „To w spaniałe” . Ktoś włożył w to mnóstwo pracy. Być może nie przedstawia wartości komercyjnej, ale to kawał dobrej roboty. Często staram się przeglądać błogi związane tematycznie z językiem C# i technologią LINQ. To moje ulubione słowa kluczowe, których używam, kiedy zaglądam na błogi, by zobaczyć, co się tam dzieje. Dzięki temu mogę się dowiedzieć, czy ludzie prawidłowo korzystają z mojej pracy, czy nie. To jest nauka na przyszłość.

Rozwój języka W ja k i sposób rozpoznaje pan prostotę?

Anders: Istnieje prawdziwa prostota oraz coś, co ja nazywam prostotą pozorną. Z nią spotykam się bardzo często. Mamy z nią do czynienia w przypadku, gdy ktoś zrobi coś bardzo złożonego, a następnie stwierdzi: „Nikt nigdy nie będzie w stanie z tego skorzystać. To o wiele za bardzo skom plikow ane, ale drzemią w tym olbrzymie możliwości. Spróbujmy utworzyć na bazie tego prosty system. Spróbujmy opakować to prostym interfejsem” . Wówczas, gdy trzeba zrobić coś, do czego system nie był przeznaczony, następuje kompletna klapa. Napotykamy wielką złożoność, ponieważ to, na co patrzyliśmy, było tylko cienką woalką nad czymś, co jest bardzo złożone. Nie wiem, czy wyrażę się dość jasno, ale o prostocie myślę w następujący sposób: prostota często oznacza, że robimy więcej przy pom ocy mniejszej ilości zasobów. Zasobów jest mniej, ale w ykonują

C#

3 73

te same działania, co inne mechanizmy, lub nawet znacznie więcej. Wszystko polega na zrobieniu więcej, gdy ma się do dyspozycji mniej. Prostota nie polega na robieniu więcej za pomocą większej ilości zasobów i tworzeniu na wierzchu prostej warstwy. Czy postępowałby pan zgodnie z tą zasadą, gdyby dziś miał pan stworzyć nowy język programowania?

Anders: Ależ tak. Do tej pory stworzyłem dużo języków program owania lub ściśle rzecz biorąc, wykonałem wiele implementacji. Myślę, że bardzo ważne jest, aby przed przystąpieniem do tworzenia nowego języka programowania mieć jasność co do tego, dlaczego coś się robi i jaki jest problem do rozwiązania. Częstą pom yłką popełnianą przez twórców nowych języków program ow ania jest to, że zbyt ściśle wiążą się z problem em , który chcą rozwiązać. Być może język programowania jest właściwym instrumentem do rozwiązania tego problemu, zatem projektanci języków rozwiązują ten problem i być może wykonują dobrą robotę. Trzeba pamiętać, że każdy język programowania zawiera 10% nowości i 90% konstrukcji, które można nazwać chlebem powszednim programowania i które muszą w języku pozostać. Wiele nowatorskich rozwiązań, które można znaleźć w nowych językach programowania, jest doskonałych w zakresie tych 10% nowości, ale fatalnych w 90% tego, co musi się znaleźć w każdym języku, aby za jego pomocą naprawdę można było pisać programy. Z tego powodu nowe języki programowania nie odnoszą sukcesu. Trzeba zrozumieć, że istnieje wiele nudnych standardowych konstrukcji, które muszą się znaleźć w każdym języku programowania. Jeśli ktoś nie zadba o ich prawidłową implementację, nie odniesie sukcesu. Z drugiej strony oznacza to, że jeżeli można zmodyfikować istniejący język program ow ania, zamiast tworzyć nowy, rów nanie wygląda zupełnie inaczej. Wtedy bowiem mamy już rozwiązanych 90% problemów. W łaściwie rozw iązanych jest 100% problem ów . Próbujemy dodać tylko kilka uspraw nień. Tak ja k w przypadku C + +?

Anders: Tak jak C++, który jest wspaniałym przykładem ewolucji języka C, a także różne wersje języka C#, które zaim plem entow aliśm y, oraz jeszcze wiele innych przykładów. Jestem wielkim zwolennikiem ewolucji. Oczywiście w pewnym momencie nadchodzi taka chwila, że już nie można dodawać nowych rzeczy — są zbyt wielkie tarcia pomiędzy now ym i rzeczami, które dodajemy, a starym sposobem ich w ykonywania w języku, tak że dalsze dodawanie staje się po prostu niemożliwe. Stworzenie nowego języka jest w większym stopniu wyjątkiem od reguły niż regułą. Czy stworzyłby pan język ogólnego przeznaczenia, czy język dziedzinowy?

Anders: Myślę, że właściwa odpowiedź na pańskie pytanie pow inna brzmieć: „Ani jednego, ani drugiego” . Stworzyłbym raczej język programowania ogólnego przeznaczenia, który doskonale się nadaje do tworzenia języków dziedzinowych.

374

ROZDZIAŁ

TRZYNASTY

Kłopot z językami dziedzinowymi jest taki, że dobrze nadają się do rozwiązywania problemów z danej dziedziny, ale nie są uniwersalne. Istnieją określone własności języków ogólnego przeznaczenia, które są potrzebne prawie w każdym języku dziedzinowym. Jest tak pod w arunkiem , że język dziedzinowy nie jest po prostu językiem definiowania danych, w którym tylko definiujemy strukturę danych. W takim przypadku lepiej skorzystać z języka XML. Jeśli tworzymy rzeczywisty język programowania, w którym jest logika, predykaty, reguły lub cokolwiek, to potrzebne są nam wyrażenia. W yrażenia m ają z kolei operatory. Być może trzeba również zdefiniować funkcje standardowe. Użytkownicy m ogą chcieć wykonywać działania, o których nigdy byśmy naw et nie pomyśleli. Jest wiele standardowych konstrukcji, które są potrzebne. Myślę, że znacznie lepiej jest wówczas, gdy istnieje możliwość stworzenia języka dziedzinowego na bazie języka uniwersalnego. To znacznie bardziej kom fortow a sytuacja w porów naniu z koniecznością zaczynania wszystkiego za każdym razem od początku. Jednym z problem ów, jakie wiążą się dziś z językami uniw ersalnym i, jest to, że co praw da są coraz lepsze w tw orzeniu w ew nętrznych języków dziedzinowych — czego przykładem jest LINQ — ale trudno znaleźć właściwy schemat użycia tych wewnętrznych języków dziedzinowych. W przypadku tworzenia wewnętrznego języka dziedzinowego do pewnego stopnia ograniczamy możliwości uniwersalnego języka programowania. Lepiej by było, gdyby można było wyłączyć uniwersalność języka i skupić się tylko na pewnych obszarach języka dziedzinowego. To jeden z obszarów, w których uniwersalne języki programowania niezbyt dobrze się sprawdzają. W arto by się przyjrzeć tej sytuacji. Brian Kernighan powiedział, że jeśli ktoś chce stworzyć język programowania ogólnego przeznaczenia, powinien zacząć projektowanie, mając ten cel na myśli. W przeciwnym razie, jeśli stworzymy niewielki język, natychmiast po tym, kiedy użytkownicy zaczną go używać, będą prosili o dodawanie do niego nowych własności. Rozwijanie języków dziedzinowych niezbyt dobrze się sprawdza.

Anders: Ach tak. Chyba Gosling powiedział, że każdy plik konfiguracyjny w efekcie staje się osobnym językiem programowania. To prawda. Trzeba na to zwracać baczną uwagę. Powiedział pan, że pod pewnymi względami platforma jest ważniejsza niż sam język. Czy należy tworzyć komponenty wielokrotnego użytku?

Anders: Powodem, dla którego to powiedziałem, jest fakt, że jeśli przyjrzymy się ewolucji języków programowania, narzędzi i frameworków w ciągu ostatnich 25 - 30 lat, zauważymy, jak niewiele zmieniły się języki programowania. Jednocześnie łatwo zauważyć, o ile bardziej rozbudowane stały się frameworki, których używamy, oraz jak wydłużył się czas wykonywania. Te czynniki są praw dopodobnie o trzy rzędy wielkości większe dziś, niż były, powiedzmy, 25 lub 30 lat temu. Kiedy zaczynałem

C#

375

z Turbo Pascalem, jego biblioteka wykonawcza zawierała 100, a może 150 funkcji standardowych i tyle. Teraz mamy framework .NET zawierający 10 000 typów oraz łącznie ponad 100 000 składowych. Oczywiście wykorzystanie całej tej pracy staje się coraz ważniejsze. Istotne dlatego, że kształtuje sposób, w jaki myślimy o problemach, przy czym jednak sam framework staje się coraz ważniejszy, ponieważ to on jest wykorzystywany w naszych programach. Wykorzystanie gotowych komponentów ma dziś olbrzymie znaczenie. Z perspektywy programowania kom puter jest studnią bez dna. Mógłbyś zacząć pisać kod od zaraz i robić to do dnia swojej śmierci, a i tak nigdy nie zdołałbyś go zapełnić. Możliwości są bardzo duże, a oczekiwania użytkowników coraz bardziej wyszukane. Jedynym sposobem na osiągnięcie sukcesu jest znalezienie inteligentnych sposobów na wykorzystywanie pracy, która została w ykonana wcześniej. Kiedyś, 25 - 30 lat temu, sytuacja była inna. Do dyspozycji było 64 kB pamięci. To bez trudu dało się zapełnić w ciągu jednego lub dwóch miesięcy. W ja k i sposób język wpływa na wydajność programisty, a ja k ą rolę odgrywają pod tym względem jego indywidualne zdolności?

Anders: Myślę, że język i zdolności programisty idą ze sobą ręka w rękę. Sądzę, że język ma wpływ na sposób naszego myślenia. Zadaniem programisty jest samo myślenie. To jest surowiec — surowy materiał, który jest wykorzystywany w tym procesie. Język jest rzeczą, która wpływa na nasze myślenie — jego funkcją jest pomoc w myśleniu w sposób produktywny. Na przykład języki obiektowe wymagają myślenia o problemie w określony sposób. Z kolei języki funkcyjne każą myśleć o problem ie zupełnie inaczej. Jeszcze innego sposobu myślenia wymagają języki dynamiczne. Są to różne kapelusze, których włożenie sprawia, że zaczynamy inaczej myśleć. Czasami warto przymierzyć dwa kapelusze i wypróbować różne podejścia. Czy według pana lepiej jest dodać taką własność języka, która powoduje pewien wzrost wydajności wszystkich programistów, czy też taką, która znacznie podnosi wydajność kilku programistów?

Anders: W przypadku języków uniwersalnych dodawanie własności przydatnych tylko dla kilku osób nie jest dobre, ponieważ z tego powodu język staje się śmietnikiem złożonym z dziwacznych konstrukcji. Dobra własność języka charakteryzuje się tym, że ma wiele przydatnych zastosowań, a nie tylko jedno. Spójrzmy na wszystko to, co dodaliśmy do języka C# 3.0. To, co wspólnie tworzy koncepcję technologii LINQ (od ang. Language-Integrated Query). Sprowadza się to do sześciu lub siedmiu dyskretnych własności języka, które mają wiele przydatnych zastosowań. Nie służą one tylko jednemu, konkretnemu programiście. Są one na znacznie wyższym poziomie abstrakcji. Dla każdej dobrej własności języka powinno być możliwe pokazanie tego, w jakich scenariuszach jej użycie będzie korzystne. Jeśli okazuje się to niemożliwe, najpraw dopodobniej umieszczenie tej własności w języku było błędem. Być może lepiej by było, gdyby własność ta była dostępna za pośrednictwem API.

376

ROZDZIAŁ

TRZYNASTY

Czy myśli pan nad tym Jakie własności należy dodać bądź usunąć z języka, aby ułatwić debugowanie? Czy uwzględnia pan problemy debugowania w procesie projektowania języka?

Anders: O, z całą pewnością! Jeśli przyjrzymy się podstawowym założeniom języka C#, zauważymy, że jest to język bezpieczny pod względem typów, w którym nie występują takie zjawiska, jak przekroczenie indeksu tablicy czy też bezpański wskaźnik. Wszystko ma ściśle zdefiniowane działanie. W języku C# nie ma takiego pojęcia, jak niezdefiniowane działanie. Obsługa błędów bazuje na wyjątkach w odróżnieniu od kodów błędów, które m ożna zignorować. Wszystkie w prow adzone przez nas mechanizmy, takie jak bezpieczeństwo typów, bezpieczeństwo pamięci oraz obsługa wyjątków, bardzo pom agają wyeliminować całe klasy błędów lub ułatw iają ich znalezienie. To jest własność, o której myśleliśmy przez cały czas. W ja k i sposób stara się pan przeciwdziałać tym powtarzającym się problemom bez ograniczania możliwości programistów? Jak dokonuje się wybór między bezpieczeństwem a swobodą programisty?

Anders: Myślę, że każdy język można umieścić gdzieś na wykresie przedstawiającym zależności możliwości od wydajności. Język C# jest ewidentnie znacznie bezpieczniejszym i bardziej chronionym środowiskiem niż C++, a ten z kolei jest znacznie bezpieczniejszy od kodu asemblera. Ogólny trend dla języków programowania w całej historii polegał na podnoszeniu w górę poziomu abstrakcji oraz na zwiększeniu bezpieczeństwa środowisk programistycznych. Trzeba było również przekazywać coraz więcej pracy wykonywanej przez programistów w ręce maszyn. Dzięki temu programiści mogą skoncentrow ać się na kreatywnej stronie procesu, a to z całą pewnością dodatkow a wartość. Programiści nie potrafią praw idłow o zarządzać pamięcią. Nie potrafią również odpowiednio dbać o bezpieczeństwo typów. Z tego wynikają błędy. Myślę, że przekazanie tych zadań w ręce maszyny, a pozostawienie program istom kreatywnej części procesu jest dobrym kompromisem. Trzeba ponieść pewne koszty w postaci obniżonej wydajności, ale nie są one takie wysokie. Jeśli dzisiaj w typowej aplikacji .NET stworzymy profil wykonania programu i zwrócimy uwagę na to, gdzie program spędza swój czas, okaże się, że proces odśmiecania pojawia się bardzo rzadko. Pomimo to program jest bezpieczny i nie ma w nim wycieków pamięci. To bardzo interesujące zjawisko. To fantastyczne w porów naniu z tym, co trzeba było robić w systemach z ręcznym zarządzaniem pamięcią, na przykład w C++ lub C. Czy można by zastosować naukowe podejście do sposobu, w jaki projektuje się i rozwija język? W implementacji można zauważyć usprawnienia wynikające z badań, ale projekt języka, ja k się wydaje, jest sprawą osobistych preferencji projektanta.

Anders: Myślę, że projekt języka programowania to interesująca kombinacja sztuki i nauki. Z całą pewnością jest w nim sporo nauki. Matematyczny formalizm w notacji

C#

3 77

potrzebny do parsow ania, semantyki oraz systemu typów , a także generow ania kodu i innych mechanizmów. W języku jest bardzo dużo nauki. A także mnóstwo inżynierii. Istnieje też aspekt sztuki. Jakie są odczucia związane z językiem? Jaki proces zachodzi w naszych głowach w czasie, kiedy programujemy w tym języku, oraz co go wyróżnia? Co będzie łatwe do zrozumienia, a co będzie sprawiało trudności? Nie sądzę, abyśmy kiedykolwiek potrafili to zmierzyć. Projektowanie języka nigdy nie będzie przedsięwzięciem czysto naukowym. Zawsze w projektowaniu języka będzie występował pierwiastek sztuki. Tak jak istnieją dobre i złe obrazy i jak w pewnym sensie naukow o można stwierdzić: „Kompozycja nie została właściwie wykonana. Być może artysta posłużył się nieodpowiednią techniką” . Ostatecznie jednak ocena piękna zawsze będzie subiektywna. Istnieją takie jego elementy, których nie można sformalizować. Czy sądzi pan, że umiejętność porozumiewania się w co najmniej dwóch językach w jakimś stopniu może pomóc w projektowaniu języków programowania? Czasami potrafię po włosku opisać pojęcie jednym słowem, tymczasem aby powiedzieć to samo po angielsku, potrzebuję całego zdania. Oczywiście czasami jest odwrotnie.

Anders: Nie wiem. To dobre pytanie. Nigdy o tym nie myślałem. Możliwe, że taki związek zachodzi. Uważam, że aby być dobrym projektantem języków programowania, trzeba rozumieć wiele języków program ow ania. Co do tego nie m am żadnych wątpliwości. Czy znajom ość m ów ionych języków w jakimś sensie pom aga — nie wiem. Być może istnieją takie związki. W zespole projektowym z pewnością są osoby, które mówią wieloma językami, lub takie, które mają zdolności muzyczne. W ydaje się, że zdolności lingwistyczne oraz muzyczne są w pew nym sensie powiązane z umiejętnościami projektowania języków. Nie jestem jednak pewien, jaki jest charakter tego związku.

c# Jak długa jest przyszłość języka C#? Pan zajmuje się nim od ponad 10 lat!

Anders: Projekt języka C# rozpoczął się pod koniec grudnia 1998 roku, a zatem minęła już dziesiąta rocznica. Nie jest to 10 lat obecności w branży, ale 10 lat od chwili narodzenia się pomysłu. Zaryzykowałbym stwierdzenie, że przed nami jest co najmniej następnych 10 lat, ale to zależy od wielu okoliczności. Już dawno zaprzestałem prób przewidywania dalekiej przyszłości w tej branży, ponieważ nigdy nie udawało mi się jej poprawnie przewidzieć. Ale z całą pewnością widzę dobrą, zdrową przyszłość dla języka C#. Nie zaprzestaliśmy wprowadzania usprawnień. W dalszym ciągu jest bardzo dużo do zrobienia.

378

ROZDZIAŁ

TRZYNASTY

Kiedy patrzę na ewolucję języka C # z punktu widzenia projektowania aplikacji, zauważam dążenie do tego, by języ k C # zastąpił język C+ + w roli narzędzia programowania systemowego.

Anders: Język C# może być wykorzystywany do tego celu, ale jest wiele zastosowań, dla których zarządzane środowisko wykonawcze, takie jak .NET lub Java, bardziej się nadaje. Kiedy porównam język C# do Javy, to wydaje mi się, że język C# w większym stopniu podlega ewolucji. Wydaje się, że projektanci Javy dążą do stworzenia takiej podstawy, aby każdy kod wyglądał w mniejszym lub większym stopniu tak samo. Niezależnie od tego, czy programista programuje w Javie od dekady, nigdy wcześniej nie programował, czy właśnie ukończył sześciomiesięczny kurs Javy, cały kod będzie wyglądał tak samo. Z kolei język C# zapożycza nowe idee z Haskella lub języka F#. Czy istnieje dążenie do dodawania takich nowych własności, których nie zdołają natychmiast dostrzec i zrozumieć ludzie po sześciomiesięcznym kursie języka C#?

Anders: Ująłbym to w ten sposób: nie zajmuję się językiem C# po to, by stworzyć nową wersję COBOL-a. Na czym polega siła rewolucji internetowej oraz rewolucji elektronicznej, których jesteśmy świadkami? Polega na ciągłej ewolucji. Myślę, że na tym polega największa siła tej rewolucji. Gdy ktoś przestanie ewoluować, przestaje wnosić jakąkolwiek wartość. Jest to oczywiście podejście ekstremalne. Oczywiście stabilność platformy także ma swoją wartość, ale uważam, że tę wartość dostarczamy, przede wszystkim zapewniając zgodność wstecz. Mamy możliwość wyboru. Możemy zatrzymać autobus na wersji C# 1.0 i nie posunąć się ani kroku dalej. Te osoby, które napraw dę chcą być bardziej produktyw ne i budow ać now e rodzaje aplikacji, takie jak SOA lub jakiekolwiek inne, oraz chcą wykorzystać bardziej dynamiczne style programowania — adaptowalne programy oraz bardziej deklaracyjne style programowania niż te, które zastosowaliśmy w technologii LINQ — muszą ewoluować lub zejść z drogi. Rezygnacja z ewolucji sprawi, że jakieś inne narzędzie zapełni lukę, którą pozostawimy. Czy otrzymuje pan jakieś uwagi dotyczące samego języka C#, a nie implementacji?

Anders: Codziennie dociera do nas wiele uwag na tem at języka. Otrzymuję e-maile, czytam błogi prow adzone przez użytkow ników języka, czytam fora, na których użytkownicy zadają techniczne pytania, uczestniczę w konferencjach — wszelkimi możliwymi kanałami otrzymuję codziennie uwagi na temat tego, co w języku działa, a co nie działa. Uwagi te trafiają do zespołu projektowego, który zbiera wszystkie szalone pomysły. Niektóre z nich nigdy nie znajdą się w języku, ale utrzym ujemy je na liście, ponieważ kiedyś może się pojawić jakiś dobry pomysł związany z danym tem atem . Wiemy, że nie do końca jeszcze wszystko działa tak, jak pow inno, ale istnieje dążenie do tego, by coś zrobić.

C#

379

Następnie stopniowo znajdujemy rozwiązania problemów. Niektóre z nich to proste rzeczy. Ludzie o nie proszą, a my je wykonujemy. Inne rzeczy, o których rzadko się mówi, takie jak LINQ, sprawiają większy problem. Nigdy się nie zdarzyło, żeby ktoś przyszedł i powiedział: „Chcielibyśmy, aby język zawierał w budow aną obsługę zapytań” . Zwykle bowiem nie myśli się o jakiejś konkretnej konstrukcji. Nie powiedziałbym, że istnieje jeden konkretny sposób, w jaki trafiają do nas różne spostrzeżenia. To bardzo organiczny proces, a uwagi trafiają do nas z wielu różnych miejsc. Oczywiście nie ma możliwości, aby udało się zaprojektować język bez tych wszystkich uwag, a zatem proces rozw oju produktu bazuje na słuchaniu tego, co użytkownicy z nim robią. W ja k i sposób zarządza pan zespołem projektowym? W ja k i sposób podejmuje pan decyzje?

Anders: Przede wszystkim klienci, którzy dzielą się z nami swoimi uwagami, mówią: „Byłoby świetnie, gdyby mógł pan dodać tę konkretną własność” . Kiedy zaczynamy analizować problem, okazuje się, że próbują zrobić taką czy inną rzecz. Zwykle też użytkownicy mówią, co ich zdaniem mogłoby rozwiązać problem. Od tej chwili twoim zadaniem jest stwierdzenie, na czym polega prawdziwy problem , i dopasow anie go do większego frameworku języka. W pewnym sensie pierwszą część zbierania uwag m ożna porów nać do pracy detektywa. Jej celem jest zrozumienie, na czym polega rozwiązanie problemu wskazywanego przez użytkownika. Trzeba stwierdzić, na czym polega ich prawdziwy problem? Następnie należy zdecydować, co powinniśmy zrobić z tym problemem. Przy rozwijaniu języka zawsze należy uważać, aby nie dodawać do niego zbyt dużo własności, ponieważ im więcej ich dodamy, tym bardziej postarzymy ten język. Ostatecznie język po prostu tonie pod swoim własnym ciężarem. Jest w nim zbyt wiele różnych elementów — zbyt wiele konstrukcji, które ze sobą kolidują. Podczas dodawania własności do języka należy zachować rozsądek, ponieważ nikt nie chce, aby w języku istniały trzy różne sposoby wykonywania tej samej czynności, do czego dochodzi w języku wyłącznie z powodów historycznych. W związku z tym wielokrotnie mówimy tak: „Gdybyśmy mogli zacząć od początku, z całą pewnością dołączylibyśmy własność, o którą ludzie proszą” . Ponieważ nie możemy zacząć od początku, nie dodajemy nowej własności, jej wprowadzenie byłoby bowiem zbyt radykalne, a nie m ożna zasadniczo zmienić charakteru bestii poprzez podaw anie jej pieprzu. M ożna jedynie spowodować, że wyrośnie jej druga głowa, a tego nie chcemy. Jeśli chodzi o sam proces projektowy, to mamy bardzo silny zespół projektowy języka C# składający sie zwykle z sześciu do ośmiu osób, które regularnie się spotykają. Spotykamy się regularnie od 10 lat, w każdy poniedziałek, środę i piątek, w godzinach 13:00 - 15:00. N iektóre spotkania nie doszły do skutku, ale są to term iny, które

380

ROZDZIAŁ

TRZYNASTY

wszyscy mamy od dekady w swoich kalendarzach, i zanosi się, że w dalszym ciągu tam pozostaną. Ludzie biorący udział w projekcie zmieniają się. Ja byłem przez cały czas. Scott W iltam uth był przez większą część czasu. Inni przychodzą i odchodzą, ale proces trwa od 10 lat. Spotkania zespołu stały się częścią praktyki projektowej. Właśnie na nich wykonujemy bieżące zadania projektow e. Dla ciągłości p roduktu istotne znaczenie ma to, aby projektowanie było procesem ciągłym. Często się zdarza, że w projektach następują zrywy: „O! W łaśnie nadszedł czas na wydanie następnej wersji. Spotkajmy się i zadecydujmy, co ma się w niej znaleźć” . Później następuje seria spotkań, po których ludzie się rozchodzą, i żadne prace projektowe nie są wykonywane przez następny rok. Po upływie roku, kiedy nadchodzi czas na wydanie kolejnej wersji, nie można naw et zebrać jeszcze raz tych samych ludzi. Ostatecznie powstaje schizofreniczny produkt, w którym każde wydanie wygląda inaczej. Jeśli projekt jest prowadzony w sposób ciągły, zachowuje się prawie tak jak żywy organizm, który podtrzymujemy przy życiu. Dobre pom ysły również nie przychodzą w edług harm onogram u. One po prostu się zdarzają. Jeśli nie stworzymy procesu służącego do przechwytywania dobrych pomysłów, jeśli nie będziemy na bieżąco uwzględniać ich w projekcie, to może dojść do sytuacji, że określona idea zostanie utracona. W naszym zespole przez cały czas dyskutujemy na tem at kolejnej wersji, którą mamy zamiar opublikować, oraz następnej, która będzie wydana później. Myślę, że taki proces bardzo dobrze się sprawdza. Dla języka C# stosowany jest proces standaryzacyjny ECMA, który dla języków wykorzystuje się rzadko. Jaka była motywacja do wdrożenia tego procesu?

Anders: Dla wielu osób standaryzacja jest koniecznym warunkiem zaakceptowania określonego narzędzia. Istnieją pewne instytucje (może w mniejszym stopniu branże), na przykład agencje rządowe, w których standaryzacja jest właściwie wymogiem. Podobnie w edukacji. Z procesu standaryzacji firma Microsoft uzyskuje interesujące korzyści. Zawsze, kiedy tworzymy technologię taką jak .NET, nieuchronnie powstają implementacje tej technologii opracowane dla innych platform. M ożna wówczas wypróbować je losowo, aby sprawdzić te własności, które my zrobiliśmy źle i z którymi mamy złe doświadczenia. To oznacza również złe doświadczenia dla tych klientów, którzy w większości korzystają z naszej implementacji, ale potrzebują tej zewnętrznej implementacji do obsługi starszego sprzętu lub innych celów. Kiedy położymy wszystko na szali, okazuje się, że standaryzacja ma sens — naw et z punktu widzenia biznesowego. Standaryzacja wymusza również zachowanie wielkiej precyzji tego, co tworzymy. Ma to wielkie zalety. Opracowanie standardu języka C# wiązało się z koniecznością napisania bardzo zwięzłej specyfikacji tego języka. Ta bardzo dokładna specyfikacja języka jest inwestycją, która zwróciła się nam już wielokrotnie.

C#

381

Standaryzacja jest dobra ze względu na istnienie lepszych frameworków testowych dla działu kontroli jakości, lepszych narzędzi do im plem entow ania nowych własności języka, ze względu na to, że dokładnie wiadomo, jakie działania powinny wykonywać kompilatory testowe. Z kolei z punktu widzenia możliwości nauczania języka fakt istnienia zwięzłej specyfikacji oznacza, że użytkownicy, zamiast zgadywać, zawsze mogą sięgnąć do niej jako do materiału referencyjnego. Specyfikacja pomaga w zapewnieniu wstecznej zgodności kodu. Tak więc jest wiele korzyści, których istnienie początkowo trudno zauważyć, jednak w gruncie rzeczy one istnieją. Dzięki procesowi standaryzacji poddajemy nasz produkt ocenie bardzo wymagającej społeczności. Otrzymaliśm y wiele uwag od innych firm oraz osób prywatnych związanych z procesem standaryzacji. To sprawiło, że C# stał się lepszym językiem. To bardzo cenne. Nie mam pewności, czy te instytucje i osoby indywidualne zwróciłyby uwagę na nasz produkt, gdyby nie proces standaryzacji. Standaryzacja opóźnia jednak rozwój języka.

Anders: To prawda. Standaryzacja do pewnego stopnia spowalnia proces rozwoju. W pewnym sensie zależy to od sposobu wyrażenia standardu. Niektóre standardy są sformułowane w następujący sposób: „Musisz zaimplementować to i nic innego; rozszerzenia do tego, co tu wyspecyfikowaliśmy, będą naruszeniem standardu” . Nigdy za bardzo w to nie wierzyłem. Standardy powinny ustanowić linię bazową, a także pewien sposób zapewnienia wyrównania do tej linii i jej nieprzekraczania. Ale standardy z całą pewnością powinny pozostawiać swobodę wprowadzania usprawnień. Tylko w taki sposób m ożna bowiem stworzyć wersję v2 standardu — poprzez wdrożenie proponowanych usprawnień. Nie można tego zabraniać. Dla języka C# istnieje standard, ale ten standard nie blokuje możliwości rozwoju. Proces rozwoju odbywa się jednak na zewnątrz procesu standaryzacji, ponieważ nie chcemy, aby źródłem innowacji była społeczność tworząca standard. To nie jest jej celem. Niezależnie od frameworku, w którym operujemy, musi on pozwalać na to, by innowacje były wprowadzane w innym miejscu. Jaka jest pana opinia na temat formalnych aspektów projektowania języka? Niektórzy sugerują, że powinno się wyjść od stworzenia specyfikacji formalnej na papierze, a dopiero później przystąpić do pisania kodu. Inni natomiast całkowicie ignorują specyfikację formalną.

Anders: Właściwa praktyka rzadko oznacza któreś z ekstremalnych podejść. Uważam, że języki zupełnie pozbawione formalnej specyfikacji to zazwyczaj całkowity chaos. Z kolei języki, w których najpierw wszystko jest formalnie wyspecyfikowane, a dopiero później projektuje się kom pilator, także nie są zbyt przyjemne w użytkowaniu. W przypadku języka C# równolegle pracowaliśmy nad kompilatorem i specyfikacją języka. Oba te elementy wywarły na siebie wzajemny wpływ. Często w czasie pisania kom pilatora napotykam y problemy, do których później wracamy w specyfikacji.

382

ROZDZIAŁ

TRZYNASTY

Zdarza się również, że w czasie pisania specyfikacji, kiedy próbujemy rygorystycznie analizować wszystkie możliwości, znajdujem y elementy, które skłaniają nas do pomyślenia w taki sposób: „Być może należałoby zrobić to inaczej w kompilatorze. Istnieją nowe przypadki, o których wcześniej nie pomyśleliśmy” . Myślę, że i jedno, i drugie jest ważne! Osobiście jestem zadowolony ze standaryzacji języka C#, ponieważ zmusiła nas ona do bardzo dokładnego myślenia o tym, czym jest język i w jaki sposób działa. Następnie zmusiła nas do stworzenia specyfikacji formalnej, której — jak pan w spom niał — brakuje w niektórych językach, a to po prostu nie jest właściwe. Kiedy kod źródłowy spełnia rolę specyfikacji, to aby zrozumieć, o co chodzi w konkretnym programie, trzeba sięgnąć do kodu źródłowego kompilatora. Niewiele osób potrafi to robić. Jedyną alternatywą jest zgadywanie bądź napisanie testów i obserwowanie tego, co się zdarzy, z nadzieją, że uda się przechwycić wszystkie warunki brzegowe. Nie uważam, aby to było właściwe podejście. W ja k i sposób debuguje pan kod w języku C#?

Anders: Moim zasadniczym narzędziem debugowania jest instrukcja Console.Writel ine. Z mojego doświadczenia wynika, że w ten sposób postępuje wielu programistów. Dla bardziej złożonych przypadków używam debugera, ponieważ muszę spojrzeć na ślad stosu oraz sprawdzić, co stało się z m oim i zm iennym i w tym lub innym miejscu. Dość często można jednak dojść do sedna sprawy za pomocą kilku prostych testów. Czy stosuje pan jakieś konkretne zasady podczas projektowania interfejsów API?

Anders: Przede wszystkim staram się, aby były jak najprostsze, tylko co to znaczy? Zabrzmiało to dość niedorzecznie, prawda? Jestem gorącym zwolennikiem interfejsów API, które zawierają jak najmniej m etod i jak najmniej klas. Niektórzy uważają, że im więcej elementów w API, tym lepiej. Nie podzielam tej opinii. Myślę, że ważne jest, aby zastanowić się nad tym, jakie działania użytkownicy będą zwykle wykonywali z naszym API. Najlepiej znaleźć pięć typowych scenariuszy użycia, a następnie dążyć do tego, aby API zapewniało jak najłatwiejszą realizację tych scenariuszy. Idealnie by było, aby dany scenariusz użycia był pojedynczym odw ołaniem do API. Nie pow inno być tak, że w celu realizacji typowego scenariusza użycia istnieje konieczność napisania wielu wierszy kodu zawierającego wywołania tego API. Jeśli taka sytuacja występuje, oznacza to, że poziom abstrakcji interfejsu API nie jest odpowiedni. Uważam również, że ważne jest pozostawianie wyjścia. Należy zapewnić bezproblem owe przejście od bardzo prostego w ykorzystania API do bardziej zaawansow anych, o ile będą potrzebne. W wielu interfejsach API występuje rodzaj funkcji realizującej takie przejście. Niestety, często jest tak, że istnieje kilka prostych metod, które można wywołać, ale w momencie, kiedy stajemy przed koniecznością

C#

3 83

zrobienia czegoś bardziej zaawansowanego, spadamy z klifu. Aby wykonać bardziej zaawansowane operacje, musimy uczyć się wielu nowych rzeczy, o których wcześniej nie pomyśleliśmy. Jestem zwolennikiem bardziej stopniowych przejść. A co z dokumentacją?

Anders: Niestety w większości przypadków stan dokum entacji oprogram owania jest bardzo zły. Zawsze mówię programistom — sam także staram się tak postępować, choć nie zawsze mi się to udaje — że połow a wartości tw orzonego API to dobra dokumentacja. Doskonały API będzie bezużyteczny bez dokumentacji wyjaśniającej jego przeznaczenie oraz sposób wykorzystania. To bardzo trudne. Wiele firm stosuje praktykę, zgodnie z którą programiści piszą kod, a specjaliści od dokumentacji piszą dokum entację. Ludzie ci nigdy ze sobą nie rozm awiają. W efekcie powstaje dokumentacja, w której zapisano takie zdania, jak to: „M etoda PrzesuńKontrolkę przesuwa kontrolkę”, lub zawarto rzeczy oczywiste opisane za pomocą bardzo wielu słów. To bardzo źle. Myślę, że programiści powinni pisać znacznie więcej dokumentacji. Czy jest pan zwolennikiem umieszczania komentarzy w kodzie, czy też preferuje pan zewnętrzne dokumenty?

Anders: Zawsze byłem orędow nikiem tw orzenia w kodzie kom entarzy dokum entacyjnych w formacie XML. Jeśli znajdują się one w kodzie, są szanse, że programista pracujący nad kodem zauważy, że to, co zostało napisane w komentarzu, nie jest prawidłowe. Wtedy może zechce poprawić błąd. Jeśli jednak dokumentacja znajduje się gdzieś w innym pliku, to program ista nigdy nie będzie m iał szansy, by na nią popatrzeć, a zatem nigdy jej nie poprawi. Kluczem do sukcesu jest maksymalne zbliżenie programistów z autorami dokumentacji. Mechanizm ten co prawda nie jest doskonały, ale próbujemy go ulepszać. Co według pana należy robić, aby stać się lepszym programistą?

Anders: To trudne. Jest wiele dobrych książek na tem at program ow ania w C#. Zachęcam do sięgnięcia po jedną z dobrych książek. Nie będę tu wymieniał tytułów, ale istnieje wiele dobrych publikacji, które mogą pom óc w stawaniu się lepszym program istą oraz w lepszym rozum ieniu platform y .NET. Sporo przydatnych materiałów można również znaleźć w internecie. W arto zajrzeć do serwisu Codeplex. Istnieje bardzo dużo projektów open source, które można pobrać, obejrzeć i uczyć się z nich. Tym, co mnie pomogło zostać lepszym programistą, było między innymi oglądanie różnych stylów program ow ania oraz różnych rodzajów języków program ow ania. W ciągu ostatnich 5 - 10 lat wiele się nauczyłem, przyglądając się programowaniu funkcyjnem u. To specyficzny sposób program ow ania, który uczy wielu różnych

384

ROZDZIAŁ

TRZYNASTY

umiejętności. Oczywiście to jest programowanie, ale widziane pod innym kątem oraz pozwalające na wyrobienie sobie innego sposobu widzenia problemu. Dla mnie było to niezwykle przydatne.

Przyszłość informatyki Co pan uważa za największe problemy w informatyce?

Anders: Jeśli spojrzymy na to z góry, zauważymy, że w ewolucji języków zawsze dążono do ciągłego podnoszenia poziomu abstrakcji. Wystarczy przypomnieć sobie tablice programowe (ang. plugboard), kod maszynowy, następnie symboliczny język asemblera, dalej C i C++ oraz zarządzane środowiska wykonawcze — widać, że za każdym razem podnosiliśm y poziom abstrakcji. Głównym wyzwaniem jest wyszukanie następnego poziomu abstrakcji. Obecnie w informatyce jest kilka głównych wyzwań. O jednym — współbieżności — mówiliśmy już wcześniej: celem jest stworzenie użytecznych modeli programowania współbieżnego, które będą mogły być zrozumiane przez szerokie rzesze programistów, a nie tylko przez kilku w ybrańców. Dziś żyjemy właśnie w takim świecie. Nawet najwięksi programiści są zaskoczeni przez swój własny kod. Współbieżność to olbrzymie wyzwanie. W branży sporo mówi się dziś o językach dziedzinowych oraz o metaprogramowaniu. M oim zdaniem w tej dziedzinie jest więcej dyskusji niż praktycznego podejścia. Uważam, że nie znamy odpowiedzi na najważniejsze pytania w tej dziedzinie. Mówi się o programowaniu aspektowym i intencjonalnym, ale zagadnienia te nie są jeszcze dobrze usystematyzowane. W zależności od tego, komu zadajemy pytanie, słyszymy odpowiedź: „Nie ma czegoś takiego, jak języki dziedzinowe” albo ,Języki dziedzinowe można znaleźć wszędzie” . Nie możemy naw et dojść do porozum ienia i ustalić, czym jest język dziedzinowy — ale z całą pewnością istnieje coś, co pozwala na wymyślenie bardziej deklaracyjnych sposobów wyrażania rzeczywistości. Pod pewnym i względami wyczerpały się możliwości stosow ania im peratyw nych stylów program ow ania. Nie mamy zamiaru iść dalej z naszymi imperatywnymi językami program owania. To nie jest tak, że m ożna dodać jakieś nowe instrukcje, które spowodują, że nagle staniemy się dziesięciokrotnie bardziej wydajni. Myślę, że o większości współczesnych języków programowania można powiedzieć, że zmuszają do nadmiernego specyfikowania rozwiązania problemu. Trzeba pisać jakieś zagnieżdżone pętle for, instrukcje if czy inne konstrukcje, podczas gdy chcemy jedynie połączyć ze sobą dwa elementy danych. Nie istnieje jednak żadna konstrukcja pozwalająca na wyrażenie takiego połączenia. Nie ma innego wyjścia, jak stworzenie tablic asocjacyjnych, słowników, bla, bla, bla.

C#

385

Pytanie sprowadza się do tego, w jaki sposób m ożna przenieść się na ten bardziej deklaracyjny poziom programowania. Oczywiście im dalej się posuwamy, tym więcej pojęć mamy do wyrażenia, ponieważ język staje się w większym stopniu dziedzinowy. Jest wiele truizm u w śnie o językach dziedzinowych. Pomimo to m am odczucie, że nie znaleźliśmy jeszcze odpow iedniego narzędzia do ich zaim plem entow ania. W związku z tym w dalszym ciągu stworzenie takiego języka jest wyzwaniem. Obecnie możemy zauważyć interesujące odrodzenie się dynam icznych języków programowania. Uważam, że nie ma to związku z dynamicznym charakterem języka, ale bardziej z jego możliwościami metaprogramowania. Jeśli spojrzymy na język Ruby on Rails, okazuje się, że jego największa siła tkwi w możliwości metaprogramowania, a nie w tym, że język ten jest dynamiczny. Często tak bywa, że ewaluacja i m etaprogram ow anie są znacznie łatwiejsze w języku dynam icznym niż w statycznym. Z drugiej strony rezygnacja z mechanizmów uzupełniania instrukcji oraz sprawdzania błędów w fazie kompilacji jest wysoką ceną do zapłacenia. Wiele osób skupionych od jakiegoś czasu wokół języków dynamicznych podkreśla zalety przeglądarki Smalltalka.

Anders: Nie jestem pewien, czy to kupuję. M echanizm ten sprawdza się wtedy, gdy problem jest nieskomplikowany. Kiedy pojawił się Smalltalk, sytuacja wyglądała właśnie w taki sposób — problemy nie były zbyt złożone. Obecnie, jeśli wziąć pod uwagę rozmiar istniejących frameworków, jest nie do pomyślenia, aby programiści znali wszystkie wywołania API dla określonego obiektu lub nawet by chcieli je poznać. Takie narzędzia, jak autouzupełnianie instrukcji, technologia Intellisense, refaktoryzacja sterow ana przez m etadane fazy kompilacji lub statyczna typizacja, są po prostu bezcenne. Ich znaczenie w przyszłości będzie jeszcze większe, ponieważ świat coraz bardziej się komplikuje. Obecnie można zauważyć odrodzenie dynamicznych języków programowania, ale uważam, że przede wszystkim 1) wynika to z zainteresowania m etaprogram ow aniem oraz 2) pod wieloma względami jest reakcją na złożoność środowiska J2EEE. Osobiście znam wielu programistów Javy, którzy przechodzą na stronę języka Ruby tylko dlatego, że toną w frameworkach, bibliotekach Struts, Spring, Hibernate i czym tam jeszcze. Jeśli ktoś nie jest technologicznym guru, nie m a szans na to, by samodzielnie użyć wszystkich tych mechanizmów. Czy programowanie powinno być bardziej dostępne dla osób, które nie są i nie mają aspiracji, by zostać technologicznymi guru?

Anders: Tak sądzę. W szystko zależy od tego, co pan uważa za program ow anie. W pew nym sensie program ow aniem m ożna nazwać korzystanie z arkusza kalkulacyjnego. Gdyby m ożna było stworzyć takie narzędzie, dzięki którem u użytkownicy programowaliby, nawet nie zdając sobie z tego sprawy, byłoby wspaniale.

386

ROZDZIAŁ

TRZYNASTY

Nie m am aspiracji do tego, by uczyć zwykłych użytkowników pisania programów w środowiskach program ow ania używanych przez współczesnych programistów. Programowanie oczywiście tak, ale na znacznie wyższym poziomie. Jakie problemy czekają nas dziś i za pięć lat?

Anders: W spółbieżność jest obecnie największym wyzwaniem. To problem, który mamy przed sobą. Musimy koniecznie znaleźć jego rozwiązanie. Współbieżność jest jednym z największych wyzwań, jakie stoją przede mną i moim zespołem w najbliższej przyszłości. Tak jak zwykle chcemy to zrobić w sposób ewolucyjny. Jak jednak rozwiązać problem współdzielonych stanów i efektów ubocznych bez konieczności naruszenia wstecznej zgodności dla całego istniejącego kodu? Na razie tego nie wiemy. Może być jednak tak, że współbieżność jest wystarczająco dużą zm ianą paradygm atu, aby zaszła konieczność opracowania nowych języków lub kompletnych nowych frameworków. Nie sądzę jednak, abyśmy już teraz byli na to gotowi. Myślę, że sporo możemy zyskać poprzez umożliwienie pisania interfejsów API, które wewnętrznie są współbieżne. Powinni je pisać ludzie dobrze rozumiejący wybraną dziedzinę, niezależnie od tego, czy problem dotyczy transformacji, przetwarzania liczb, sygnałów, czy manipulowania obrazami. Interfejsy API powinny być skonstruowane tak, aby z zew nątrz wyglądały na synchroniczne i by oddzielały współbieżność wewnątrz API. W spółczesne języki program ow ania pow inny spełnić pewne w arunki konieczne, niezbędne do prawidłowego stworzenia tego rodzaju interfejsów API. Jeden z nich już został spełniony. Jest to zdolność przekazywania kodu jako parametrów. Ponieważ możliwości interfejsów API są coraz bardziej złożone, nie wystarczy przekazywać do nich płaskich wartości czy struktur danych. Potrzebna jest możliwość przekazywania fragmentów kodu, które interfejs API zaaranżuje i uruchomi. Potrzebne są funkcje wyższego rzędu oraz abstrakcje, takie ja k mapowanie, składanie i redukowanie.

Anders: Funkcje wyższego rzędu? Tak. Aby można je było zrealizować, potrzebne są takie elementy, jak wyrażenia lam bda, dom knięcia itp. Aby m ożna je było wykorzystywać w środowisku współbieżnym, potrzebna jest także pewność, czy wyrażenie lam bda jest czyste, czy też daje efekty uboczne. Czy m ożna je autom atycznie uruchom ić współbieżnie, czy też z pow odu efektów ubocznych jest to niemożliwe. Skąd można się tego dowiedzieć? Takich mechanizmów nie ma w dzisiejszych językach, ale oczywiście dyskutujemy na temat ich dodania. Oczywiście sztuką jest dodanie ich w taki sposób, który nie wprowadza zbyt dużych ograniczeń i nie narusza wstecznej zgodności z istniejącym kodem. To olbrzymie wyzwanie. Są to problemy, o których nasz zespół myśli codziennie.

C#

3 87

Czy potrzeba stosowania współbieżności zmienia tylko implementację, czy także projekt języka?

Anders: Z całą pewnością zmienia projekt. Wiele osób miało nadzieję, że będzie m ożna wprowadzić w kom pilatorze przełącznik /parallel, który będzie oznaczał „skompiluj kod do współbieżnego urucham iania” . W efekcie kod będzie działał szybciej i automatycznie będzie współbieżny. Nic z tych rzeczy. Podejmowano takie próby i okazały się one nieskuteczne w im peratyw nych stylach program ow ania wykorzystywanych w głównych językach programowania, takich jak C++, C# i Java. Te języki są bardzo trudne do autom atycznego zrównoleglenia, ponieważ ich użytkownicy bardzo m ocno wykorzystują efekty uboczne w swoich programach. Należy wykonać kilka rzeczy. Po pierwsze, należy stworzyć nowoczesne API przystosowane do obsługi współbieżności, które będą na wyższym poziomie niż wątki, blokady, monitory oraz wszystkie inne współczesne elementy obsługi współbieżności. Są również określone warunki, które powinien spełnić język, aby styl programowania współbieżnego stał się łatwiejszy i bezpieczniejszy — na przykład gw arantow ana niezmienność obiektów, czyste funkcje z gwarancją braku efektów ubocznych, analiza izolacji grafów obiektów, tak by było wiadomo, czy określona referencja do grafu obiektu jest współdzielona z kimś innym. Jeśli nie jest, to można bezpiecznie dokonać jej mutacji, a jeżeli jest, to mogą wystąpić efekty uboczne. Potrzebne są mechanizmy tej natury. Dzięki nim kom pilator może przeprowadzić analizę i zapewnić bezpieczeństwo — analogicznie do dzisiejszego bezpieczeństwa typów, bezpieczeństwa pamięci itp. Są to niektóre z elementów, które muszą się pojawić w ciągu kolejnych 5 lub 10 lat, abyśmy mogli sprawniej programować w tych współbieżnych systemach. W gruncie rzeczy polega to na tym, by powiedzieć komputerowi, co ma zrobić.

Anders: Jedna z najpoważniejszych trudności związanych z imperatywnym stylem program ow ania w ynika z nadm iernej specyfikacji problem ów. Z tego pow odu automatyzacja współbieżności jest niezwykle trudna. Czy w przyszłości obsługa współbieżności będzie mogła być zadaniem frameworku?

Anders: Tak sądzę. Istnieje wiele różnych rodzajów współbieżności. Jeśli jednak mówimy o współbieżności na poziomie danych (ang. data-parallel), gdzie wykonuje się operacje na dużych zbiorach danych — na przykład przetwarzanie obrazów, rozpoznaw anie głosu lub obliczenia num eryczne — to sądzę, że jest wysoce prawdopodobne i odpowiednie wykorzystanie modelu, w którym operacje współbieżne można postrzegać jako API. Istnieje API wyższego poziomu, które pozwala powiedzieć: „To są dane, a to operacje, które chciałbym na nich wykonać. Wykonaj je tak szybko, jak szybko uzyskasz dostęp do potrzebnej liczby procesorów” .

388

ROZDZIAŁ

TRZYNASTY

To dość interesujące, poniew aż dzisiaj dość łatw o jest powiedzieć: „O to dane” . Można po prostu przekazać referencję do jakiejś dużej tablicy, jakiegoś obiektu lub czegokolwiek. Specyfikacja tego, jakie operacje są do wykonania, zwykle obejmuje przekazywanie referencji do fragmentów kodu — delegatów lub wyrażeń lambda. Z całą pewnością byłoby dobrze, gdyby kom pilator m ógł przeanalizować i zagw arantow ać, że te wyrażenia lam bda nie m ają efektów ubocznych, oraz wygenerować ostrzeżenie, jeśli je mają. Ale to tylko jeden rodzaj współbieżności. Istnieją także inne typy współbieżności dla bardziej asynchronicznych systemów rozproszonych. Dla tego rodzaju współbieżności także można skorzystać z obsługi w językach programowania. Jeśli przyjrzymy się takiemu językowi jak Erlang, który jest używany w wysoce skalowalnych systemach rozproszonych, okaże się, że ma dla niego zastosowanie zupełnie inny model programowania, który jest znacznie bardziej funkcjonalny. Bazuje na asynchronicznych agentach, przekazywaniu komunikatów itp. Są tam pewne interesujące rzeczy, na podstawie których możemy się dużo nauczyć i które możemy wykorzystać także w naszych językach. Czy paradygmat obiektowy stwarza problemy?

Anders: W szystko zależy od tego, co rozum iem y przez pojęcie „paradygm at obiektowy” . Polimorfizm, hermetyzacja i dziedziczenie same w sobie nie stanowią problem u, choć języki funkcyjne dzięki ich algebraicznym typom danych tworzą inne spojrzenie na polimorfizm. Oprócz tego uważam, że największym problemem z programowaniem obiektowym jest to, że realizuje się je w bardzo imperatywnym stylu. Obiekty herm etyzują zm ienne stany, a program ista wywołuje m etody lub wysyła do obiektów kom unikaty, które pow odują modyfikow anie obiektów, bez inform ow ania innych osób odwołujących się do tych samych obiektów. W rezultacie pow stają zaskakujące efekty uboczne, które są niemożliwe do przeanalizow ania. W tym sensie programowanie obiektowe jest problemem. Można jednak korzystać z program ow ania obiektowego z obiektam i niezm iennym i. W takim przypadku nie będzie takich problemów. Właśnie w taki sposób działają na przykład funkcyjne języki programowania. Wróćmy do pańskiego zainteresowania programowaniem funkcyjnym. Czy studenci inform atyki powinni uczyć się więcej m atem atyki i szerzej eksperymentować z programowaniem funkcyjnym?

Anders: Cóż, uważam za bardzo ważne uwzględnianie programowania funkcyjnego w programach nauczania informatyki. Czy należy od tego rozpocząć — to zupełnie inna sprawa. Nie jestem pewien, czy pierwszy kontakt z programowaniem powinien dotyczyć funkcyjnego języka program ow ania, ale z całą pewnością uważam, że programowanie funkcyjne powinno być uwzględnione w programach nauczania.

C#

389

Czego powinni nauczyć się inni z pańskiego doświadczenia?

Anders: Jeśli przyjrzeć się pierwszemu produktow i, nad którym pracowałem — językowi Turbo Pascal — to widać, że odzwierciedlał on brak wiary w tradycyjny sposób wykonywania działań. Nie trzeba się niczego bać. To, że ktoś mówi, że jakiejś operacji nie da się wykonać, niekoniecznie musi oznaczać, że rzeczywiście nie da się jej wykonać. Oznacza to tylko, że ta osoba nie potrafi tego wykonać. Myślę, że próby znajdow ania now ych rozwiązań dla istniejących problem ów zawsze są ciekawe. Uważam, że prostota zawsze będzie zwycięzcą. Jeśli można znaleźć prostsze rozwiązanie jakiegoś problemu, z całą pewnością pow inno to być celem num er jeden. Zawsze należy dążyć do upraszczania kodu. Uważam, że aby być naprawdę w czymś dobrym, trzeba być pasjonatem. Tego nie m ożna się nauczyć. Myślę, że to po prostu się ma i tyle. Programistą zostałem nie dlatego, że chciałem zarabiać dużo pieniędzy lub że ktoś m nie do tego nam ów ił. Zostałem programistą, ponieważ ta dziedzina bez reszty mnie pochłonęła. Nie można mnie zatrzymać. Ja po prostu muszę pisać programy. Była to jedyna rzecz, którą chciałem robić. Zawsze byłem wielkim pasjonatem programowania. Trzeba mieć tę pasję, aby napraw dę być w czymś dobrym, ponieważ to pozwala zapom nieć o czasie, a czas jest elementem kluczowym. Programowanie wymaga mnóstwa pracy.

390

ROZDZIAŁ

TRZYNASTY

ROZDZIAŁ

CZTERNASTY

UML

W jaki sposób prezentujemy pomysły dotyczące projektu innym osobom? W branży budowlanej wykorzystuje się szczegółowe plany. Zunifikowany język modelowania (ang. Unified M odeling Language — UML) to język graficzny przeznaczony do przedstawiania artefaktó w projektu oprogram ow ania. Kombinacja analizy obiektowej Jamesa Rumbaugha, obiektowego projektu Grady'ego Boocha oraz obiektowej inżynierii oprogramowania lvara Jacobsona umożliwiła programistom i analitykom modelowanie oprogram owania za pomocą specyficznego rodzaju diagramów. Choć dla tego języka powstało wiele standardów, z pewnością każdy z nas spotkał się z niektórymi pojęciami z tego języka w formie prostych rysunków na tablicy.

391

Uczenie się i nauczanie Czytałem, że kiedy zaczynał pan pracować w firmie Ericsson, to nie wiedział pan prawie nic o programowaniu. W ja k i sposób nauczył się pan programować?

Ivar Jacobson: Kiedy zaczynałem pracować w firmie Ericsson, nie wiedziałem niczego o telekom unikacji. To było bardzo cenne doświadczenie. M imo że pracow ałem w dziale zajmującym się produkcją przełączników sprzętowych, mogłem abstrahować od idei budowania dużych systemów. Pracowałem tam prawie cztery lata i nauczyłem się sposobu myślenia o systemach w ogóle. Ta wiedza była bardzo unikatowa, ponieważ ludzie pracujący nad oprogram ow aniem nie mieli doświadczenia w budow aniu dużych systemów. Byłem inżynierem elektrykiem — praw dopodobnie jedyną osobą, która posiadała w ykształcenie akademickie w inżynierii. Większość osób, które tam pracowały, nie miały wyższego wykształcenia. Na uniwersytecie nauczyłem się, jak podchodzić do problemów. Zyskałem również poczucie pewności siebie — przekonanie, że jestem w stanie rozwiązać niemal każdy praktyczny problem. Podobno b rał pan fragm ent kodu asemblerowego do domu, studiował go pan wieczorem, a następnie przygotowywał na jego tem at pytania adresowane do programistów.

Ivar: Prawie nie mieliśmy dokumentacji. W większości tworzyli ją ludzie, którzy nie wiedzieli zbyt dużo o oprogram owaniu. Pisali o w ym aganiach i dokum entowali te wymagania w postaci grafów przepływu, ale były one niespójne i niekompletne. My również mieliśmy diagram y przepływu, które opracowywali i stosowali nasi pracownicy. Nie były one jednak podzielone na kom ponenty, w związku z tym osiągnęły rozmiary olbrzymich diagramów. Dodaliśmy opisy do każdego wiersza kodu asemblerowego, ale pracow nicy naszej firmy uczyli się głównie poprzez wspólną pracę, rozmowy oraz czytanie kodu. Czasami zadawałem swoim podwładnym pytania dotyczące kodu, który analizowałem wieczorem. W większości były to pytania w stylu „Co chciałeś uzyskać w tym miejscu?” . Zanim zrozumiałem określony fragment kodu, często musiałem czytać go trzy do pięciu razy. Byłem jednak uparty, dlatego w ten sposób przeanalizowałem bardzo dużo kodu. W ciągu dnia zajm ow ałem się prow adzeniem zajęć związanych z zarządzaniem projektem. Byłem menedżerem projektu — nie musiałem zatem rozumieć wszystkich szczegółów technicznych, ale musiałem wiedzieć, o co chodzi w projekcie, tak bym przynajmniej potrafił zapytać uczestników projektu, w jakim punkcie się znajdują. Byłem kimś w rodzaju adm inistratora projektu. Nie znosiłem tej roli i gdy tylko odpowiednio dużo się nauczyłem, w większym stopniu zaangażowałem się w projekt. Zaledwie trzy miesiące zajęło mi dojście do wniosku, że to, co robiliśmy, nigdy nie stanie się produktem. W naszym projekcie brało udział 75 osób, a to, czym się wtedy

392

ROZDZIAŁ

CZTERNASTY

zajmowaliśmy, miało dla firmy Ericsson absolutnie kluczowe znaczenie. Czy może pan sobie wyobrazić w tej sytuacji menedżera projektu mówiącego do swojego szefa: „To nigdy nie stanie się produktem ” ? Jak doszło do tego, że wymyślił pan pojęcie przypadku użycia?

Ivar: Było to dość naturalne. W branży telekom unikacyjnej istnieje coś takiego, jak przypadki ruchu (ang. traffic cases). Przypadki ruchu przypominały przypadki użycia, ale miały zastosowanie tylko do połączeń telefonicznych. Nie mieliśmy żadnych przypadków użycia lub przypadków ruchu dla innych własności w centrali, naw et wtedy, gdy te własności reprezentow ały ponad 80% kodu — na przykład dotyczyły obsługi i utrzymania. W przypadku takiego oprogramowania mówiliśmy tylko o własnościach (ang. features). Lista własności była bardzo długa. Trudno było stwierdzić, jakie związki pomiędzy nimi zachodzą. Mieliśmy zatem dwa różne pojęcia: przypadki ruchu i własności. Używano ich w branży telekom unikacyjnej od co najmniej 50 lat, ale nie m ożna było ich łatw o ze sobą połączyć. Bardzo intensywnie myślałem nad znalezieniem jednolitego pojęcia, które m ożna by wykorzystać do opisania wszystkich rodzajów interakcji z systemem. Zacząłem patrzeć na system z zewnątrz, tak jakby to była czarna skrzynka. Próbowałem zidentyfikować wszystkie scenariusze, które wydawały się być przydatne dla użytkow ników . Pojęcie w dosłow nym tłum aczeniu ze szwedzkiego pow inno brzmieć przypadek stosowania, ale moje tłum aczenie nie było zbyt dobre, dlatego został przypadek użycia i jestem z tego zadowolony. Do kw ietnia 1986 roku moje przypadki użycia m usiały nabrać kształtu, dlatego zamodelowałem je w postaci przypominającej klasę. Przypadki użycia można uważać za obiekty, które istnieją tak długo, jak długo zachodzi transakcja pomiędzy użytkownikiem a systemem. Mogą również komunikować się z innymi użytkownikami — tak jak w przypadku połączeń telefonicznych. Jednym z moich najważniejszych celów było zapewnienie możliwości wielokrotnego wykorzystywania przypadków użycia. W związku z tym potrzebowałem bardziej abstrakcyjnego systemu przypadków użycia, porównywalnego do klas abstrakcyjnych w program ow aniu obiektowym. Stworzenie analogii do obiektów i klas pom ogło mi znaleźć jednorodne pojęcie (przypadek użycia), które może być stosowane zarówno do opisywania przypadków ruchu, jak i własności. Utrwalenie się pojęcia zajęło trochę czasu, ale do 1992 roku zidentyfikowałem wszystkie istotne aspekty przypadków użycia. Po tym, jak napisałem książkę o przypadkach użycia: Object-Oriented Software Engineering [Addison-Wesley], nie poznałem nikogo, kto by wniósł coś istotnie nowego do tego zagadnienia. Zdarzało się, że inni lepiej opisywali ten m echanizm — na przykład w książce Kurta Bittnera i lana Spence’a [Use Case Modeling; Addison-Wesley Professional]. Autorzy tej publikacji dziś pracują w mojej firmie. Ich książka lepiej opisuje szczegóły pomysłu przypadków użycia.

UM L

3 93

Odkrycie programowania aspektowego było oczywiście nowością. Podobnie zresztą jak odkrycie, że przypadki użycia są dobrymi aspektami. W rezultacie powstała książka Aspect-Oriented Software Development with Use Cases (we współpracy z Pan Wei Ng, który także pracuje w mojej nowej firmie). Książka ta została opublikowana w 2005 roku [Addison-Wesley Professional]. Co się stało, kiedy zaprezentował pan ten pomysł programistom w firmie Ericsson?

Ivar: Pierwszą reakcją moich kolegów oraz kierownictwa firmy Ericsson na metodologię było stwierdzenie: „To właściwie nie jest nic nowego”. Byłem pewien, że oni po prostu nie dostrzegają nowości. Ja zauważyłem natychm iast, że przypadki użycia są jednocześnie przypadkami testowymi. Jeśli zatem określimy z góry przypadki użycia, mamy do dyspozycji dużo przypadków testowych. W 1986 roku to była prawdziwa nowość. Mogliśmy stosować metodologię projektowania zarządzanego przypadkami użycia. Każdy przypadek użycia opisywał pewien zbiór scenariuszy. Z kolei scenariusz pokazywał sposób implementacji poprzez wykorzystanie współpracujących ze sobą klas lub komponentów. W ja k i sposób można dzielić się doświadczeniami w branży oprogramowania?

Ivar: To bardzo specyficzny problem, którem u poświęciłem ostatnie pięć lat. Trzeba dobrze rozumieć posiadaną wiedzę, tak by m ożna było ją opisać. Ludziom trudno uczyć się nowych pojęć. Nawet gdybyśmy mieli najlepsze procesy na świecie, w dalszym ciągu byłaby konieczność przekazywania tej wiedzy innym. Potrzebujemy systematycznych pom ysłów oraz przekazywania systemu wiedzy. Istnieją lepsze lub gorsze sposoby tego przekazywania. Piętnaście lat tem u stosowaliśmy metodologię Objectory. Później przekształciła się ona w proces RUP (ang. Rational Unified Process). Oczywiście dodano wiele nowej wiedzy, ale zagadnieniem technologii przechw ytywania wiedzy nikt naw et się nie zajął. Było to najlepsze, co mogliśmy wtedy zrobić. Było unikatowe, ponieważ nigdy wcześniej nie realizowano tego na taką skalę. Obecnie promujemy przekazywanie wiedzy bazujące na stosowanych praktykach. Zamiast przekazywać wiedzę na temat wszystkiego, co musimy wiedzieć o wytwarzaniu oprogramowania, przekazujemy po jednej praktyce na raz i tylko wtedy, gdy inni najbardziej jej potrzebują. Praktyki są niewielkie i łatwe do przyswojenia. Wszystko, co mieści się w praktyce, logicznie do siebie pasuje, dlatego z praktyką można łatwo się zapoznać, tymczasem w przypadku procesu jest wiele elementów, o których zawsze trzeba pamiętać. Można powiedzieć, że w przeszłości proces był po prostu zlepkiem pomysłów. Przekształciliśmy go w uporządkowany zbiór praktyk. W ja k i sposób powinniśmy podchodzić do informatyki w edukacji?

Ivar: Naszym problemem jest to, że większość profesorów uniwersyteckich bardzo niewiele wie o praktykach inżynierskich. Niewielu z nich samodzielnie opracowało

394

ROZDZIAŁ

CZTERNASTY

programy wykorzystywane w praktyce. Zdarza się, że któryś z nich opracował jakiś kompilator, ale większość tych kom pilatorów miało charakter czysto akademicki. Być może ktoś stworzył jakieś oprogram owanie szkoleniowe. Nie m ożna od nich oczekiwać dobrego nauczania inżynierii oprogramowania. Na poziomie uniwersyteckim m ożna nauczyć się program ow ania w Javie lub w dowolnym innym języku, ale jeśli chcemy naprawdę zrozumieć oprogramowanie, pow inniśm y posiadać wiedzę z wielu innych dziedzin. Trzeba wymienić tu takie zagadnienia, jak form ułow anie wymagań, tworzenie architektury, testowanie: jednostkowe, integracyjne, systemowe i wydajnościowe. Trzeba też wspom nieć 0 zarządzaniu konfiguracją, kontroli wersji. Potrzebna jest również znajomość różnic pom iędzy budow aniem fram ew orków a budow aniem aplikacji, budow aniem oprogramowania wielokrotnego użytku, architektury zorientowanej na usługi itp. Na uczelniach nie można nauczyć się naprawdę trudnych zagadnień. Czy studenci potrzebują bardziej praktycznych doświadczeń, ja k na przykład uczestnictwa w projektach open source czy też stażu w dużych korporacjach?

Ivar: Szkolenie na uczelniach głównie opiera się na edukacji oraz tworzeniu prostych systemów. Oczywiście nie znam dokładnego obrazu aktualnej sytuacji na świecie, porównajmy ją jednak z innymi dyscyplinami inżynierskimi — na przykład branżą budowlaną. Na kierunkach związanych z budow nictw em oddzielnie uczy się architektury 1 oddzielnie praktycznego budowania. Proces nauczania składa się z kilku różnych dyscyplin. Studenci, którzy mają zostać architektami, w dalszym ciągu muszą uczyć się praktyki budowlanej. W przeciwnym razie nigdy nie zostaną dobrymi architektami. Zawsze można marzyć, jeśli jednak nie da się zrealizować tych marzeń, stają się one nieużyteczne. Na uczelniach nie uczymy studentów rzemiosła inżynierskiego. Tworzenie oprogram ow ania jest w znacznie większym stopniu rzemiosłem niż sztuką. Wiele osób chciałoby to widzieć inaczej, ale jest bardzo mało programistów, którzy mogą poświęcać swój czas na sztukę. Większość z nich to inżynierowie. Nie oznacza to, że nie są oni twórczy. Czy ktoś ośmieli się powiedzieć, że inżynierowie mechanicy budujący różne rodzaje maszyn nie są twórczy? Czy jeśli buduję statki, to nie jestem twórczy? Albo domy? Oczywiście, że jestem. Architekci również są twórczy. Tak więc trzeba zdać sobie sprawę z tego, że w ytwarzanie oprogram ow ania jest inżynierią, a nie sztuką. Inżynierów trzeba uczyć inżynierii. Tymczasem na wielu uniw ersytetach w Ameryce i Europie istnieje długa tradycja, zgodnie z którą profesorowie tych uczelni są wyłącznie akademikami. Podstawowy problem, moim zdaniem , polega na tym, że nie istnieje dobra teoria inżynierii program ow ania. Dla większości osób inżynieria oprogramowania jest po prostu zlepkiem przypadkowych pomysłów. W mojej opinii jest to jeden z najważniejszych problemów do rozwiązania.

UML

395

Proszę wyjaśnić, dlaczego pańskim zdaniem jest to tak istotny problem.

Ivar: Nasze poglądy na sposób tworzenia oprogramowania zmieniają się bardzo często. O wiele częściej, niż zmieniają się trendy w modzie. Olbrzymie firmy na świecie beztrosko porzucają kosztowne procesy i rezygnują z wielkich inwestycji, czasami nawet ich nie wypróbowują. Zamiast uczyć się na podstawie doświadczeń, bezmyślnie rozpoczyna się coś, co jest uznawane za całkowicie nowe. W rzeczywistości niewiele się zmienia. Podobnie jak w świecie mody, jest wielka wrzawa z powodu zmian zupełnie błahych. W przypadku czegoś tak trywialnego jak odzież być może to jest dopuszczalne, ale jeśli wziąć pod uwagę wysokość inwestycji w oprogram ow anie, ten sposób postępowania jest marnotrawstwem. Jest nadzwyczaj kosztowny i absurdalny. Najnowszym trendem jest bycie zwinnym (czego przykładem jest m etodyka Serum). Ruch program ow ania zwinnego przypom ina nam , że podczas tw orzenia oprogram ow ania najważniejsi są ludzie. W zasadzie nie ma w tym niczego nowego — motyw ten pojawia się mniej więcej co dekadę, kiedy naiwni menedżerowie próbują sformalizować to, co jest w zasadzie praktyką w kreatywnym rozwiązywaniu problem ów . W ażne jest to, abyśmy nie zatracili um iejętności pracy w zespole, w spółpracy, dokum entow ania tego, co robimy, oraz planow ania pracy w skali codziennej, cotygodniowej i comiesięcznej. Jednak przy ponownym skupieniu uwagi na tych elementach duża część praktyki zostaje utracona lub zaciem niona przez nadanie nowych nazw starym czynnościom. W ten sposób powstaje iluzja, że odkryto coś całkowicie nowego. Efektem tego wszystkiego jest m nóstw o straconego wysiłku w czasie, kiedy stare prawdy są odkrywane na nowo, ale w zupełnie nowym przebraniu. Młodsi i mniej doświadczeni współpracownicy prom ują nowe trendy, podążają za nowymi guru wspieranymi przez wrzawę w mediach zawsze głodnych nowości. Menedżerowie, którzy utracą kontakt z właściwym procesem wytwarzania oprogramowania, znajdują się w beznadziejnej sytuacji: opierają się nowej modzie i przez to przyklejają do siebie łatkę osób niedouczonych. Realizuje się projekty pilotażowe w celu udow odnienia wydajności nowego podejścia. Zmotywowani programiści są w stanie zrealizować te projekty na niewielką skalę. W rezultacie nowe podejście wypiera stare i wszystko to, co sprawdzało się w starym podejściu, jest odrzucane razem z elementami, które się nie sprawdzały. Dopiero kiedy jest za późno, ktoś zauważa, że w nowym podejściu oprócz działających elementów są takie, które nie działają. U podstaw tego problem u leży głębokie niezrozum ienie natury w ytwarzania oprogram ow ania. N aukow cy próbują rozwiązywać ten problem poprzez opracowywanie nowych teorii — na przykład metod formalnych. Celem stosowania tych metod jest udowodnienie poprawności programów w wyniku stosowania języków formalnych. Języki te jednak nigdy nie zostały przyjęte nigdzie poza środowiskiem akademickim. W branży poświęcono lata na standaryzację opuchniętych metamodeli, których nie sposób zrozumieć.

396

ROZDZIAŁ

CZTERNASTY

Uniwersytety i instytuty techniczne uczą nas określonego sposobu pracy. W każdym projekcie obierana jest specjalna metoda, której muszą się nauczyć wszyscy członkowie projektu, zanim przystąpią do pracy. Za każdym razem, gdy zmieniamy pracę, musimy uczyć się nowego podejścia. Dopiero gdy je poznamy, możemy zająć się prawdziwym zadaniem. Nie jest to wydajne. Kiedy zawsze wszystko zaczynamy od początku, nie możemy uczyć się z własnego doświadczenia. Powinniśmy przestać gonić za chwilową modą i łatwymi rozwiązaniami, które zawsze nas zawodzą. W jaki sposób? Jest to problem, o którym myślałem co najmniej 10 lat. Teraz m am wreszcie pogląd na to, jak należy postępować. Jakie jest pańskie rozwiązanie?

Ivar: Potrzebna jest prosta teoria, która precyzuje to, czym właściwie jest wytwarzanie oprogramowania. Moim zdaniem taką teorię mamy przed nosem. Trzeba tylko umieć z niej skorzystać. Należy zacząć od w ypróbow ania wszystkich m etod, procesów i praktyk i znaleźć prawdę o wytw arzaniu oprogram owania. Na przykład można zrobić to, co zrobiliśmy w mojej firmie i co obecnie jest używane przez setki firm na całym świecie. Po pierwsze, powinniśmy znaleźć zasadnicze elementy, które zawsze są, lub czynności, które zawsze wykonujemy podczas tworzenia oprogramowania. Na przykład zawsze piszemy kod, zawsze go testujemy (chociaż czasami zapominamy o dokumentowaniu sposobu testow ania), zawsze myślimy o wym aganiach (tworzymy przy tym dokumentację lub nie), zawsze mamy zaległości w pracy (jawne bądź niejawne) i zawsze m am y plan na papierze lub w naszych głowach. M ożna zapożyczyć nadużywaną m etaforę i powiedzieć, że musimy znaleźć DNA w ytw arzania oprogram ow ania. Razem z m oim i kolegami zidentyfikowaliśmy pon ad 20 takich elementów, przestudiowawszy około 50 metod, w tym m etody program owania ekstremalnego oraz Serum. Z pozoru może się wydawać, że pomiędzy tymi metodami oraz sposobami ich wykorzystania występują duże różnice. Dla przykładu wymagania można określać za pom ocą własności lub za pom ocą przypadków użycia. Istnieje jednak wspólna podstaw a tych dwóch m etod, którą uwzględniłem w zbiorze m oich zasadniczych elementów. Następnie na te zasadnicze elementy należy nanieść powszechnie używane i sprawdzone metody i praktyki: architekturę, metodologię Serum, komponenty, iteracje itp. Można wyróżnić około 15 takich praktyk. Ponieważ jądro jest agnostyczne w powiązaniu z dow olną konkretną praktyką, m ożemy spróbow ać znaleźć rzeczywistą różnicę pomiędzy różnymi praktykami — nie tylko pozorną, ale dokładną. Zbadanie takiej różnicy zmniejsza pierwiastek religii w stosowaniu każdej z metod. Edukacja stanie się bardziej logiczna, ponieważ koncentruje się na indyw idualnych pomysłach, a nie na konkretnym zlepku pomysłów tworzącym każdą z metod, procesach bądź m etodologii. Myślę, że studenci będą to uwielbiali.

UM L

3 97

Byłoby doskonale, gdyby nasze instytuty techniczne lub uniw ersytety nauczały studentów podstaw inżynierii programowania, a następnie, wykorzystując tę bazę, szkoliły studentów w stosowaniu dobrych praktyk. Jest tu również sporo miejsca na odpowiednie badania. Przypom nijmy sobie słowa Kurta Lewina: „Nie ma nic równie praktycznego, jak dobra teoria”. Dobra teoria ułatwia poznawanie i rozwijanie wiedzy, bez zbytniego uciekania się do religii. Dużo pan podróżuje. Czy zauważył pan różne podejścia do programowania lub projektowania oprogramowania w różnych częściach świata?

Ivar: Oczywiście, ale to, co dzieje się dziś w Stanach Zjednoczonych, zdarzy się również w pozostałej części świata. Być może Stany Zjednoczone są nieco z przodu, jeśli chodzi o wypróbowywanie nowości, ale także usuwają te elementy, które mają. Wiele firm w Stanach Zjednoczonych jest dobrze przygotow anych do rezygnacji z tego, co posiada, w celu poszukiwania nowości, natomiast w Europie, zanim podejmie się taką decyzję, musi być ona dokładnie przemyślana. W schodnia Azja jest z tyłu o kilka lat, jeśli chodzi o nowe technologie, ale z drugiej strony niekoniecznie muszą tam popełniać te same błędy. Pewien bardzo wyraźny trend zauważyłem w Chinach. Chcieli tam naśladować Indie, dlatego pięć lat temu w Chinach bardzo popularne stało się stosowanie metodologii CMMI. Obecnie zauważono jednak, że CMMI dotyczy wyłącznie usprawnienia procesu, co jest tylko częścią problemu. Zanim jednak będzie można usprawnić proces, musi on najpierw istnieć. W związku z tym wyciągnięto wreszcie wniosek, że potrzebne są dobre praktyki, które pozwolą na szybkie w ytwarzanie oprogram ow ania przy niskich nakładach. W jakim stopniu kultura wpływa na sposób projektowania oprogramowania?

Ivar: Nie wiem. Zauważyłem, że Finowie mają w pewien sposób bardziej kowbojską mentalność od reszty Skandynawów. Mocniej stąpają po ziemi i dzięki temu uzyskują wyniki. Specjalne fińskie słowo sisu oznacza „nigdy się nie poddaw aj” . Maksymę tę Finowie traktują poważnie, dlatego nie robią niepotrzebnych rzeczy. Wiele osób pewnie stwierdziłoby, że natura Finów bardzo przypomina podejście Agile, co jest pozytywne. Pozostali Skandynawowie również są bardzo dobrzy w wytwarzaniu oprogramowania. Weźmy na przykład firmę Ericsson. Myślę jednak, że nie powinniśmy przesadnie akcentować tego tematu, ponieważ na poparcie tej tezy nie mam zbyt wielu dowodów.

398

ROZDZIAŁ

CZTERNASTY

Czynnik ludzki Skąd wiadomo, że dana osoba sprawdzi się w roli architekta projektu wytwarzania oprogramowania?

Ivar: Spróbuję wyrazić się jak najjaśniej. Myślę, że architektura jest bardzo ważna, ale ja jestem bardzo ostrożny w nadawaniu określonym osobom etykiety architektów. Postępuję tak z wielu powodów. Wielokrotnie miałem do czynienia z firmami, które wysyłały do innych instytucji zespół architektów i zlecały im pracę nad projektami. Takie działanie mogłoby się sprawdzić, gdyby pracowali oni wewnątrz określonego projektu. Zwykle jednak takie firmy, jak duże banki, mają grupę architektów korporacyjnych, którzy sami tworzą projekt architektury. Po stworzeniu architektury przekazują ją programistom. Programiści wtedy mówią sami do siebie: „Co to jest? Przecież to bezużyteczne” . W wielu firmach architekci korporacyjni siedzą w wieżach z kości słoniowej i nie robią niczego sensownego. Nigdy nie uważałem, że należy traktować architektów systemów jako specjalną klasę ludzi. Oprogram owanie jest bowiem tworzone przez ludzi, a nie przez skostniałe instytucje. Wiele firm próbuje organizować infrastrukturę wytwarzania oprogramowania w postaci szeregu działów, wydziałów lub grup. Jedna grupa zajmuje się wymaganiami, inna architekturą i projektem, jeszcze inna kodowaniem, kolejna testowaniem. Być może są jeszcze inne. Tak zorganizowane firmy zajmują się różnymi projektami, zatem jeden menedżer projektu pracuje z różnym i grupam i osób. Odpowiedzialność za sform ułowanie wymagań leży w rękach lidera grupy form ułow ania wymagań. Za testow anie odpow iada lider grupy testow ania. Nie są to zespoły, ale grupy, dlatego właściwie nie wiadomo, gdzie jest projekt. Menedżer projektu jest po prostu adm inistratorem , a nie menedżerem , który może udzielać wskazówek. Efektem stosow ania takiej praktyki jest bardzo pow olny i kosztowny proces w ytwarzania kiepskiego oprogram ow ania, ponieważ wymagania pisane przez grupę zajmującą się wymaganiami są trudne do zrozumienia dla członków pozostałych grup. Zamiast wydzielać takie grupy, pracujemy w zespołach, w których są kom petentni ludzie do zarządzania wymaganiami, projektowania oprogramowania itp. Zespołem kieruje menedżer. Sam zespół jest wewnętrznie zorganizowany. Przypomina on drużynę piłkarską: są napastnicy, pomocnicy, obrońcy i bramkarz, ale jeśli jest taka potrzeba, zamieniają się rolami pomiędzy sobą. Czasami napastnik pełni rolę obrońcy, a obrońca strzela gola. To jest model, jakiego potrzebujemy w oprogramowaniu. Potrzebny jest nam zespół, który wspólnie walczy oraz w którym ludzie wzajemnie sobie pomagają. Ludzie, którzy piszą wymagania, muszą rozumieć trudności, przed jakimi stają ludzie zajmujący się kodowaniem. Dzięki tem u twórcy wymagań mogą sprawdzić, czy w ym agania dają się przetestow ać i czy nie zostały napisane tylko po to, aby wypełnić dokument.

UML

399

M amy now y m odel w ytw arzania oprogram ow ania: zam iast m odelu organizacji mamy model zespołu. Jak zdefiniowałby pan termin „inżynieria społeczna"?

Ivar: Inżynieria społeczna dotyczy nakłaniania ludzi do wspólnego działania. Dotyczy organizowania zespołów. Ma za zadanie organizowanie czasu w trybie codziennym, cotygodniowym, comiesięcznym itd. Nie jest związana z techniką. Dotyczy sposobów tw orzenia m otywacji członków zespołu oraz zapału do tego, co robią, a także wskazywania sposobów uzyskania wyników. Zawsze było dużo książek na tem at tej dziedziny zarządzania, ale w odniesieniu do oprogram ow ania jest to now y obszar. Ruch Agile, który dotyczy właśnie tego, 0 czym mówiłem, pojawił się w momencie, kiedy w branży powszechnie stosowano takie technologie, jak CMMI i RUP. Nigdy nie wierzyłem w to, że ktoś będzie przestrzegał metodologii RUP, ponieważ metodologię tę należy wykorzystywać w większym stopniu jako bazę dla wiedzy, bazę dla pomysłów. Z kolei pracować należy zgodnie z tym, co m a sens dla ludzi. Zawsze to powtarzałem. Niestety, metodologię RUP zrozumiano jako dokładny przepis działania — analogiczny do przepisu na przygotowanie dania. N ikt z nas, kto kiedykolwiek zajm ow ał się tw orzeniem oprogram ow ania, nigdy nie sądził, że m ożna to robić krok po kroku, ściśle według przepisu. Dlaczego tak powoli usprawniamy metody i proces programowania?

Ivar: To jest prawdziwy problem . M oim zdaniem branża jest bardzo niedojrzała. Jest nieco bardziej dojrzała, niż była 20 lat temu, ale dziś budujemy znacznie bardziej złożone systemy. Dwadzieścia lat tem u zaczynaliśmy od języka program ow ania 1 systemu operacyjnego. Dziś mamy do dyspozycji różnego rodzaju frameworki. Branża oprogramowania jest najbardziej wrażliwa na modę ze wszystkich dziedzin technicznych, jakie znam. Ludzie chcieliby, aby nowy hit pojawiał się co dwa, trzy lata. W przeciwnym wypadku nie widzą żadnego postępu. Sposób realizacji nowych pom ysłów polega nie tylko na odrzuceniu złych lub przestarzałych praktyk, ale w gruncie rzeczy na odrzuceniu wszystkiego i rozpoczęciu wszystkiego od początku. Ponieważ nie posuw am y się naprzód poprzez systematyczną modyfikację tego, co mamy, i dodaw anie now ych elementów, stoim y w miejscu. W związku z tym nie czujemy prawdziwego postępu. Nowe, popularne metodologie, które stosujemy dziś, nie różnią się zbytnio od tych, które były stosowane 20 - 30 lat temu. Różnica polega na odmiennym rozłożeniu akcentów oraz innym sposobie mówienia o tych technikach. Można również zauważyć kontrreakcje na rozbudowane procesy, które odniosły sukces, jak na przykład CMMI i RUP. Kontrreakcja oznacza, że wszystko, co należy do tych lub podobnych obozów,

400

ROZDZIAŁ

CZTERNASTY

jest złe, a teraz potrzebujem y czegoś nowego i świeżego. W rzeczywistości jednak nie jest to ani nowe, ani świeże. Nowe m etodologie nie są rzeczywiście nowe — są jedynie odmianami tego, co już było. M etodologia Agile akurat wniosła coś nowego: wzmocniony akcent na ludzi oraz inżynierię społeczną. Jednak znaczenie nawet tych pojęć jest znane osobom, które pomyślnie tworzyły oprogram ow anie w przeszłości. Jeśli chodzi o wytwarzanie oprogram ow ania, ludzie są najważniejszym zasobem. Posiadanie do dyspozycji kom petentnych i zmotywowanych ludzi to najważniejszy w arunek wstępny tego, by stworzyć dobre oprogram ow anie szybko i niskim nakładem . Czasami o tym zapominamy. Innym aktualnym problemem jest moim zdaniem to, że absolwenci wyższych uczelni znają najnowsze technologie, ale w zasadzie nie wiedzą, jak postępować z oprogramowaniem komercyjnym odziedziczonym z przeszłości. Kiedy przychodzą do pracy: młodzi, świeży i energiczni, nie potrafim y przekonać ich do korzystania z czegoś, co oni uznają za przestarzałe. Jeśli m ają coś takiego robić, po prostu odmawiają pracy, zwłaszcza jeśli jest jej pod dostatkiem. Ci młodzi, niedoświadczeni i dobrze wykształceni ludzie uzyskują przewagę w firmach. W rezultacie nie posuwamy się naprzód. W ja k i sposób należy podchodzić do problemu oprogramowania odziedziczonego?

Ivar: Oprogramowanie tradycyjnie było tworzone przez ludzi, którzy nigdy jawnie nie stosowali żadnej m etodologii. Nie potrafili dokładnie opisać tego, co robią. Nie dokum entowali tego, co robili. W dalszym ciągu trudno zrozumieć strukturę systemu, jeśli zaczyna się go analizować później — jeśli nie rozumie się architektury lub pojęć, które w nim występują. Przejęcie takiego systemu przez nowych ludzi jest niezwykle trudne. Jeżeli z jakiejś firmy jednocześnie odejdą wszyscy ludzie, ta firma upadnie. Nawet jeśli mamy pieniądze na zatrudnienie nowych ludzi, to ci nowi ludzie nie będą wiedzieli, co robić. Z oprogram ow aniem jest tak samo. Na tym polega natura prowadzenia biznesu. Lepiej jest, jeśli funkcjonuje system, który jest łatw y do zrozumienia, jeśli istnieje metodyka szkolenia ludzi w tym systemie, ale nie ma tu żadnej magii. Potrzebne jest oprogram ow anie, które będzie zrozumiałe, będzie m iało dobrą architekturę i dobre modele. Wiemy, że kod bez widocznej architektury jest prawie niemożliwy do zarządzania. W ielkim wyzwaniem dla większości dużych firm jest modyfikowanie systemów odziedziczonych oraz zmiana sposobu ich wytwarzania i/lub rozszerzania. Istnieją charakterystyczne dla tych systemów praktyki, które z czasem się rozwijają. Wiele spośród tych praktyk to nie są techniki Agile czy też techniki zgodne z Agile. Modyfikacja metod wytwarzania dla nowych systemów lub produktów jest znacznie mniejszym wyzwaniem. Stosowane podejście należy zoptymalizować pod kątem

UML

401

systemów odziedziczonych. Mój pogląd m ożna wyrazić następującym zdaniem: rozwój produktu jest procesem zarządzania zmianami — zmianami od określonego stanu do stanu zawierającego coś więcej. Nowy rozwój jest jedynie specjalnym przypadkiem — zmianą niczego w coś. Taki pogląd powinien obejmować wszystko, co robimy, oraz praktyki, jakie stosujemy podczas wytwarzania oprogramowania. Ogólnie rzecz biorąc, istnieją dwa podejścia do zarządzania i usprawniania systemów odziedziczonych. Pierwsze polega na stosowaniu praktyk, które w rzeczywistości nie zmieniają produktu, ale poprawiają sposób działania — na przykład wytwarzania iteracyjnego, ciągłej integracji, wytwarzania zarządzanego testami, wytwarzania zarządzanego przypadkami użycia, historii użytkownika, programowania w parach oraz zespołów przekrojowych (ang. cross-cutting teams). Koszty i zagrożenia wynikające z wprowadzenia takich praktyk są marginalne, choć w dużych firmach w dalszym ciągu znaczące. Drugie podejście ma charakter bardziej podstawowy: modyfikacja produktu za pomocą takich praktyk, jak architektura (na prostym poziomie), architektura korporacji, architektura linii produkcyjnej, kom ponentów itd. Należy przeprowadzić istotną rekonstrukcję. Koszty i ryzyko są większe, ale zwrot inwestycji też jest istotnie większy. Czy stosowanie właściwych metod pozwala uniknąć problemu zarządzania systemem bez widocznej architektury?

Ivar: Nie pozwala go uniknąć, ale pozwala zmniejszyć jego skalę. Dokumentowanie oprogram ow ania może nie przynieść efektu, ponieważ ludzie i tak nie czytają dokum entacji. Pomimo to dobra dokum entacja skupiająca się na zasadniczych sprawach jest przydatna, ponieważ dzięki temu system staje się bardziej przystępny. Jeśli na przykład potrafim y opisać architekturę, oznacza to, że taka architektura rzeczywiście istnieje! Nie można jednak oczekiwać, że kiedy jedna grupa ludzi odejdzie, to druga po prostu wszystko przejmie. Potrzebny jest okres przejściowy, który pozwoli ludziom nauczyć się struktury, z którą będą pracować. Niezależnie od tego, jak bardzo szkolimy ludzi, to jeśli nie istnieje widoczna architektura, nie m a łatwego sposobu przekazywania wiedzy na tem at systemu. Jaki jest najlepszy sposób przekazywania wiedzy?

Ivar: Ogólnie rzecz biorąc, ludzie pracujący z oprogramowaniem nie czytają książek ani podręczników. Jeśli gdziekolwiek czyta się podręczniki, to tylko na wyższych uczelniach. Stwierdzenie, że ludzie korzystają z książek i podręczników podczas pracy, jest po prostu mitem. Napisałem kilka książek i jestem szczęśliwy, że ludzie kupują moje książki, jednak jeśli chodzi o książki w ogóle, to niewiele osób je czyta. Z natury ludzkiej wynika niechęć do czytania książek na tem at procesu wytwarzania oprogram ow ania oraz języków.

402

ROZDZIAŁ

CZTERNASTY

Zamiast uczyć się rozbudow anych metodologii lub języków, takich jak UML lub Java, prościej skoncentrować się na praktykach. Praktyki są łatwiejsze do zarządzania. Można zostać ekspertem w dziedzinie jakiejś praktyki bez potrzeby bycia ekspertem w kompletnej metodologii. W większości moi koledzy, którzy napisali książki dotyczące metodologii, byli ekspertami tylko w niewielkich fragm entach tej m etodologii — praktykach. Zamiast pracować nad rozbudowaną metodologią bądź językiem, lepiej skoncentrować się na jednej praktyce na raz. Nie ma takiego człowieka, który znałby wszystkie dobre, użyteczne praktyki. Praktyki m ożna jednak połączyć i stworzyć określony sposób pracy. Ostatnie pięć lat poświęciłem upraszczaniu i wydzielaniu praktyk w taki sposób, aby można było z nich tworzyć większe procesy (sposoby postępowania). Czytałem również o wykorzystaniu kart.

Ivar: Każda metodologia zaczyna się od pewnych interesujących pomysłów. Inne interesujące pomysły są zapożyczane z zewnątrz. W ten sposób tworzy się zlepek pomysłów, który określa się terminem metodologia, proces, podejście czy innym. Jest doskonale móc robić to w sposób spójny, kompletny i prawidłowy. Niektórym świetnie się to udaje. Niektórzy zostają okrzyknięci mianem guru. Postępowanie w taki sposób jest jednak łatwą częścią problemu. Prawdziwa trudność polega na tym, aby skłonić inne osoby do zaakceptow ania wykorzystywanego podejścia. Innym problem em jest um iejętność zm iany tego, co mamy, kiedy na horyzoncie pojawią się nowe pomysły. Tak więc w większości przypadków nie odnieśliśmy sukcesu w tworzeniu metod. Ludzie w mojej firmie (konkretnie Brian Kerr i Ian Spence) opracowali pewne ważne innowacje wdrażania metod. Jedna z tych innowacji polega na wykorzystaniu kart opisujących zasadnicze elementy, które robimy podczas wytwarzania oprogramowania. Wykorzystanie kart to „zwinny” sposób opisywania praktyk. Na karcie są zasadnicze elementy, resztę można wywnioskować samodzielnie.

UML W ja k i sposób zdefiniowałby pan UML?

Ivar: UML jest językiem projektowania oprogramowania, który można zastosować do specyfikowania, definiowania architektury, projektowania, testowania i używania oprogramowania.

UML

403

W jaki sposób język UML komunikuje się z innymi metodami inżynierii oprogramowania?

Ivar: Wszystkie inne metodologie inżynierii oprogramowania zidentyfikowane przez OMG we wczesnych latach dziewięćdziesiątych (o ile dobrze sobie przypominam, było ich 26) miały własne notacje, ale większość z nich zaadaptowało język UML. Czy pańska grupa złożona z trzech projektantów dawała jakieś korzyści projektowe, czy też jedynie obligowała do przyjmowania kompromisów?

Ivar: Prowadziliśmy ożywione dyskusje, które pom ogły nam w zaprojektow aniu lepszego języka w porównaniu z tym, jaki każdy z nas mógłby stworzyć w pojedynkę. Nie zdołalibyśmy zrobić tego, co zrobiliśmy, bez udziału takich osób, jak David Harel, Jim Odell, Cris Kobryn, M artin Griss, G unnar Overgaard, Steve Cook, Brian Selic czy Guus Ramacker. Co zmieniłby pan w przyszłości? Co może się zmienić w języku UML?

Ivar: Oto najważniejsze problemy, które należałoby rozwiązać: •

Język jest zbyt złożony. Musimy to zmienić. Aż 80% wszystkich aplikacji można zaprojektować przy użyciu mniej niż 20% konstrukcji języka UML. W mojej firmie zdefiniowaliśmy podzbiór języka UML, który nazwaliśmy EUML (ang. Essential Unified Modeling Language). Wykorzystujemy też zupełnie inny sposób opisywania języka UML. Jest on znacznie bardziej atrakcyjny dla zwykłych użytkowników. Tradycyjny język UML został zaprojektowany dla osób zajmujących się tworzeniem metodologii lub producentów narzędzi.



Chciałbym zrestrukturyzować język UML, tak aby stworzyć z niego zbiór języków dziedzinowych (ang. Domain-Specific Languages — DSL). Chciałbym zrobić to w sposób podobny do tego, w jaki zrestrukturyzowaliśmy proces UP (ang. Unified Process) w mojej firmie. Język DSL jest częścią języka m odelow ania (UML jest tego przykładem). Język modelowania tworzymy jako kompozycję wielu takich języków DSL, podobnie jak komponujemy oprogramowanie z wielu wzajemnie od siebie zależnych zagadnień. Chociaż powiedziałem, że język nie został zaprojektowany dla zwykłych użytkowników, ale dla osób tworzących metodologie i narzędzia, to w zasadzie nie został on dobrze zaprojektowany naw et dla tych grup. Konstrukcje semantyczne języka UML są źle zdefiniowane. W języku UML — w szczególności UML 2.0 — zawarto wiele konstrukcji pochodzących z tak wielu różnych obozów metodologii, że czytelne zdefiniowanie konstrukcji semantycznych stało się niemożliwe. Tak jak wiele innych języków, język UML stał się „tłusty i zwiotczały” (takiego określenia użył John Backus dla języka Ada). Skoncentrowano się na konkretnej składni (ikonach) oraz do pewnego stopnia na statycznych konstrukcjach semantycznych, natomiast konstrukcje operacyjne pozostały niezdefiniowane. Spodziewałem się, że będziemy za to krytykowani, ponieważ wtedy standardem w projektowaniu języków było stosowanie takich technik, jak semantyka denotacyjna. Nie robiliśmy tego, pisaliśmy strona po stronie,

404

ROZDZIAŁ

CZTERNASTY

wiedząc, że kod jest bardzo trudny do zrozumienia. Moglibyśmy użyć tych samych praktyk, jakie zastosow ano do zdefiniow ania SDL (standard m odelow ania telekomunikacyjnego z wykorzystaniem VDM został zdefiniowany już w 1984 roku). SDL stał się językiem m odelow ania z dobrze zdefiniow aną semantyką. M imo że do języka UML zaadaptowano główne części języka SDL, nie wykorzystaliśmy praktyk projektowania języków używanych ponad 15 lat wcześniej. Wielka szkoda. Chociaż UML nie został form alnie zdefiniowany, okazał się znacznie lepiej zaprojektow any niż większość popularnych języków m odelow ania obiektowego. Kiedy język UML stał się dostępny, odrzucono niemal wszystkie rywalizujące ze sobą języki. W przypadku gdy język UML jest odpow iednio używany, może on pom óc programistom w osiągnięciu sukcesu. Zwolennicy języka UML nie powinni się obawiać — przed tym językiem jest wielka przyszłość, ale powinien on zyskać lepszą strukturę. Potrzebuje też formalnej definicji. W ja k i sposób znaleźć te elementy, które można usunąć z języka UML? Jaki proces zastosowałby pan w celu uproszczenia języka?

Ivar: Zacząłbym od podstaw języka. Nie zaczynałbym od całego języka i nie usuwał potem indyw idualnych części. Wiem, które konstrukcje języka są rzeczywiście przydatne, a które nie. Istnieją konstrukcje języka, których nawet nie poddawałbym analizie. Nie chcę wchodzić w szczegóły, ale już zidentyfikowaliśmy te 20%, które powinny pozostać, przynajmniej w pewnym przybliżeniu. Kiedy uczymy języka UML, uczymy podzbioru Essential UML, który bazuje na naszym doświadczeniu. Do opisywania elementów języka używamy tych samych koncepcji, jakich używa się do opisywania elementów procesu lub praktyki. Używamy kart, a każda karta reprezentuje konstrukcję języka — na przykład kom ponent, interfejs itp. Mówimy o pedagogice. Nie m ów im y o niczym now ym ani o żadnej nowej konstrukcji języka. Dowiedzieliśmy się, że ludzie w rzeczywistości ani nie czytają, ani nie lubią opasłych specyfikacji języka, w związku z tym potrzebujemy bardziej przystępnego sposobu nauki. Kolejno poznajem y obiekty, interfejsy, klasy, komponenty itp. W ja k i sposób zrestrukturyzuje pan język UML do postaci zbioru języków dziedzinowych ?

Ivar: W języku UML m am y podstaw ow y rdzeń, który daje się zastosować w uniwersalny sposób. Zidentyfikowałbym elementy tego rdzenia i opisałbym język UML, dodając elem ent po elemencie. Elementy UML w odniesieniu do procesów nazywamy praktykami. Elementy języka UML przypominające praktyki będą językami dziedzinowymi. Język dziedzinowy, jak wskazuje jego nazwa, będzie obsługiwał określoną dziedzinę — na przykład określony sektor branżowy (systemy korporacyjne, telekomunikacyjne, ochrony zdrowia itp.) lub dyscyplinę (wymagania, projekt, czas rzeczywisty,

UML

405

testow anie itp.). Na jeden język dziedzinowy składa się dość niewielki podzbiór języka UML. W ten sposób należy tworzyć UML z różnych języków dziedzinowych. Wspomniane języki dziedzinowe powinny mieć wspólny rdzeń i wspólną semantykę. W przeciwnym wypadku mogą wystąpić ogromne trudności w tłumaczeniu pomiędzy elementami należącymi do różnych dziedzin. Czy istnieją praktyki wykorzystywane do projektowania języka SDL, które można zastosować do usprawnienia języka UML?

Ivar: Piętnaście, a może dwadzieścia lat temu, kiedy projektowaliśmy język SDL, użyliśmy m etody VDM (ang. Vienna Development M ethod) opracowanej przez pracow ników firmy IBM w końcu lat sześćdziesiątych lub siedemdziesiątych. Jest to język, za pom ocą którego m ożna m atem atycznie opisać takie pojęcia, jak język, system operacyjny, lub dowolny inny system. Język ten bazuje na dyskretnych pojęciach matematycznych: teorii zbiorów, odwzorowaniach itp. Dzięki temu można m atem atycznie zdefiniować znaczenie każdej konstrukcji języka. Najpierw zidentyfikowaliśmy abstrakcyjną składnię, a potem opisaliśmy tę składnię, używając dyskretnych pojęć m atem atycznych. N astępnie użyliśmy tego opisu do zdefiniow ania dziedzin elementów. Zdefiniowaliśmy statyczne elementy semantyczne poprzez opisanie tego, jakie warunki będą prawdziwe, a jakie fałszywe dla elementów tych dziedzin. W dalszej kolejności scharakteryzowaliśmy operacyjne konstrukcje semantyczne poprzez opisanie znaczenia określonych instrukcji. To był matematyczny sposób opisania języka. Na koniec odwzorowaliśmy notację graficzną na abstrakcyjną składnię. Byłem dość mocno zaangażowany w projekt języka SDL, ale nie mogłem przekonać m oich kolegów z projektu UML do zrobienia czegokolwiek w tym kierunku dla języka UML. W ich odczuciu było to zbyt akademickie. Bazując na doświadczeniach z pracy nad SDL, nie zgadzałem się z taką opinią, ponieważ jeśli ktoś chce tworzyć narzędzia, pow inien znać dokładną semantykę. W przeciwnym razie pozostaje zgadywanie. Kiedy do zespołu dołączyli Steve Cook z firmy IBM oraz Brian Selic z firmy Objectime (później przejętej przez firmę Rational), powiedzieli: „To jest nieprofesjonalne. Nie dołączymy do zespołu, jeśli język nie zostanie zdefiniowany w bardziej formalny sposób”. W związku z tym zaproponowałem kompromis. Powiedziałem: „Zdefiniujmy abstrakcyjną składnię i statyczne konstrukcje semantyczne w sposób matematyczny, ale operacyjne konstrukcje semantyczne opiszmy, używając języka naturalnego” . Język UML 2.0 jest lepszy niż UML 1.0, ale nie wystarcza, jeśli naprawdę chce się zrozumieć wszystkie szczegóły. Co pan sądzi o używaniu języka UML do generowania kodu implementacji?

Ivar: Nie ma zasadniczej potrzeby istnienia dwóch rodzajów języków. Po co ma istnieć język do wyrażania samego projektu, jeśli projekt jest abstrakcją implementacji?

406

ROZDZIAŁ

CZTERNASTY

Po co w takim razie używać innego języka do opisywania implementacji? Taką sytuację mamy dzisiaj i role wspomnianych języków nakładają się. Istnieje kilka przyczyn, dla których istnieją te dwa języki. Być może najważniejsza to fakt, że nie zdołaliśm y przekonać inform atyków , by docenili w artość języka modelowania. W większości uważają oni, że język programowania wystarczy. Realia są takie, że kod jest językiem zaprojektowanym dla maszyn (na przykład kompilatorów) i nie wykorzystuje wszystkich możliwości ludzkiego mózgu. Uważam, że w pewnym momencie uda nam się zademonstrować wartość modelowania wizualnego i przekonać inform atyków do prowadzenia badań w tej dziedzinie. Na temat języka UML prowadzonych jest wiele badań. Nie istnieje zasadniczy powód, aby było konieczne istnienie dw óch języków, ale na razie nie jesteśmy jeszcze na odpowiednim etapie. Czy problem polega tylko na nakłonieniu ludzi, by zwrócili na to uwagę?

Ivar: Problem polega na tym, by przekonać naukowców , że kod nie pozwala na precyzyjne wyrażanie wszystkiego. Wielu z nich już to zrozumiało, choć nie wszyscy. Musimy pokazać więcej sukcesów. Język UML jest w zasadniczy sposób lepszy od wszystkiego, co było dostępne do tej pory. Język SDL był bardzo użyteczny w branży telekomunikacyjnej, ale UML jest bardziej uniwersalnym językiem (uwzględnia istotne konstrukcje języka, niedostępne w języku SDL). Język UML utw orzono w końcu lat dziewięćdziesiątych, a zatem — poniew aż nie istnieje nic, co byłoby w zasadniczy sposób lepsze — należy się spodziewać, że wyparcie języka UML zajmie kolejnych 20 - 30 lat. Jednak do tego czasu możemy usprawnić sposób, w jaki uczymy języka UML. Wierzę, że z upływem czasu potwierdzi się wartość języka UML. Potrzebujemy czegoś podobnego do języka UML, aby wspomóc skalowanie wytwarzania oprogramowania. Być może więcej ludzi z doświadczeniem w wytwarzaniu oprogramowania zaangażuje się w badania. Być może pokażą oni, że sposób, w jaki dziś uczymy studentów języka UML, nie gwarantuje skalowalności. Czy istnieje określony rozmiar projektu oprogramowania, dla którego zastosowanie języka UML dodaje złożoności i pracy, a nie daje żadnych korzyści?

Ivar: Jeśli do kosztów projektu dodam y koszty szkolenia i edukacji oraz narzędzi do wykorzystywania i wspierania języka UML, to może się okazać, że wdrożenie języka UML jest zbyt kosztowne. Jeśli jednak uczestnicy nowego projektu rozumieją notację UML i wiedzą, jak używać co najmniej jednego narzędzia wspierającego użycie UML, sytuacja jest zupełnie inna. Jeżeli ktoś chce uczyć pracowników podstaw inżynierii oprogram owania podczas standardow ych godzin pracy, zm otyw owanie ludzi może być trudne, zwłaszcza

UML

407

gdy realizowany projekt jest mały. W przypadku większych projektów motywacja jest zupełnie inna, ponieważ zagrożenia wynikające z rezygnacji z odpowiedniego modelowania są poważne. Przypuśćmy, że nie jestem przekonany do używania języka UML. Co by m i pana powiedział, aby mnie przekonać, że zastosowanie języka UML pomoże mojemu zespołowi?

Ivar: Odpowiedź na to pytanie zależy od tego, kto pyta. Jeśli pytający nie wie nic na temat oprogramowania, dość łatwo będzie go przekonać, że jest mu potrzebny język graficzny, ponieważ pisanie kodu nie jest dobrym sposobem komunikowania się ludzi ze sobą. Kod jest dobry do interpretowania przez maszyny, ale niezbyt nadaje się do wykorzystania przez ludzi. Jeśli pytanie zadałby doświadczony programista, zapytałbym go, w jaki sposób opisałby swój system — jego kom ponenty oraz sposób interakcji. W jaki sposób opisałby określony scenariusz z punktu widzenia użytkownika? Czy będzie on implementowany poprzez interakcje pomiędzy kom ponentam i lub obiektami? Nie istnieje język program ow ania, który pozwalałby na wykonywanie takich operacji w sensowny sposób. W związku z tym jest to obszar, w którym można wykorzystać UML. Istnieje wiele podobnych przykładów. N iektórych osób nigdy nie zdołałbym przekonać, poniew aż pracowali z kodem od wielu, wielu lat. Gdyby jednak zapytać te osoby, jakby się czuły, gdyby musiały pracować z całkowicie nieznanym językiem, takim jak Prolog, albo z całkiem nową klasą języków, jak na przykład języki deklaracyjne lub funkcyjne (Scheme bądź Lisp), prawdopodobnie uznałyby, że używanie języka graficznego byłoby dla nich pomocne. Nigdy nie miałem specjalnych problemów z przekonaniem do używania UML ludzi, którzy zrozumieli wymagania tworzonego przez siebie systemu.

Wiedza W jakim stopniu wiedza z inżynierii oprogramowania jest związana z konkretnym językiem programowania?

Ivar: W bardzo niewielkim. Uniwersytety uczą języków program ow ania, zatem powszechnie uważa się, że centralnym punktem jest język. Prawdziwy problem polega jednak na zrozumieniu oprogramowania w ogóle. W jaki sposób definiuje się wymagania? Skąd możemy się dowiedzieć, że budujemy właściwy system? Jak sprawdzić, czy sposób użyty do budow ania systemu jest prawidłowy? W jaki sposób realizować zarządzanie konfiguracją i kontrolę wersji? W jaki sposób zastosować 30 bądź 40 praktyk, których nie nauczyliśmy się w szkole?

408

ROZDZIAŁ

CZTERNASTY

W szkołach ludzie uczą się łatwych rzeczy — takie właśnie znajdują się w szkolnych program ach nauczania. Języki program owania są stosunkowo łatwe do nauczania i uczenia się. Kiedy studiow ałem w MIT, w ybrałem kurs 6001, podczas którego używaliśmy języka Scheme — odmiany języka Lisp — do opisania pewnych zjawisk w świecie kom puterów . Studenci, którzy wybierali ten kurs, byli bezpośrednio po szkole średniej. Na zajęciach pisało się kod i był to jeden z najbardziej fantastycznych kursów, jakie kiedykolwiek przeszedłem. Używaliśmy języka do opisania takich zjawisk, jak kompilacja, wykonywanie, interpretacja, oraz wielu interesujących zjawisk w świecie informatyki. Uczono także podstawowych pojęć programowania, dzięki czemu programowanie stało się proste. Dziś mamy do dyspozycji frameworki, ale nauczenie się frameworku jest znacznie trudniejsze. M imo wszystko są to stosunkow o łatwe zagadnienia. Stanowią one jeden z kilku elementów, które trzeba znać, aby zostać dobrym programistą. Musimy podnieść nasz stopień kompetencji w inżynierii oprogramowania. Potrzebny je s t sposób dostarczenia wiedzy wtedy, kiedy będzie potrzebna, a nie, zanim stanie się potrzebna.

Ivar: Zgadza się. I nie wolno wyrzucać tego, co już się ma. Należy wyjść z miejsca, w którym się znajdujemy. Każdy, kto zajmuje się dziś wytwarzaniem oprogramowania, zna pewne praktyki, które nie mają jakiegoś specjalnego znaczenia, ale są przydatne. Nie należy próbować zmieniać wszystkiego od razu, ale usprawniać to, co najbardziej wymaga usprawnienia. Może się zdarzyć, że ktoś jest dobry w programowaniu lub zarządzaniu konfiguracją, ale nie wie, jak dobrze tworzyć wymagania lub testować oprogramowanie. Istnieją sprawdzone praktyki wykonywania tych czynności. M ożna zachować te praktyki, które stosuje się dziś, i zmienić to, co wymaga zmian. Nie trzeba odrzucać wszystkiego tylko po to, by skorzystać z czegoś zupełnie nowego. Na tym polega naturalny proces ewolucji. Przewiduje pan — tak czytałem — z e w przyszłości będą istniały inteligentne agenty, które będą naszymi partnerami w programowaniu parami. Jak pan sobie to wyobraża?

Ivar: W ytwarzanie oprogram ow ania nie jest jakąś wiedzą tajem ną. Przyjrzyjmy się tym 5 - 1 0 milionom ludzi, którzy nazywają siebie programistami. Bardzo niewielu z nich robi coś twórczego lub całkowicie nowego. Świat zew nętrzny sądzi, że program iści są twórczymi i bardzo inteligentnymi osobami. To niestety bardzo mocno mija się z rzeczywistością. Istnieją naukowe dowody na to, że 80% działań, które programista wykonuje w ciągu dnia — drobnych kroków i mikrokroków — nie jest pracą umysłową. Programiści zazwyczaj robią to, co robili wcześniej 50, 100, a nawet 1000 razy. Po prostu stosują wzorzec w nowej sytuacji.

UML

409

Oczywiście istnieją elementy pracy twórczej, ale większość osób tego nie robi. Tylko 20% wysiłku to praca umysłowa. W dalszym ciągu nie jest to wiedza tajem na — czasami po prostu trzeba wymyślić coś, o czym wcześniej nigdy się nie myślało. Tymczasem 80% pracy polega na stosow aniu reguł. W określonym kontekście w ytwarzanie oprogram ow ania w ymaga użycia kolejnych wzorców. Nie zawsze te wzorce są zdefiniowane. Może się zatem zdarzyć, że zastosujemy zły wzorzec i w ten sposób stworzymy błędnie działające oprogramowanie. Ludzie nie zawsze stosują takie same wzorce, zatem część tw orzonych przez nich program ów jest dobra, a inna nie. Istnieje sposób opisywania i stosowania tych reguł za pośrednictw em narzędzi. Na tym właśnie bazuje pomysł inteligentnych agentów. Inteligentne agenty rozumieją kontekst oraz działania, które należy zastosować, i je stosują. Wiele działań mogą wykonywać samodzielnie, ponieważ znają tryw ialne reguły. Mogą też zadawać pytanie programiście pracującemu w parze z agentem. Firma, którą założyłem — Ivar Jacobson International — opracowała inteligentne agenty wspierające wytwarzanie oprogram owania i osiągnęła doskonałe rezultaty. Firmie Tata Consulting Services udało się zredukować koszty o około 20% dzięki zastosowaniu stosunkowo niewielkiego zbioru reguł. Poprawiono jakość i skrócono czas szkolenia dla programistów i deweloperów. Dzięki tem u można było szybko wykorzystać nowych pracowników do wykonywania przydatnych działań. Nie mam żadnych wątpliwości, że ta technika się sprawdza. Problem polega na tym, że w dalszym ciągu mamy zbyt wiele różnych platform oraz wiele różnych narzędzi, które ludzie chcą wykorzystywać. Jeśli ktoś naprawdę chce wytwarzać tego rodzaju oprogramowanie, powinien przystosować je do wielu narzędzi oraz różnych rodzajów platform. W związku z tym tworzenie takich agentów w małej firmie jest trudne. Staje się wykonalne w przypadku osiągnięcia statusu dużej firmy, takiej jak TCS. Wykorzystanie techniki inteligentnych agentów potencjalnie pozwala na redukcję kosztów nawet do 80%. Na przykład istnieją inteligentne agenty do specyfikowania przypadków użycia, projektowania przypadków użycia, testowania przypadków użycia itp. To tylko początek. Nie mam co do tego wątpliwości: w tym kierunku powinna iść technika — tam są pieniądze. Czy ostatecznym celem je st umożliwienie użytkownikom komunikowania się z komputerem, tak by m ógł zapytać, co należy zrobić, czy też zawsze będzie istniała zasadnicza różnica pomiędzy programistami a użytkownikami?

Ivar: Myślę, że coraz więcej prac zamiast programistów będzie wykonywała społeczność użytkowników. Jedną z metod osiągnięcia tego stanu jest zastosowanie programowania regułowego. W przypadku programowania regułowego nikt nie musi rozumieć fazy wykonywania programu, trzeba jedynie zapisać reguły. Ich interpretacją zajmuje się silnik reguł. Nie jest to zasadniczo nic nowego. Społeczność naukowców z dziedziny

410

ROZDZIAŁ

CZTERNASTY

sztucznej inteligencji naucza tego od 40 lat. Technologia obiektowa pom ogła nam lepiej zrozumieć sposoby budowania mechanizmów modelowania. Jeszcze 20 - 30 lat tem u systemy regułowe były m onolityczne i bardzo trudne do m odyfikowania. Dziś, dzięki zastosow aniu agentów, m am y coś w rodzaju obiektowego systemu ekspertowego. Modyfikowanie takiego systemu jest znacznie łatwiejsze. W ja k i sposób rozpoznaje pan prostotę?

Ivar: Prostota to zasadnicze pojęcie będące bazą bycia inteligentnym, robienia czegoś inteligentnego czy w ogóle inteligencji. Einstein pow iedział kiedyś coś takiego: „Powinno być tak prosto, jak się da, ale nie prościej” . W pełni się z tym zgadzam. Właśnie w ten sposób określam inteligencję. Jeśli ktoś jest inteligentny, robi wszystko tak prosto, jak się da, ale nie prościej. Wszystko, co robimy, powinno być robione w inteligentny sposób. Kiedy tworzymy architekturę, powinniśmy modelować jak najmniej, ale tyle, ile potrzeba. Jeżeli nie stworzymy modelu, poświęcimy mnóstwo energii na próby opisywania tego, co robimy, i nie uzyskamy potrzebnego przeglądu sytuacji. Na przykład form ułowanie wymagań z góry, próba zidentyfikowania wszystkich wymagań, zanim zaczniemy cokolwiek budować, nie jest mądre. Mądre podejście polega na zidentyfikowaniu najważniejszych przypadków użycia, najważniejszych własności i rozpoczęciu ich implementacji w taki sposób, by uzyskać pewne informacje zwrotne. Zwykle identyfikuję około 1 0 - 1 5 takich mądrych przypadków. Kiedy pracujemy nad tworzeniem oprogramowania, powinniśmy być mądrzy. Mądrość jest rozszerzeniem zwinności. Techniki Agile to w większości inżynieria społeczna, choć ostatnio dodano do niej więcej elementów. Nie trzeba być mądrym, żeby być zwinnym, ale aby być mądrym, trzeba być zwinnym. Drugi z moich wywiadów będzie dotyczył tego, jak stać się mądrym.

Przygotuj się na zmiany M a pan tytuł licencjata fizyki nadany przez MIT, magistra astronomii z Caltech i doktora informatyki z MIT. Jaki wpływ ma pańskie wykształcenie uniwersyteckie na sposób, w ja k i myśli pan o projektowaniu oprogramowania?

James Rum baugh: Myślę, że moje wykształcenie z wielu dziedzin daje mi nowe spojrzenie oraz efekt synergii niemożliwy do uzyskania w przypadku posiadania standardow ego przygotow ania inform atycznego. W fizyce pojęcie symetrii ma podstaw ow e znaczenie. M ożna powiedzieć, że stanow i sedno współczesnej fizyki. Próbowałem zastosować to pojęcie do modelowania. Na przykład asocjacje gwarantują bardziej symetryczny pogląd na sytuację w porównaniu z tradycyjnym pojęciem wskaźników w ykorzystywanym w większości języków program owania.

UML

411

Podczas m oich studiów inform atycznych w MIT pracow ałem w grupie struktur obliczeniowych profesora Jacka Dennisa — jednej z pierwszych grup zajmujących się podstaw ow ym i m odelam i obliczeniowymi. T am ten zaczyn poglądów wraz z intelektualnym rygorem stworzył bardzo stymulujące środowisko. Jeszcze dziś mają one wpływ na mój sposób myślenia. Jakie zagadnienia studenci powinni zgłębiać bardziej intensywnie?

James: Nie jestem znawcą współczesnych akadem ickich program ów nauczania, ale mam wrażenie, że na wielu uczelniach skupiono się na bardzo wąskiej dziedzinie informatyki. Położono nacisk na języki program ow ania i systemy, zamiast dążyć do zrozumienia podstawowych zasad rządzących techniką obliczeniową. Rzadko na przykład spotykam program istów, którzy rozum ieliby zasady złożoności obliczeniowej i potrafili stosować je w praktyce. Zamiast tego stosują jakieś bezcelowe pseudooptymalizacje, z których wynika więcej złego niż dobrego. Uważam, że najważniejszą umiejętnością w informatyce (a także w fizyce i innych twórczych dziedzinach) jest zdolność abstrakcji. Moje doświadczenie pokazało, że mniej niż połowa programistów potrafi prawidłowo stosować abstrakcję. Jeden z moich kolegów twierdzi, że jest ich mniej niż 10%. Być może ma rację. Niestety, wiele osób zajmujących się oprogramowaniem nie posiada podstawowych umiejętności niezbędnych do właściwego wykonywania swojej pracy. Jaki jest najlepszy sposób przekazywania wiedzy w dziedzinie oprogramowania? Nie mam pewności, czy ktokolwiek czyta tysiącstronicowe podręczniki.

James: Jeśli ktoś potrzebuje tysiąca stron pod ręką, to jest coś niepraw idłow ego w systemie, nad którym pracuje. Nie został on właściwie podzielony. Niestety, wiele osób zajmujących się tą dziedziną kultywuje złożoność. Firma IBM ze złożoności uczyniła religię. To oczywiście pomaga w sprzedaży usług konsultingowych. Inżynierowie podczas procesu swojego kształcenia nabyw ają wielu umiejętności. Najpierw na studiach, a następnie w pracy, gdy zajmują się praktycznymi projektami. Najważniejsze jest poznanie ogólnych zasad. W inżynierii obejm ują one prawa fizyczne oraz zasady inżynierskie z określonej dyscypliny. W technice obliczeniowej oznacza to zasady informatyki — na przykład algorytmy, struktury danych i teorię złożoności — razem z zasadami inżynierii oprogram ow ania. W każdej dziedzinie ważne jest to, aby poczuć, o co w niej chodzi. Jeśli aplikacja przestrzega oczekiwanych norm i jest zaprojektowana w spójny sposób, to zdolny programista będzie w stanie intuicyjnie wyczuć strukturę i działanie nowego systemu, bez przeszukiwania setek stron podręczników. W ażne jest również dostarczanie wskazówek dotyczących tego, jak system działa. Nie wystarczy samo wyszczególnienie części składowych i założenie, że komuś uda się wywnioskować, w jaki sposób będą ze sobą działać, kiedy się je połączy. Jeśli ktoś próbuje nauczyć się posługiw ania skom plikow aną aplikacją, jak na przykład

412

ROZDZIAŁ

CZTERNASTY

Photoshop, najlepszym punktem wyjścia jest skorzystanie z samouczka pokazującego, w jaki sposób skorzystać z prostych poleceń w celu wykonania zadania. Zawsze m ożna skorzystać z obszernej listy poleceń w celu zapoznania się ze szczegółami, ale to jest zły sposób na poznaw anie systemu. Bardzo wielu projektantów systemu uważa, że spełniło swój obowiązek udokumentowania systemu, jeśli przedstawią wyczerpującą listę poleceń i procedur składających się na system. To jednak nie pomaga użytkownikom w zrozumieniu, jak działa system. A zatem największy brak w przekazywaniu wiedzy systemowej to koncentracja na nadmiernie statycznej dekompozycji informacji zamiast na wzorcach użycia. Ruch Pattern Movement lansował słuszny pogląd, by skoncentrować się na użytkowaniu, chociaż czasami zbyt wąsko pojmowano, czym są wzorce. Czym się pan kieruje przy wyborze osoby, która ma pełnić rolę architekta projektu tworzenia oprogramowania?

James: To bardzo trudne. Dobry architekt powinien posiadać zdolność właściwego łączenia teorii z praktyką. Powinien umieć wybierać pomiędzy elegancją a wydajnością oraz pom iędzy doświadczeniem a wizją. Zadaniem architekta jest dbanie o praw idłow ość ogólnej struktury systemu, podejm ow anie decyzji, które mają globalny zasięg. Obejmuje to dekompozycję na moduły, główne struktury danych, mechanizmy komunikacji oraz cele do zoptymalizowania. Architekt, który ma obsesję na punkcie szczegółowego kodowania, może mieć trudności w widzeniu ogólnego obrazu systemu. Architekt musi mieć zdolność skutecznego komunikowania się z zespołem, tak aby projektanci i programiści mogli ze sobą współpracować. Nikt nie potrzebuje architekta, który jest geniuszem, ale nie potrafi w zrozumiały sposób mówić do zwykłych ludzi. Zdolności polityczne są niew ątpliwie plusem. Część zadania architekta polega na nakłonieniu rywalizujących ze sobą frakcji do skutecznej współpracy. Architekt powinien mieć doświadczenie w pracy nad dużymi systemami. Nie można nauczyć się tego na studiach ani z książek. Zanim ktoś otrzyma kierownictwo nad dużym projektem, musi mieć praktyczne doświadczenie. W ja k i sposób można dzielić się doświadczeniami w branży oprogramowania?

James: Zwykłem mówić, że problemem oprogramowania w porów naniu z innymi twórczymi dziedzinami jest to, że nie istnieje muzeum program owania. Jeśli ktoś jest artystą malarzem, studiuje dzieła malarskie znanych artystów wykonywane przez wieki. Może oglądać reprodukcje w książkach lub oryginalne obrazy w muzeach. Jeśli ktoś jest architektem , może zobaczyć różne budowle. Programiści są zdani sami na siebie. Ruch Pattern M ovement dostarczył katalogów użytecznych technik, które można zastosować do wielu różnych sytuacji. To dobry sposób przechwytywania dobrych praktyk od najlepszych programistów, tak by wszyscy mogli z nich skorzystać.

UML

413

Ludzie potrzebują jednak dużych przykładów tego, jak wszystko działa ze sobą w kompletnej aplikacji. Nieco później ruch open source dostarczył przykładów dużych program ów , które może analizować każdy. Jednak nie wszystko w systemach jest rów nie dobre, a początkujący programiści potrzebują kogoś, kto nim i pokieruje. Potrzebujemy odpowiednio skomentowanych studiów przypadków, tak by programiści mogli zrozumieć, co jest dobre, a co złe w systemach. Powinny być tam zilustrowane dobre praktyki, a także wskazane miejsca, które nie zostały wykonane zbyt dobrze. Tak jak w przypadku nabyw ania innych umiejętności, w ażne jest, by zapoznać się z przykładami złych praktyk. Dzięki temu wiadomo, czego należy unikać. W jakim stopniu wiedza z inżynierii oprogramowania jest związana z konkretnym językiem programowania?

James: Niestety zbyt dużo uwagi poświęca się myśleniu o specyficznych językach program ow ania. Większość działań związanych z projektow aniem program u jest niezależna od języka programowania. Oczywiście nie można zignorować języka program ow ania i na strategicznym poziom ie trzeba pam iętać o podstaw ow ych właściwościach języka — na przykład sposobie obsługi pamięci masowej, współbieżności itp. Jednak większa część projektu obejmuje takie zagadnienia, jak struktury danych, złożoność obliczeniowa oraz dekompozycja na oddzielne wątki sterowania. To wszystko jest niezależne od konkretnego języka program owania. Z językami program ow ania jest podobnie jak z językami naturalnym i. N otatka prasowa może być sporządzona równie skutecznie niezależnie od używanego języka. Jeśli zaś piszemy utw ory liryczne, język m a znaczenie od samego początku. Jeżeli ktoś pisze programy tak, jakby pisał wiersze, jest dla siebie nazbyt pobłażliwy. Jeśli natom iast usiądzie z zam iarem napisania właściwych słów, czyli kodu, to nie tłum aczy ich z brudnopisu, ale używa swojej wiedzy na tem at języka, aby wybrać właściwe wyrażenie. Czy zawsze będzie widoczna różnica pomiędzy programistami a użytkownikami, czy też każdy będzie potrafił powiedzieć komputerowi, co ma zrobić?

James: Zauważyłem, że niektóre osoby potrafią jasno wyrażać swoje myśli w języku naturalnym , podczas gdy inne tego nie potrafią. Zatem jeśli naw et potrafilibyśmy mówić do komputera w jego macierzystym języku, komputery mogłyby mieć problemy ze zrozumieniem niektórych osób, ze względu na to, że osoby te nie myślą w czytelny sposób. A zatem zawsze będzie istniała różnica pomiędzy ludźmi, którzy potrafią myśleć i wyrażać się czytelnie, a tymi, którzy tego nie potrafią. Poza tym niektóre sposoby wyrażania myśli okazują się o wiele bardziej spójne wtedy, gdy tem at jest ograniczony. Notacja muzyczna jest niezwykle kompaktowym sposobem przechwytywania muzyki, natomiast notacja szachowa świetnie nadaje się do opisywania gry w szachy. Sporządzenie dokładnych planów budow lanych jest znacznie lepszym sposobem zapewnienia tego, że budynek zostanie zbudowany tak,

41 4

ROZDZIAŁ

CZTERNASTY

jak tego chcemy, niż bezpośrednia rozmowa z budowniczymi w języku naturalnym. A zatem potrzebujemy ludzi, którzy potrafią jasno myśleć oraz dokładnie się wyrażać przy użyciu specjalizowanych języków. Nie uważam, aby istniała możliwość mówienia do komputerów w języku naturalnym w dającej się przewidzieć bliskiej przyszłości. Pamiętajmy, że już COBOL miał być sposobem komunikowania się z komputerami po angielsku. A więc już od dłuższego czasu panuje zbyt wielki optymizm, jeśli chodzi o możliwość porozumiewania się z komputerami w języku naturalnym. Jakie wnioski z lekcji na temat powstania, rozwoju i przystosowania się języka UML do warunków współczesnych mogą wyciągnąć programiści, którzy tworzą systemy komputerowe dziś oraz będą je tworzyć w najbliższej przyszłości?

James: Po pierwsze, trzeba mieć trochę szczęścia, aby osiągnąć sukces. Ja znalazłem się we właściwym miejscu i w odpowiednim czasie. Opracowaliśmy notację OMT jako jeden z pierwszych języków modelowania obiektów i mieliśmy szczęście napisać książkę, która objaśniała zasady modelowania w odpowiednio prosty sposób. Późniejsze metody być może były równie dobre, ale m inął ich czas. Miałem również szczęście pracować w dziale GE Research w czasie, kiedy firma GE nie prowadziła poważnych interesów związanych z oprogramowaniem. Nie mam pojęcia, dlaczego pozwolono nam pracować nad tym tak długo, ale mogliśmy pracować nad naszą notacją bez konieczności reklam ow ania szeregu produktów firmy. Dzięki tem u zyskaliśmy wiarygodność, jakiej nie udało się uzyskać twórcom innych metod. Moje doświadczenia w firmie Rational Software są bardziej mieszane. Stworzenie grupy złożonej z badaczy z trzech wiodących notacji obiektowych pozwoliło nam stworzyć język UML i zapewnić jego szeroką akceptację. Nie oznacza to, że język UML był tak bardzo lepszy od wielu istniejących m etod (choć pozwolił zaokrąglić pewne ostre krawędzie, jakie występowały w poszczególnych metodach). Pozwolił on jednak na stworzenie wspólnej platformy i uniknąć próżnych dyskusji dotyczących różnych kształtów symboli oraz innych drobiazgów. Niestety, firma Rational nie potrafiła podążyć za sukcesem metodologii i szybko stworzyć skutecznych i łatwych do wykorzystania narzędzi. Nie sądzę, aby kierownictwo firmy (czy też większość programistów) było prawdziwymi zwolennikami modelowania — w dalszym ciągu wierzono w styl programowania heroicznego, co można było zauważyć na podstawie obserwacji narzędzi. Dlaczego ktoś miałby kupować narzędzie od ludzi, którzy sami tego narzędzia nie używali? Kiedy zmieniło się nastawienie, było już zbyt późno. Kolejna lekcja brzmi: trzeba wierzyć w to, co się robi; jeśli się nie uwierzy, to nie zadziała. Grupa OMG (ang. Object Management Group) to studium przypadku pokazujące, jak polityczne niesnaski mogą zniszczyć każdy dobry pomysł. Pierwsza wersja języka UML była dość prosta, ponieważ nie było czasu, by dodać zbyt dużo śmieci. Głównym błędem było niespójne spojrzenie — niektóre rzeczy okazały się zbyt

UML

415

wysokopoziomowe, natom iast inne za bardzo związane z konkretnym i językami programowania. Właśnie te problemy należało rozwiązać w drugiej wersji. Niestety, w rozwój drugiej wersji było zaangażowanych zbyt wiele osób, które zazdrościły naszego początkowego sukcesu. Uważali oni, że mogą osiągnąć sukces równie duży jak nasz (jak się okazało, nie zdołali tego zrobić). Proces OMG umożliwiał wstawianie różnych rzeczy do specyfikacji UML 2.0, a ponieważ proces głównie bazuje na zgodzie, jest niem al niemożliwe, aby wyeliminować złe pomysły. A zatem UML 2.0 stał się opasłym m onstrum , w którym znalazło się dużo głupiej zawartości, brakowało spójnego punktu widzenia oraz nie było sposobu, by go zdefiniować. Był jak asygnata finansów publicznych, na której umieszczono m nóstw o różnych tow arów . Pokazuje to ograniczone możliwości twórczego działania komisji. Cały proces ilustruje efekt drugiego systemu Brooksa. Jeśli ktoś nigdy nie czytał książki Freda Brooksa Mythical M an-M onth [Addison-Wesley Professional], pow inien zaopatrzyć się w nią już dziś i natychmiast przeczytać. To absolutnie najlepsza książka, jaką kiedykolwiek napisano na tem at inżynierii oprogram ow ania. Smutne jest to, że większość problemów, które autor zacytował w książce sprzed 30 lat, zdarza się i dziś. Menedżerowie próbują zatrudniać nowych ludzi w celu przyspieszenia opóźnionych projektów, a to sprawia, że te projekty jeszcze bardziej się opóźniają. Być może jest to dobry sposób na sformułowanie głównego problemu, przed jakim staje technika obliczeniowa: większość osób, które się nią zajmują, nie zna historii komputerów, a zatem, jak powiedział Toynbee na tem at historii świata, jest skazana na powtarzanie tych samych błędów. W odróżnieniu od naukowców i inżynierów, którzy budują swoją wiedzę na poprzednich odkryciach, zbyt wiele osób zajmujących się techniką komputerową interpretuje system lub język jako nową rzecz, nie zdając sobie sprawy, że podobne rzeczy były robione wcześniej. Na pierwszej konferencji OOPSLA w 1986 roku duże zainteresowanie wzbudziła prezentacja systemu Sketchpad opracowanego przez Ivana Sutherlanda w 1963 roku. Zastosowano w nim pierwsze idee obiektowe na długo przed wynalezieniem technik obiektowych. Na dodatek wykonano je lepiej niż w większości współczesnych systemów obiektowych. System ten sprawiał wrażenie nowatorskiego w 1986 roku i w dalszym ciągu, ponad 20 lat później, sprawia takie wrażenie. A zatem po co dziś używać wielu graficznych narzędzi, które są gorsze od systemu Sketchpad? Dlaczego ciągle we współczesnych systemach operacyjnych w ystępują błędy przepełnienia bufora — podstawowe źródło luk w zabezpieczeniach wykorzystywanych przez złośliwe oprogramowanie? Dlaczego ciągle używamy takich języków, jak C i C++, w których nie istnieją pojęcia ograniczonych tablic, przez co powstaje możliwość wystąpienia błędów przepełnienia bufora? Oczywiście w języku C++ można zdefiniować ograniczone tablice, ale programiści w dalszym ciągu zbyt często posługują się nagimi wskaźnikami. Wszystko to wynika z ignorancji, lenistwa lub arogancji programistów.

416

ROZDZIAŁ

CZTERNASTY

Przetwarzanie danych jest trudne, w związku z czym niemożliwe staje się uniknięcie pomyłek w skomplikowanych systemach. Nie usprawiedliwia to jednak ciągłego popełniania dziecinnych błędów po tylu latach praktyki. A zatem jakie są najważniejsze pytania dotyczące tw orzenia nowego systemu? Po pierwsze, należy zrozumieć, do czego i komu będzie on służyć. Nie należy od razu zbyt am bitnie podchodzić do zadań — lepiej szybko stworzyć użyteczny system i dodawać do niego nowe własności, niż próbować wymyślać wszystko, co kiedykolwiek będzie potrzebne. To jest dobra zasada w ytw arzania oprogram ow ania zgodnie z metodologią Agile. Nie można oferować wszystkiego dla wszystkich, zatem trzeba się przygotować na podejm owanie trudnych wyborów. Należy jednak zrozumieć, że jeśli system odniesie sukces, to rozwinie się w kierunku trudnym do przewidzenia. Należy zatem przygotować się na nieoczekiwane zmiany.

Korzystanie z UML Co pan sądzi o używaniu języka UML do generowania kodu implementacji?

James: Myślę, że to fatalny pomysł. Wiem, że pod tym względem moje zdanie różni się od zdania wielu innych ekspertów w dziedzinie języka UML, ale trzeba pamiętać, że w języku UML nie ma żadnej magii. Jeśli m ożna generować kod na podstawie modelu, jest to język programowania. UML nie jest dobrze zaprojektowanym językiem programowania. Najważniejszy powód: brakuje w nim dobrze zdefiniowanego p un k tu widzenia. Częściowo jest to celowe, a częściowo wynika z tyranii procesu standaryzacyjnego grupy OMG, która próbuje dostarczyć wszystko wszystkim. W UML nie istnieje dobrze zdefiniowany zbiór założeń dotyczących pamięci operacyjnej i masowej, współbieżności i niemal wszystkich innych pojęć. Jak można programować w takim języku? Faktem jest, że UML oraz inne języki m odelow ania nie mają być wykonywalne. Cechą modeli jest to, że są niedokładne i niejednoznaczne. Ten fakt doprowadzał do szaleństwa wielu teoretyków. W związku z tym próbowali oni sprawić, aby język UML stał się dokładny. Istnieje jednak powód, dla którego modele są niedokładne: pom ijam y rzeczy, które dają niewielki efekt, tak byśmy mogli skoncentrow ać się na tym, co przynosi duży, globalny efekt. Oto jak to działa w modelach fizycznych: modelujem y istotne efekty (na przykład grawitację Słońca), a niewielkie efekty traktujem y jak perturbacje podstawowego m odelu (na przykład wzajemne oddziaływanie planet na siebie). Gdyby ktoś spróbował bezpośrednio rozwiązać cały zbiór wszystkich równań, ze wszystkimi szczegółami, nie osiągnąłby niczego. Myślę, że wiele ostatnio przeprow adzonych prac nad językiem UML poszło w niewłaściwym kierunku. Język UML nigdy nie miał być językiem programowania. Należy go używać do stworzenia odpowiedniej strategii, a końcową wersję programu pisać w odpowiednim języku programowania.

UML

417

Niestety, nie znam żadnego, naprawdę dobrego języka programowania. W każdym języku jest wiele niedociągnięć, które stwarzają ryzyko popełnienia błędów. Całej rodzinie języków C (C, C++, Java itp.) brakuje bardzo wiele (składnia jest prawie niemożliwa do parsowania), ale jesteśmy na nie skazani, niezależnie od tego, czy nam się to podoba, czy nie. Programiści wielu nowych, modnych języków demonstrują ignorancję wszystkich poważnych teorii językowych. Z drugiej strony wiele bardziej akademickich języków jest zbyt eleganckich i lekceważy wiele ważnych własności, jak na przykład potrzebę tego, by wiele zespołów niezależnie od siebie pracowało nad tym samym systemem. Czego potrzebuje język, aby m ógł być wykorzystywany przez wiele zespołów programistów?

James: Pozwoli pan, że powrócę do Algola 60 — jednego z pierwszych języków program ow ania, których używałem (praw dopodobnie zanim urodziło się wielu Czytelników tego wywiadu). W prowadzono w nim wiele istotnych pojęć, takich jak notacja opisu składni BNF, procedury rekurencyjne oraz strukturalne konstrukcje sterujące. Pod wieloma względami był to język znacznie czytelniejszy od FORTRAN-a. Zawierał jednak cztery istotne wady, które powodowały, że był nie do wykorzystania w praktyce: nie zawierał wbudowanych konstrukcji wejścia-wyjścia, nie obsługiwał liczb podwójnej precyzji oraz arytm etyki macierzowej, nie pozw alał na osobną kompilację procedur oraz nie zapew niał standardow ego interfejsu dla języka maszynowego oraz procedur w FORTRAN-ie. Teoretycznie to drobiazgi, ale z punktu widzenia inżynierii oprogramowania są to poważne bariery. Twórcy wielu języków akademickich popełnili ten sam błąd: języki te rozwiązują interesujące problem y m atem atyczne, ale przeoczono w nich praktyczne aspekty tego, w jaki sposób język będzie używany w kontekście. Te problemy nie są bowiem interesujące z punktu widzenia teorii. A właśnie te niewielkie elementy determinują przydatność języka. Przede wszystkim programiści powinni mieć możliwość pracy nad częściami systemu w izolacji, bez konieczności wykorzystywania deklaracji ani kodu pozostałej części systemu. Programistom potrzebny jest także sposób łączenia ze sobą poszczególnych części i zapewnienie ich działania jako systemu. Uważam, że to wymaga pewnego mechanizmu deklarowanych typów. Należy wykorzystać różne rodzaje mechanizmów komunikacji, ponieważ współczesne systemy są w wysokim stopniu współbieżne. W większości języków nie istnieje dobry sposób opisywania lub deklarow ania dynamicznych zachowań. Myślę, że potrzebne są narzędzia debugowania, które będą lepiej zintegrowane z językiem, ale takie, które można włączyć lub wyłączyć w fazie w ykonyw ania program u. Obecnie kodow anie jest zbyt m ocno oddzielone od testowania.

418

ROZDZIAŁ

CZTERNASTY

Czy UML jest jedynie narzędziem do koordynacji pracy w dużym zespole programistów?

James: Przede wszystkim jest to narzędzie służące jako przew odnik i pom oc w organizacji myślenia indywidualnych programistów. Trzeba pracować na różnych poziomach abstrakcji. Kod jest takim poziomem, ale nie jest to najbardziej przydatny poziom pozwalający na zrozumienie, w jaki sposób działa system. Istnieje potrzeba pracy na wyższym poziomie. Oznacza to odejście od wszystkich szczegółów kodu i zajęcie się elementami, które są ważne na wyższym poziomie. Dlatego właśnie język UML nie powinien być wykonywalny. To zniszczyłoby cały sens abstrakcji. Skoro jesteśmy przy architektach: podkreślił pan rolę dobrej komunikacji. Czy UML pomaga w rozwiązaniu tego problemu?

James: Dostarcza wspólnego zbioru pojęć i notacji. To pom aga w komunikacji. Nie ma możliwości komunikacji, jeśli nie istnieje wspólne słownictwo. W przeciwnym razie będziemy myśleli, że się komunikujemy, podczas gdy różni ludzie będą mieli na myśli różne rzeczy. Taka sytuacja jest naw et gorsza od całkowitego braku komunikacji. Czy architekt systemu może się lepiej komunikować z członkami zespołu za pomocą UML?

James: Tak. Na tym polega cały sens języka UML. Kiedy zaczynałem lansować podejście obiektowe w firmie GE, odwiedziłem oddział GE Aircraft Engines. Mieliśmy olbrzymie problemy w przekonaniu programistów do tego, że program ow anie obiektowe to dobry pomysł. Programiści trwali przy istniejących pojęciach (jak na przykład programowanie w FORTRAN-ie) i nie rozumieli, o czym mówiliśmy. Za to kilku inżynierów lotnictw a dokładnie zrozum iało, o co chodzi. Idea program ow ania obiektowego bardzo im się spodobała. Byli przyzwyczajeni do tego, że w swojej pracy wykonywali modele i abstrahowali od w ysokopoziom ow ych pojęć, takich jak „krzywa wydajności silnika” czy też „prędkość przeciążenia a kąt natarcia” . Byli przyzwyczajeni do tworzenia obiektów m entalnych, które m iały reprezentować fizyczne pojęcia. Programiści nie mogli zobaczyć lasu z powodu drzew — skupiali się na kodzie, nie rozumiejąc, że celem kodu jest reprezentowanie pojęć wyższego poziomu. Wielu programistów w dalszym ciągu nie zdaje sobie z tego sprawy. Robin Milner, twórca języka ML, zilustrował ideę hierarchii modeli, łącząc ze sobą wszystkie elementy — począwszy od wysokopoziomowych języków projektowania, ja k UML, poprzez niskopoziomowy kod asemblera, model fizycznego sprzętu, a skończywszy na dodatkowych modelach wchodzących w skład środowiska, w którym był używany sprzęt. Podał przykład samolotu — tu występuje kod wysokopoziomowy, który schodzi do sprzętu, sprzęt wykorzystujący własne modele, a następnie cały

UML

419

sprzęt samolotu zaprojektowany zgodnie z modelami aerodynamicznymi, modelami fizycznymi oraz modelami pogody. Kiedy pilot naciska przycisk, używane są wszystkie te modele. Czy należałoby zmniejszyć liczbę poziomów (w kierunku jednego uniwersalnego modelu/języka), czy też zwiększyć abstrakcję (a tym samym liczbę poziomów)?

James: Doskonałe pytanie. Jednym z ważniejszych pojęć z fizyki (lub raczej ogólnie z nauki) jest idea wielu poziom ów wyłaniających się widoków. Każdy widok jest zbudow any na bazie w idoku znajdującego się pod spodem, ale kiedy zostanie stworzony, staje się wewnętrznie spójny i ma jednoznaczny sens. A zatem mam y poziom y fizyki kw antow ej, chemii, m ikrobiologii, organizm ów biologicznych, populacji, ekosystemów i środowisk. Inną hierarchiczną wieżę tworzy technika obliczeniowa: fizyka m ateriałów , półprzew odniki, obwody, systemy cyfrowe, komputery, oprogramowanie firmware, systemy operacyjne, frameworki aplikacji, aplikacje i sieci. Żaden z poziom ów nie jest właściwy, prawdziwy czy zasadniczy. Każdy ma znaczenie w swoich w łasnych kategoriach i m ożna go zdefiniować w kontekście poziomu znajdującego się bezpośrednio poniżej. Nie oznacza to jednak, że może on być rozumiany na niższym poziomie. Znaczenie każdego poziomu jest unikatowe i może być rozumiane tylko na tym poziomie. To jest system wyłaniający się: znaczenie każdego poziomu wyłania się z prostej niższej warstwy, ale musi być rozumiane w swoich własnych kategoriach. A zatem żeby zrozumieć dowolny złożony system (oczywiście ostatecznym przykładem jest wszechświat), trzeba pracować na wielu poziomach jednocześnie i żaden z nich nie może być uznany za podstawowy. Wieża wyłaniających się poziomów nie została dobrze zobrazowana za pomocą języków m odelow ania. Potrzebny jest sposób m odelow ania systemu na wielu poziom ach jednocześnie. Nie mówię o czteropoziom ow ym m etam odelu OMG. Powstał on, ponieważ pewne osoby popełniły ten sam błąd co Bertrand Russel i założyły, że nie można modelować czegoś w kategoriach samego siebie. Oczywiście jest to możliwe — m ożna o tym przeczytać w książce Douglasa H ofstadtera I A m a Strange Loop [Basic Books]. Błąd popełniają również wielbiciele kodu, którzy lekceważą modelowanie. Myślą, że kod jest jedyną rzeczą, która ma znaczenie. To tak jakby powiedzieć, że znaczenie m ają tylko obwody albo tylko fizyczne półprzewodniki. W ażne są wszystkie poziom y, a my musimy pracować na właściwym poziomie we właściwym celu. Twierdzę, że poziom kodu nie nadaje się do tego, by objaśnić w użyteczny dla ludzi sposób działanie dużego, złożonego systemu. Nie istnieje pojedynczy, uniwersalny język. Potrzebny jest framework, który pozwala na pracę na wielu poziomach abstrakcji. Takie zadanie powinien spełnić język UML 2.0, ale go nie spełnił. Nie oznacza to, że dodane szczegóły były złe, ale że skomplikowały one m odelow anie — zły jest współczynnik kosztów do uzyskanych korzyści. Najważniejszą rzeczą, której brakuje, jest czytelny sposób budow ania warstw w yłaniających się modeli, tak by poszczególne poziomy były od siebie oddzielone.

420

ROZDZIAŁ

CZTERNASTY

Na przykład język UML zawiera dość niskopoziomowe pojęcia, które są właściwsze na poziomie języków programowania. Przykład stanowią uprawnienia lub wskaźniki. Posiada też pojęcia wysokopoziomowe. Nie istnieje jednak dobry sposób oddzielenia pojęć niskopoziomowych od wysokopoziomowych. Profile były taką próbą, ale niezbyt dobrze sprawdziły się w tej roli. Podczas konstruow ania UML wiele problem ów wynikało z napięcia pomiędzy pojęciami języka programowania a pojęciami logicznymi wyższego poziomu. Innym ważnym problem em jest różnica w tonie pom iędzy różnym i fragm entam i tw orzonym i przez różne osoby. Na przykład opracow anie wykresu sekwencji kom unikatów okazało się bardzo przydatne dla języka UML, ale było to oprogramowanie w zupełnie innym stylu w porównaniu z diagramem aktywności. Jaki proces zastosowałby pan w celu uproszczenia języka?

James: Wątpię, że można go uprościć za pośrednictwem procesu standaryzacyjnego, na przykład procesu OMG, w wyniku którego powstał język UML 2.0. Istnieje zbyt wiele rywalizujących ze sobą koncepcji i wszyscy chcą przy tym zrealizować własne pomysły. Problem polega na tym, że procesy standaryzacyjne kładą zbyt mały nacisk na spójność, prostotę i jednolity styl. Za to zbyt dużo uwagi poświęca się rozbudowanej zawartości. W rzeczywistości nie jestem zbyt gorącym zw olennikiem struktur standaryzacyjnych. Zwykle w efekcie ich działania powstają nadmiernie rozbudowane produkty, którym brakuje elegancji i łatwości użytkowania. Byłem niechętny, aby się w nie zaangażować. Moje obawy dotyczące ich negatywnych efektów okazały się uzasadnione. Najlepszym podejściem byłoby stworzenie przez kilka osób ich własnych dopracow anych wersji UML i pozwolenie, aby to opinia publiczna — na podstawie wniosków wynikających z używania — decydowała, która wersja jest lepsza. Język, który w rezultacie by powstał, niekoniecznie musiałby nazywać się „UML”, ponieważ ta nazwa jest związana z określonym bagażem prawnym i emocjonalnym. Ważne jest, aby wszyscy projektanci języków jasno określili cel swoich wersji. To rozwiązanie znacznie lepsze od próby tworzenia języka, który ma być wszystkim dla wszystkich. W ja k i sposób rozpoznaje pan prostotę?

James: Prostota wymaga, aby bardziej chcieć robić niewiele niż za dużo. Trzeba pamiętać, że język m odelow ania nie jest językiem program ow ania. Jeśli pewnych możliwości brakuje językowi, m odelujący zawsze może wymyślić coś, co zapełni lukę. Jeśli w celu zapam iętania wszystkich własności języka musimy nosić za sobą wielką planszę, to znaczy, że język ten nie jest prosty. Jeśli ciągle mamy do dyspozycji cztery (lub pięć) alternatywne sposoby zamodelowania oczywistej sytuacji, to język nie jest prosty.

UML

421

Przypuśćmy, że mam sceptyczne nastawienie do języka UML. W ja k i sposób przekonałby mnie pan, że ten język mi się przyda?

James: Sam mam sceptyczne nastawienie do wielu fragmentów UML. Myślę, że został zbyt mocno rozbudow any przez zbyt wielu kucharzy w kuchni OMG. Próbowano go również promować jako rozwiązanie wszystkich problemów dla wszystkich ludzi. Cała branża kom puterowa ma tendencję do nieuzasadnionej wrzawy nad każdym now ym produktem . Istnieje również tendencja do wyszukiwania pojedynczego rozw iązania dla wszystkich problem ów . Życie i technika obliczeniowa są zbyt skomplikowane, aby były możliwe proste rozwiązania. UML jest narzędziem bardzo przydatnym do projektowania struktury danych, średnio przydatnym do dekompozycji systemów na warstwowe moduły i niezbyt przydatnym dla elementów dynamicznych, do których obsługi nie za bardzo się nadaje. Okazuje się przydatny, ale nie rozwiąże wszystkich problemów. Dodatkowo potrzebujemy wielu innych umiejętności i narzędzi. Czy istnieje określony rozmiar projektu oprogramowania, dla którego zastosowanie języka UML dodaje złożoności i pracy, a nie przynosi żadnych korzyści?

James: Nie, ale to nie oznacza, że języka UML należy używać w taki sam sposób zarówno w bardzo małych, jak i w bardzo dużych projektach. W m ałych będzie znacznie mniej ceregieli przy używaniu narzędzi, modeli, procesów oprogramowania itp. W niewielkim projekcie można wykorzystać diagramy klas i struktury danych, natomiast w projekcie typu round-trip już niekoniecznie. A zatem UML dostarcza sposobu szkicowania wstępnego projektu, ale ostatecznie trzeba posłużyć się językiem programowania. W dużych projektach połow ę lub naw et więcej wysiłku włożonego w proces projektow ania stanowi kom unikacja, a nie samo uchwycenie projektu. W takim przypadku istotne znaczenie ma posiadanie narzędzi i procesów do dekomponowania systemu, zarządzania dostępem do modeli i kodu oraz śledzenia postępu. W przeciwnym razie ludzie przez cały czas będą deptali sobie po palcach. Wiem, że wielu programistów rozpacza na samą myśl o poddaniu się tego rodzaju dyscyplinie. W sporcie, budownictwie, branży prasowej, procesie projektowania silników rakietowych oraz niemal wszystkich innych kooperacyjnych przedsięwzięciach takie płaczki zostałyby wyeliminowane z zespołu bez większego żalu. Nadszedł czas, byśmy zaczęli stosować takie podejście w branży oprogramowania. Jeśli chcemy, by branża ta była brana serio, nie mamy innego wyjścia.

422

ROZDZIAŁ

CZTERNASTY

Warstwy i języki W jednej ze swoich pierwszych odpowiedzi powiedział pan, że ruch Pattern Movement zrobił wiele dobrego, ale wzorce były postrzegane zbyt wąsko. Czy może pan rozwinąć tę myśl?

James: Kiedyś uczestniczyłem w w arsztatach, w których brali udział członkowie Hillside Group. To właśnie oni prezentowali bardzo wąskie, niemal religijne spojrzenie na wzorce projektowe i za nic nie chcieli zweryfikować swoich poglądów. Bardzo bronili swojego oficjalnego spojrzenia na wzorce oraz wyznawali kult architekta Alexandra. Postrzegali wzorce jako rzecz bardzo specyficzną, a ja uważam, że wzorce można stosować na wielu różnych poziomach. Nie każdy podziela ten specyficzny pogląd dotyczący niewielkich lub średniej wielkości wzorców opisanych w książce Gangu Czterech1 Wzorce projektowe oraz artykułach opublikowanych przez Hillside Group. W rzeczywistości naw et wewnątrz grupy twórców wzorców projektowych były pewne podziały. Uważam jednak, że pomysł był trafiony, a to jest najważniejsze. Twórcy wzorców projektowych mówią to samo, co mówią ludzie w wielu innych dyscyplinach: należy korzystać z doświadczenia utalentow anych ludzi. Zapisywać ich wiedzę w katalogach, tak by zwykli odbiorcy mogli z niej korzystać. Takie praktyki stosuje się w inżynierii, malarstwie i architekturze budynków — niemal w każdej twórczej dziedzinie. Branża kom puterowa bardzo powoli adaptowała ideę uczenia się z przeszłości. W dalszym ciągu proces ten przebiega niezbyt szybko. To była jedna z moich uwag do tej branży. W informatyce jest wielu ludzi, którzy myślą bardzo dobrze o samych sobie i zdają się zapominać, że za nimi jest jakaś przeszłość. Wielu ludzi na nowo wynajduje rzeczy, które dawno zostały odkryte. Myślę, że idea wzorców rozwiązuje ten problem poprzez stwierdzenie: „Popatrzcie, są wzorce, które możemy uchwycić i zrozumieć oraz udostępnić szerszemu gronu osób w branży, takich, które same nie potrafią ich stworzyć. Jeśli właściwie opiszemy te wzorce, inni będą mogli z nich skorzystać” . To stwierdzenie jest prawdziwe niezależnie od dziedziny. Liczba ludzi, którzy dokonują przełom ow ych odkryć, w każdej dziedzinie jest bardzo mała. Po początkow ym przełom ie inni przechwytują pom ysł i go rozwijają. Ktoś powiedział, że po tym, kiedy ktoś przedstawi swój pomysł, traci nad nim kontrolę. Nie da się kontrolować tego, co oznacza pomysł. Nowi ludzie znajdą dodatkowe znaczenia, których twórca pomysłu nie dostrzegał.

1 Określenie to dotyczy Ericha Gamma, Richarda Heima, Ralpha Johnsona i Johna Vlissidesa — autorów książki Wzorce projektowe. Elementy oprogramowania obiektowego wielokrotnego użytku (WNT, 2005).

UML

423

Chciałbym nawiązać do pewnych rzeczy, które powiedział pan wcześniej w związku z pańskim wykształceniem z fizyki. W fizyce występuje wiele warstw. To, co ma zastosowanie i jes t odpowiednie w jed n e j warstwie, niekoniecznie jes t odpowiednie w innej. Nie można jednak zaprzeczyć istnieniu innych warstw.

James: Na tym właśnie polega cała koncepcja wyłaniających się systemów. To jest właściwa istota nauki oraz istota języka. Jest to jedno z pojęć z teorii złożoności. Istnieje zjawisko w yłaniania się nowych warstw i nie istnieje jedna, podstaw ow a warstwa. W informatyce zrozum iano to już dawno. Istnieje wiele w arstw i żadna z nich nie jest rzeczywistą warstwą. Mówi pan, że twórcy wzorców celowo stosują wąskie spojrzenie na wzorce. Czy ma pan na myśli to, że uwzględniają jedną warstwę wtedy, gdy powinni patrzeć na cały stos?

James: Mówiąc szczerze, w ruchu Pattern M ovem ent są ludzie, którzy zajmują się różnym i warstwami. Na przykład istnieje kilka książek dotyczących wzorców architektury. Być może oryginalna grupa Hillside Group m iała konkretny pogląd na to, czym jest wzorzec, i powiązała go z całą koncepcją języka wzorców Alexandra. Myślę, że należy stosować to pojęcie szerzej. Jedną z często pojawiających się uwag krytycznych dotyczących książki „Wzorce projektowe" jest to, że wzorce te nie są zbyt skuteczne w innych językach niż C+ + lub Java.

James: Jednym z zasadniczych cech wzorców jest to, że powinny dotyczyć konkretnej sytuacji, w której się znajdujemy. Nie do wszystkiego można zastosować bardzo ogólne podejście. Jeśli piszemy wzorce dla języka program ow ania, to wiele z nich jest specyficznych dla określonego języka programowania. Niektóre zaś mogą być ogólne i działają dla szerokiego zakresu języków program owania. Reguła ta sprawdza się również w inżynierii: niektóre zasady sprawdzają się dla stali, ale nie dla drewna i odwrotnie, a inne są wspólne. Być może na tym właśnie polegała moja uwaga dotycząca modelowania. Język UML nie rozróżniał dobrze różnych zastosowań. Istnieją zastosowania modelowania, które dotyczą specyficznych języków program ow ania. Istnieją również zastosow ania modelowania dotyczące w większym stopniu warstwy logiki. UML uwzględnił obie grupy i upakow ał je wszystkie w jednym worku. Osobiście przyjmuję część odpowiedzialności za tę sytuację. W pierwszej wersji języka połączyliśmy ze sobą wiele różnych rzeczy. To się zdarza w pierwszych wersjach — nie ma się doświadczenia pozwalającego na odpowiednie podzielenie zadań. M iałem nadzieję, że w drugiej wersji uda nam się wszystko uporządkow ać tak, by można było powiedzieć: „Te własności mają zastosowanie do języka C lub C++”, ponieważ mają cechy, które pasują do języka C, C++ i tym podobnych języków. Dobrze, jeśli te własności tam są, ale nie są one tak ogólne jak inne, które mają zastosowanie dla wielu różnych języków. Dobrze byłoby wiedzieć, które są które.

424

ROZDZIAŁ

CZTERNASTY

Są własności m odelow ania mające zastosowanie na wyższych poziom ach oraz na niskich poziomach. Mechanizm profilowania stworzono po to, aby można było definiować własności z konkretnego pun ktu widzenia. Niestety, m echanizm profilow ania działa niezgrabnie. Nie pozwala na właściwe definiow anie warstw. Mówi: „O to dziedzina”, ale w pew nym sensie jest to jednopoziom ow a definicja. Nie m a sposobu na czytelne zorganizowanie ich na poziom y m odelowania. Przypuśćmy, że udało się pańskiemu zespołowi stworzyć UML 3 .0 bez zachowania zgodności wstecz. W ja k i sposób to zrobić, aby nie zdenerwować wszystkich?

James: Na samym wstępie już pan wyraził przekonanie, że wszyscy będą zdenerwowani. Oczywiście jest to problem, przed którym zawsze stają takie firmy, jak Microsoft i Apple. Czy zachowamy zgodność wstecz, tak jak praktykuje firma Microsoft, czy też czasami ją naruszymy, tak jak zrobiła firma Apple? Oba podejścia mają swoje wady i zalety. Nie można zachowywać zgodności wiecznie. To po prostu jest niemożliwe. W każdej dziedzinie ostatecznie trzeba powiedzieć: „Przykro mi, ale już nie będziemy tego robić w ten sposób” oraz „Przepraszamy, ale musicie państwo kupić coś nowego” . Przyjrzyjmy się odbiornikom telewizyjnym. Od czerwca 2009 roku w Stanach Zjednoczonych odbiorniki telewizyjne nie działają z anteny. Wiele osób przekonało się o tym dopiero wtedy, gdy nastąpiła zmiana. Większość, czytając zapowiedzi takich zmian, zdawała sobie sprawę z tego, co ma nastąpić. Pomimo to nie istnieje łatwy czy też bezproblemowy sposób naruszania zgodności wstecz. Identycznie rzecz się ma z systemami odziedziczonymi. Ludzie chcieliby, aby istniało jakieś magiczne rozwiązanie problem u dziedzictwa. Tymczasem faktem jest, że łatwe rozwiązanie nie istnieje. To poważny problem. Trzeba zakasać rękawy i wziąć się do roboty. Nie m a w tym nic specjalnego. Próbujesz stworzyć dwa niezgodne ze sobą systemy. Zawsze będzie to kosztowało trochę kłopotów i pracy oraz wymagało podjęcia trudnej decyzji, kiedy unieszczęśliwić pewną grupę ludzi. Czy woli pan, aby wykonywać takie zmiany dużymi krokami — raz na jakiś dość długi czas — czy też unieszczęśliwiać w bardzo niewielkim stopniu, ale często?

James: W każdym nowym systemie popełnia się błędy. Można wprowadzać lokalne, chwilowe zmiany. Ostatecznie jednak okaże się, że niektóre podstawowe założenia i decyzje architekturalne już nie stanowią zwartej całości. Wtedy trzeba wprowadzić poważniejsze zmiany i całkowicie wszystko przeorganizować. W przeciwnym wypadku rozwój przestanie być możliwy. Ja nazywam tę sytuację trzęsieniem ziemi. Można to robić raz dla całego systemu. Więcej nie można z uwagi na to, że istnieje zbyt wiele wbudowanych zobowiązań. W końcu system stanie się tak skostniały, że wprowadzanie istotnych zmian przestanie być możliwe. Myślę, że takie blokady zdarzają się w wielu systemach — w systemach komputerowych, ale także w innych systemach spotykanych

UML

425

w okół nas. Ostatecznie przychodzi ktoś z mieczem Aleksandra i znajduje nowe rozwiązanie, które zastępuje stare podejście. Nie naprawia się starych rzeczy. Ktoś przychodzi z czymś nowym i powoduje, że stare rzeczy stają się bezużyteczne. Myślę, że to sprawdza się w przypadku komputerów. Świat jest trudnym miejscem, ale musimy go zaakceptować w takiej postaci, w jakiej jest. Nie wiem, dlaczego to miałoby być niewłaściwe. To po prostu fakt. Nie używamy ciągle tych samych rzeczy tylko dlatego, że ktoś wymyśla coś nowego. Jeśli przyjrzelibyśmy się problemom transportu w XIX wieku, to ktoś mógłby powiedzieć: „Przewidujemy, że niedługo będzie tak wiele koni, że ulice będą tonąć w końskim nawozie i nie będzie możliwe zbudow anie tak wielu stajni!” . Z kolei w XX wieku mów iono, że w końcu każdy będzie musiał zostać operatorem telefonicznym. Takie prognozy wynikają z tego, że ludzie nie potrafią wybiec myślą poza świat, w jakim żyją i pracują w danym momencie. Jednak ostatecznie ktoś wymyśla nowy sposób wykonywania pewnych rzeczy — taki, o którym wcześniej nie myślano — co powoduje, że stare problemy przestają istnieć. Myślę, że to optymistyczny obraz. Może być jednak niekorzystny dla ludzi, którzy całe swoje życie poświęcili określonemu sposobowi działania i nie potrafią się zmienić. Ostatnie pół wieku pokazało nam, że nie można wziąć posady i trwać przy niej bez żadnych zmian. Kiedyś możliwa była praca w jednej firmie przez całe życie i można było liczyć na to, że doczeka się emerytury w tej samej branży, w jakiej zaczęło się pracę bezpośrednio po ukończeniu szkoły. Ale te czasy dawno minęły. Trzeba przygotować się na zmiany. Heinlein powiedział: „Specjalizacja jest dla owadów".

James: Owady rozmnażają się gwałtownie i giną w wielkiej liczbie. Dla człowieka takie życie nie byłoby zbyt dobre. Martwi mnie sposób, w jaki wiele firm zatrudnia pracowników. Chcieliby zatrudniać owady — zatrudniają więc ludzi w yspecjalizowanych w pewnych bardzo wąskich dziedzinach. Chcieliby, żeby ci ludzie zaczęli pracować natychmiast po zatrudnieniu. Kiedy zrobią swoje, najlepiej by było, żeby odeszli. Myślę, że to bardzo zły trend. Nie sądzę, aby w dłuższej perspektywie takie postępow anie służyło ludzkości. Potrzebujem y ludzi, którzy potrafią myśleć, zmieniać się oraz uczyć tego, czego pow inni się uczyć. Nie widzę specjalnego sensu, by w szkole uczyć się konkretnego języka programowania. W szkole pow inniśm y się uczyć koncepcji języków program owania. Nauczenie się nowego języka jest łatwe. Potrzebujemy ludzi, którzy potrafią się zmieniać. Ludzie muszą obserwować, co się dzieje. Mówi się, że współcześnie każdy musi uczyć się przez cały czas, i to jest prawda. Czy to możliwe, żeby zmienił pan swoje krytyczne zdanie na temat języka UML?

James: Oczywiście, że tak. Myślę, że pomysł ze standaryzacją był w większym stopniu posunięciem m arketingow ym niż czymkolwiek innym . Po co ktoś m iałby

426

ROZDZIAŁ

CZTERNASTY

standaryzować język modelowania? Standaryzować powinno się coś, co praktycznie urucham iam y. Myślę, że cała koncepcja standaryzacji jest bardzo przeceniona. Do modelowania nie są potrzebne standardy. Jeśli język UML jest zbyt rozbudowany, ludzie wydzielą z niego części, których potrzebują. Wszyscy używają modeli klas, a wiele osób korzysta z takich rzeczy, jak diagramy sekwencji i przypadki użycia. Są też fragmenty języka UML, których używa niewiele osób. Standaryzacja pokazuje niebezpieczeństwo zebrania zbyt wielu m ądrych ludzi, którzy mają niewielkie możliwości podejmowania decyzji. W padają na wiele pomysłów, które wydają się użyteczne, i nie ma sposobu, by powiedzieć: „To ciekawy pomysł, ale nie jest wystarczająco przydatny do tego, by wrzucić go do tego większego garnka, tak by służył większej grupie osób” . Ludzie korzystają z tego, czego chcą. Nie różni się to zbytnio od sposobu, w jaki korzystamy z Photoshopa. Mogę go używać, ale nie jestem ekspertem. Nie pamiętam, w jaki sposób posłużyć się większością jego funkcji — mogę ich poszukać, jeśli będę ich potrzebow ał. W iem jednak, jak dostosow uje się poziom y i zaznacza obiekty, a także inne czynności, z których korzystamy przez cały czas. Jeśli będę musiał zrobić coś więcej, to tego poszukam. Profesjonalny projektant grafiki będzie znał ten program znacznie lepiej. To właśnie jest sposób, w jaki używa się większości rozbudowanych aplikacji lub urządzeń — na przykład telefonów komórkowych. Nie wiemy, jak używać wszystkich funkcji, poniew aż większość z nich spełnia zadanie chwytów m arketingow ych — napisów na pudełku, które czytamy w sklepie. Ocena użyteczności języków modelowania je st trudna. Można poprosić kogoś, by użył języka programowania do rozwiązania rzeczywistego problemu i powiedział, w jakim stopniu model okazał się przydatny.

James: Przyjrzyjmy się dostępnym językom program ow ania — kłóciłbym się, czy tak wiele z nich jest zgodnych z tym paradygmatem. W większości przypadków projektanci mają mądre pomysły i umieszczają je w języku bez wykonywania dobrych testów użyteczności. W efekcie powstają problemy. Projektanci języka opracowują wymyślone zadania, które według nich powinny być rozwiązane. Czasami pow inno się powiedzieć: „Nie m usim y rozwiązywać tego problemu; można to obejść w inny sposób. To nie jest na tyle ważne, aby stało się częścią języka” . W takich systemach, jak język m odelowania, język program ow ania czy aplikacja, należy uwzględnić własności na tyle użyteczne, aby uzasadnić konieczność zapamiętania sposobu ich działania przez użytkow ników . N astępnie trzeba je przetestować, aby zyskać pewność, że działają w praktyce. Jeśli napchamy tych własności zbyt wiele, nikt nie będzie pam iętał, jak ich używać. Zatem w rezultacie staną się ciężarem. Istnieje większe prawdopodobieństwo, że spowodują problem dla systemu, ponieważ projektanci będą mieli więcej do przetestow ania, a przy tym więcej okazji

UML

427

do popełnienia błędów. Nie można po prostu spytać, czy coś jest przydatne; trzeba zapytać, czy jest wystarczająco przydatne w porów naniu ze wszystkim innym, aby warto było ponosić koszty pamiętania, jak korzystać z danej własności. Jedna z tendencji w rozwoju języka UML polega na tym, by doprowadzić do sytuacji, w której użytkownicy korzystają z podzbiorów języka. W rzeczywistości tak właśnie się dzieje. W ątpię, czy proces OMG jest w stanie rozwiązać jakikolwiek problem, poniew aż proces ten nie prowadzi do podjęcia właściwych decyzji. Nie sądzę, aby język UML 3.0, będący efektem prac grupy OMG, rozwiązał rzeczywiste problemy, ponieważ istnieje zbyt wiele sprzecznych ze sobą interesów. Zbyt wiele osób chce wstawiać do języka swoje konstrukcje. W związku z tym nie ma możliwości zachowania prostoty języka. Ja umieszczę twoją głupią konstrukcję, jeśli ty umieścisz moją.

James: Właśnie tak. Za dużo w tym wszystkim kupiectwa. Inny możliwy scenariusz to opracowanie przez inny podmiot czegoś nowego, bazującego na UML, ale o nowej nazwie oraz kilku innych podstawowych decyzjach projektowych. Oczywiście taka sytuacja w końcu prawie zawsze następuje. W przeciwnym razie ludzie stwierdzą, że narzędzie się nie sprawdza, i zamienią je na inne.

Trochę o wielokrotnym wykorzystywaniu Wydaje się, że złożoność i rozmiary aplikacji coraz bardziej rosną. Czy techniki programowania obiektowego dobrze komponują się w tej rzeczywistości, czy też tylko mocniej wszystko komplikują?

James: Po pierwsze, nie jest jasne, jak szybko rosną rozmiary systemów. W programie nie można mierzyć samej liczby bajtów. W przypadku generowania kodu znaczenie ma liczba wierszy kodu źródłowego, a nie liczba wygenerowanych wierszy, bajtów czy czegokolwiek innego. Jeśli wykorzystamy procedury wyższego poziom u, to złożoność będzie zależeć od liczby wywołań, a nie od ilości uruchomionego kodu. W miarę pracy na coraz wyższych poziomach otrzymujemy coraz większe systemy, ale ich w ew nętrzna złożoność niekoniecznie rośnie w takim samym stopniu. Nawet jeśli ktoś się z tym nie zgadza (ja zupełnie się z tym nie zgadzam — systemy rzeczywiście stają się bardziej złożone), systemy obiektowe są dobrym kierunkiem. Trzeba jednak oddzielić techniki obiektowe od podstawowego akcentu na możliwości wielokrotnego wykorzystywania. Wiem, że tworzenie części wielokrotnego użytku było głównym celem projektantów Smalltalka, którzy opracowali techniki obiektowe, ale myślę, że to był błąd. Strukturę obiektow ą m ożna stworzyć bez obsesyjnego koncentrow ania się na próbie budow ania biblioteki części wielokrotnego użytku zawierającej wszystkie klasy, których używamy w aplikacji. Możliwość wielokrotnego wykorzystywania elementów jest dobra, ale w rzeczywistości nie stanowi ona głównego

428

ROZDZIAŁ

CZTERNASTY

celu większości systemów. Tworzenie napraw dę dobrych bibliotek wielokrotnego użytku jest niezwykle trudne — wielu programistów nie potrafi tego robić zbyt dobrze i nie ma sensu nakłaniać ich, by podejmowali takie próby. Budowanie elementów w ielokrotnego użytku to zadanie oddzielne od budow ania systemu. Podczas projektow ania systemu w arto wykorzystać strukturę obiektową w celu stworzenia przejrzystej aplikacji złożonej z klas łatwych do modyfikowania, ale bez nacisku na to, by klasy te były wykorzystywane przez kogoś innego. Jeśli okaże się, że często tworzymy odmiany klasy, możemy mieć kłopoty z tym, aby rzeczywiście stała się ona klasą wielokrotnego użytku. Trzeba także wiedzieć, kiedy przestać. W idziałem wielu początkujących, którzy roztkliwiali się nad tworzeniem obiektów wielokrotnego użytku przy każdym wierszu kodu. Jeśli można wyrazić prosty algorytm w języku naturalnym i napisać odpowiedni kod, to nie ma potrzeby dzielenia go na mniejsze części — wystarczy, jeśli po prostu napiszemy kod. Program ow anie obiektowe ma za zadanie stworzenie bardziej wysokopoziomowej struktury wtedy, gdy problem y nie są proste, a nie wtedy, gdy trzeba implementować drobiazgi. Skąd możemy mieć pewność, że zalety technik obiektowych przewyższają ich wady?

James: Tak jak powiedziałem wcześniej, strukturę obiektową można wykorzystać zawsze. Problem polega na zdecydowaniu, na jak niski poziom z tym zejść. Najwięksi zwolennicy technik obiektowych zawsze dążą do stosowania ich zawsze i wszędzie. Istnieje wiele innych — oprócz struktury obiektowej — problem ów w technice obliczeniowej, na przykład dobre algorytmy, dobre struktury danych, akceptowalna złożoność obliczeniowa, zrozumiałość itp. Nie wszystko dotyczy program ow ania obiektowego. W rzeczywistości program ow anie obiektowe jest jedynie niewielką częścią całego obrazu. Paradygmat obiektowy dostarcza przydatnego frameworku do organizacji projektów i programów. Jest to bardzo ważne, ponieważ w przeciwnym razie problem mógłby nas przytłoczyć i wpędzić w zakłopotanie. Jednak zasadniczą treścią dow olnego projektu w żadnym razie nie są obiekty — tworzą ją wszystkie pozostałe rzeczy, o których wspominałem. Wspomniał pan także, że możliwości wielokrotnego wykorzystania nie stanowią sedna obiektowości.

James: Nie sądzę, aby tak powinno być. Możliwości wielokrotnego użytku są mocno przeceniane i tak było od samego początku. To był argument marketingowy używany po to, by przekonać menedżerów do inwestowania w tę technologię. Zapewnienie możliwości wielokrotnego wykorzystywania obiektów jest bardzo trudne. Tworzenie komponentów wielokrotnego użytku stanowi znacznie trudniejszą sztukę, niż posiada większość osób. Chyba Brooks lub Parmas powiedział, że trzy razy trudniej jest wykorzystać coś w praktyce niż w laboratoryjnym prototypie, a kolejne trzy razy

UML

429

trudniej jest umożliwić wykorzystanie tego samego elementu wielokrotnie. Kiedy robimy coś jednorazowego, to upieranie się, aby było to wielokrotnego użytku, jest w większości przypadków stratą czasu i wysiłków. Można jednak przedsięwziąć pewne środki zaradcze, aby w przyszłości wprowadzanie zm ian było łatwiejsze. Po pierwsze, nie należy robić niczego bardzo konkretnie, jeśli można to zrobić w sposób bardziej ogólny. Nie należy zapędzać się w kozi róg, gdy nie ma takiej potrzeby. Jeśli można znaleźć sposób na uogólnienie problemu bez większego dodatkow ego wysiłku, należy to zrobić. Przy projektow aniu systemów należy brać pod uwagę to, że w przyszłości może powstać konieczność ich modyfikacji. Nie oznacza to, że wszystkie uogólnienia trzeba w prowadzać już podczas pisania pierwszej wersji. Należy pozostawić otwartą furtkę. Należy zostawić haki potrzebne do wprowadzenia zmian. Fragmenty operacji powinno się implementować w postaci m etod, które m ożna zastąpić, ale nie m ożna pisać zbyt ogólnych m etod, jeśli nie wiadomo, czy będą potrzebne. Komplikacja polega na tym, że bardzo trudno zgadnąć, jakie potrzeby będziemy mieli w przyszłości. Często przewidywania okazują się nietrafne. Możemy poświęcić dużo czasu na generalizowanie jakiegoś pomysłu, a potem może się okazać, że to nie był ten obszar, w którym miały być wprowadzone zmiany. Podobnie jest z optymalizacją. Myślę, że zbyt wielu programistów przesadnie martwiło się tym, by wszystko działało bardzo szybko. Przez wiele lat ludzie mieli obsesję na punkcie szybkiego działania. M imo że kom putery stały się tak szybkie, nie ma to żadnego znaczenia. Programiści w dalszym ciągu mają obsesję na punkcie czegoś, co ja nazywam mikrooptymalizacją. Nie znają teorii złożoności, nie rozumieją rzędów złożoności, ale przejmują się drobnymi poprawkami szybkości działania. Kiedyś napisałem pakiet procedur i stworzyłem jego profil. Nie zawsze m ożna odgadnąć, gdzie trzeba przeprowadzić optymalizację. Należy dokonać odpowiednich pomiarów, a następnie rozwiązać występujące problemy. Jedna procedura zajmowała 30% czasu. Poprawiłem tylko tę procedurę. Ludzie przesadzają z optymalizacją. Sprawia to, że występuje większe prawdopodobieństwo awarii programu, a optymalizowany fragment może nie mieć zbyt dużego znaczenia. Myślę, że jest to dziedzina, której ludzie nie rozumieją. Mówi się: „W ażna jest wydajność, systemy w budow ane itd.” . Nonsens. Wszystko działa coraz szybciej. N adm ierna optymalizacja nie jest w arta wszystkich kosztów oraz błędów, które powoduje. Nie mogę zrozumieć, dlaczego ludzie nie robią prostych rzeczy, które mogliby wykonywać. Dzieje się coś złego ze sposobem myślenia wielu program istów, że takie rzeczy ciągle się zdarzają. Myślę, że niektórzy z nich lubią sami sobie utrudniać zadanie — coś w rodzaju alpinistów, którzy nie używają lin. Ale jeśli wspinają się w taki sam sposób, jak programują, to będą martwi.

430

ROZDZIAŁ

CZTERNASTY

Jeśli wielokrotne wykorzystywanie obiektów nie je s t celem programowania obiektowego, to co jest jego celem?

James: Możliwość wielokrotnego wykorzystywania wynika z projektu obiektowego, ale ja uważam, że celem projektu obiektowego jest jego wykorzystywanie, a nie wielokrotne wykorzystywanie. Stworzenie obiektowej struktury ułatw ia poprawne wykonanie projektu, a także zapewnia możliwości modyfikowania. W efekcie mogą powstać pewne komponenty wielokrotnego użytku, ale to tylko dodatek. Wiemy, że będziemy zmuszeni do zmodyfikowania aplikacji, ale nie wiemy gdzie. W związku z tym tworzenie systemów w sposób obiektowy ułatwia wprowadzanie przyszłych zmian, ponieważ tworzy strukturę ułatwiającą wprowadzanie modyfikacji. Sens polega na zapew nieniu możliwości w prow adzania zmian, ale niekoniecznie na stw orzeniu biblioteki kom ponentów w ielokrotnego użytku. Nie zaczynamy tw orzenia aplikacji z oczekiwaniem, że wszystkie pozostałe osoby w naszej firmie skorzystają z każdej klasy, którą stworzyliśmy. Ktoś inny w naszej firmie (a może nawet my sami) będzie zm uszony zmodyfikować program , który napisaliśmy. W iemy na pewno, że to się zdarzy. Projekt obiektowy ułatwia wprowadzanie przyszłych zmian. Myślę, że to największa wartość. Przy pisaniu programu od podstaw równie łatwo przyjąć dowolne inne rozwiązanie. Dopiero przy drugiej wersji opłaca się zastosować strukturę obiektową. Ze względu na hermetyzację pewnych elementów.

James: Tak. Dzięki strukturze obiektowej uzyskujemy czytelniejszy projekt, który jest w mniejszym stopniu poprzeplatany. Ten sposób tworzenia struktury pozwala uniknąć wiązania funkcjonalności w węzły. Oto prawdziwy problem, z którym ludzie muszą sobie radzić: bardzo złożona funkcjonalność. W pewnym sensie wykorzystamy ją wielokrotnie w następnej edycji tej samej aplikacji. Jeśli ktoś chce, może to nazwać funkcjonalnością w ielokrotnego użytku. Tylko niewielka część aplikacji będzie wykorzystana w wielu różnych projektach. Zanim zaczniemy wykorzystywać coś wielokrotnie na szerszą skalę, powinniśmy użyć tego co najmniej trzy razy. Pierwszy raz zawsze jest przypadkiem specjalnym, dwa razy to może być zbieg okoliczności, trzy razy — OK, teraz m ożna już zauważyć pewne schematy. Być może dopiero teraz warto zadać sobie trud wydzielenia części i utworzenia z nich komponentów dających się zastosować wiele razy. Nie ma potrzeby, aby wszystko było komponentem wielokrotnego użytku. Należy znaleźć elementy, które są najbardziej przydatne, i wykorzystać je wielokrotnie. To przypomina m i architekturę SOA, w której zakłada się możliwość zdefiniowania usług, które będą wykorzystywane w całym przedsiębiorstwie.

James: Moim zdaniem architektura SOA to w większym stopniu chwyt marketingowy niż cokolwiek innego. Nigdy nie mogłem zauważyć w tym głębszej treści. To idea dość powierzchowna.

UML

431

Wiele informacji na tem at architektury SOA to narzędzia marketingowe. Oczywiście trzeba to sobie odpowiednio wytłumaczyć. Przy okazji pewnego projektu, w którym brałem udział, przekonałem się, że dobra nazwa w projekcie jest warta tyle, ile rok aktyw nych działań. W mojej karierze raz wymyśliłem doskonałą nazwę, która zdecydowanie pomogła. Dla ludzi nazwy są bardzo ważne. Niestety, dla technologii OMT i UML nie potrafiliśmy znaleźć dobrych nazw. Nie lubię stosować skrótów, jeśli da się ich uniknąć. Czasami jednak to najlepsze, co można zrobić. Jakie związki dostrzega pan pomiędzy paradygmatem obiektowym a nową koncentracją na współbieżności?

James: Pojęcie obiektu jako samodzielnego pakietu obejmującego strukturę danych oraz działanie jest idealne dla współbieżności. W otaczającym nas świecie wszystko jest samodzielne i wszystko działa współbieżnie, a zatem idea obiektów w modelowaniu idealnie się nadaje do zaprezentowania współbieżności. Niestety, języki program owania nie odzwierciedlają dobrze tych własności. Prawie wszystkie języki programowania, których się uczymy, są w zasadzie sekwencyjne. Być może istnieje kilka akademickich języków, które są wewnętrznie współbieżne, ale ich większość z nas podczas kursów programowania się nie uczy. Można co prawda dodać mechanizmy współbieżności do takich języków, jak Smalltalk, C++ czy Java, ale modele obliczeniowe leżące u ich podstaw oraz sposoby myślenia są wewnętrznie sekwencyjne. A zatem problemem nie jest paradygmat obiektowy, ale raczej języki programowania i systemy. Oczywiście zaprojektow anie w spółbieżnych języków jest możliwe. Sam zaprojektowałem taki język w ramach mojej pracy doktorskiej. Podobne języki stworzyło na początku lat siedemdziesiątych kilku m oich kolegów, studentów z Com putation Structures Group Jacka Dennisa z MIT. Nie jest trudno opracować nowy język (chociaż trudno jest stworzyć taki język, którego można by łatwo użyć do rozwiązania szerokiego zakresu praktycznych problemów). Trudno jest spowodować, aby język zyskał popularność. Nie ma pieniędzy na inwestowanie w języki — zasadniczą cechą popularności języka jest powszechność używania, a ludzie nie chcą używać języka, który został zastrzeżony. Firmie trudno jest poświęcić potrzebne zasoby, jeśli miałyby one być stracone (gdyby jakaś firma godziła się na stratę środków, byłoby to podejrzane). Nie warto wierzyć w nonsens, że ktoś wynalazł lepszą pułapkę na myszy — akceptacja produktu na rynku wymaga pow ażnych działań marketingowych. A zatem nie sądzę, aby dobry współbieżny język programowania mógł uzyskać szeroką akceptację — nie dostrzegam motywacji, która miałaby to spowodować.

432

ROZDZIAŁ

CZTERNASTY

W ja k i sposób należy zaprojektować język, który będzie wewnętrznie obsługiwał współbieżności

James: Podczas mojego przewodu doktorskiego pracowałem z profesorem Jackiem Dennisem oraz jego studentam i w MIT nad kom puteram i i językami przepływu danych. Były to języki wewnętrznie współbieżne, bardzo nowatorskie, zainicjowały wiele różnych badań w następnych latach. Niestety, istniało kilka problemów, których ja nie rozwiązałem i których nie udało się rozwiązać nikom u innemu. Była zatem obiecująca idea, która jednak w dłuższej perspektywie nie zadziałała. Niektóre z pomysłów opracowanych dla tamtego języka przeniosłem do UML, ale w większości przypadków nie zanosi się na to, aby architektura przepływu danych mogła zastąpić architekturę von Neumanna. A zatem miałem swoją szansę i jej nie wykorzystałem. Istnieją również automaty komórkowe. Około połowy moich kolegów doktorantów próbow ało zbudować na ich bazie kom putery równoległe. Powinno to być dobre podejście, poniew aż właśnie w taki sposób jest skonstruow any wszechświat (a może nie; współczesna fizyka bywa bardziej dziwna niż fikcja: ostatnio pojawiły się spekulacje, że przestrzeń i czas wynikają z czegoś bardziej prymitywnego). Wydaje się jednak, że automaty komórkowe nadają się tylko do rozwiązywania określonych problemów geometrycznych. Co prawda są to bardzo ważne problemy, ale nie są natury ogólnej. Nie udało się znaleźć sposobu ich programowania dla ogólnego przypadku. Być może taki ogólny przypadek nie istnieje. Wydaje się, że najważniejszą rzeczą jest interfejs pomiędzy przepływem sterowania a strukturą danych. Wysoce równoległy przepływ sterowania nie harmonizuje z dużymi fragm entam i danych. Nie m a jasności co do tego, w jaki sposób zachować współbieżność danych, a jednocześnie w dalszym ciągu zachować zdolność wykonyw ania obliczeń, do których jesteśmy przyzwyczajeni. Praw dopodobnie powinniśmy brać pod uwagę nowe rodzaje obliczeń. Mózg jest wysoce współbieżnym komputerem, w którym nie działają algorytmy von Neumanna, ale nie mamy pojęcia, w jaki sposób zaprogramować coś, co byłoby zorganizowane tak jak ludzki mózg. Praw dopodobnie nie m ożna w ten sposób programować: pojęcia wydajnej programowalności i ekstremalnej współbieżności mogą się wzajemnie wykluczać. Być może jest to rodzaj nieoznaczoności Heisenberga na wyższym poziomie (znów fizyczne podstawy). Kiedy mówi pan o językach programowania przystosowanych do współbieżności, to oczywiste wydaje się powiązanie z językam i funkcyjnymi. Jakie są słabe strony języków funkcyjnych i dlaczego nie dostarczają one wskazówki do łatwej implementacji współbieżności?

James: Języki funkcyjne doskonale nadają się do wyrażania świata, który jest tak współbieżny, że nie ma potrzeby mówienia o współbieżności. Problem polega na tym, że w rzeczywistym świecie często potrzebny jest pośredni grunt, na którym można

UML

433

jawnie mówić o niektórych rodzajach współbieżności. Myślę, że to kolejny przykład naruszenia symetrii. Języki funkcyjne mają jednak wiele zalet. Byłoby dobrze, gdyby można ich było selektywnie używać razem z bardziej imperatywnymi językami.

Relacje symetryczne Powiedział pan, że w większości języków programowania asocjacje gwarantują bardziej symetryczny pogląd na sytuację w porównaniu ze wskaźnikami. Czy mógłby pan rozwinąć tę myśl?

James: W rzeczywistym świecie relacje są relacjami. Zazwyczaj nie są one hermetyczne w takim sensie, że działają w jedną stronę. Czasami tak się zdarza, ale w większości przypadków, jeśli A jest powiązany z B, to B jest powiązany z A. Czy jest to relacja dwukierunkowa?

James: Tak, to jest relacja. Relacje są w ew nętrznie dw ukierunkow e. Relacje matematyczne są dwukierunkowe. Samo użycie słowa „dwukierunkowe” świadczy 0 tym, że mamy na myśli wskaźniki. Relacja to relacja. Informacje są ze sobą powiązane 1 żadna strona nie ma pierwszeństwa — być może tak należałoby to powiedzieć. Dwukierunkowa nie oznacza oczywiście symetrycznej — jeśli pies ugryzie człowieka, to coś innego niż to, kiedy człowiek ugryzie psa. Myślenie o strukturze danych i strukturze systemu w kategoriach relacji jest dobrym punktem wyjścia. To podejście o wiele lepsze od próby enkapsulacji ich jako wskaźników. Na poziomie kodu mówimy oczywiście o specyficznym języku programowania. W tym momencie należy myśleć o tym, jakie możliwości daje określony język programowania. Kiedyś stworzyłem język program owania z w budow anym i relacjami i okazało się, że działał doskonale. Nie trzeba było zwracać uwagi na kierunkowość — można było dość łatwo posługiwać się relacjami w obu kierunkach. Koszty nie były przy tym zbyt wysokie. Jestem zaskoczony, że inne języki nie zapewniają takich możliwości. Czy był to język bazujący na przepływie danych?

James: W rzeczywistości był to pakiet struktury danych. Ja nazw ałem go DSM (od ang. Data Structure Manager). Był to w zasadzie zbiór procedur obsługi struktur danych, ale skonfigurowałem go w taki sposób, aby można było łatwo posługiwać się nim w kategoriach relacyjnych w dowolnym kierunku. Aby usunąć krotkę z relacji, w ystarczyło po prostu usunąć krotkę. Zoptym alizow ałem ten język tak, by m ógł działać w dowolnym kierunku, oraz wykorzystałem funkcje skrótów (ang. hashing) w celu zapewnienia liniowej złożoności. Dzięki temu użycie języka nie powodowało istotnych kosztów wydajności.

434

ROZDZIAŁ

CZTERNASTY

Wszystkie te techniki są dobrze znane, ale przeciętny człowiek nie potrafi ich bez trudu zaprogramować. Należy umieścić je w bibliotekach lub wbudować w języku. W ątpię, czy większość program istów potrafiłaby skorzystać z funkcji skrótów. Nie był to rozbudow any język, ale zastosowaliśmy jedno podejście do wykonania skrótów dla zbiorów, relacji oraz rozszerzalnych tablic. Jeśli wypełniło się tablicę danymi, jej rozmiar wzrastał dwukrotnie. Złożoność była liniowa kosztem pewnej ilości pamięci. W tym celu nie przepisywaliśmy całego języka od początku. Zamiast tego stworzyliśmy rodzaj zakładki na bazie innego języka. W ten sposób udało się uprościć wiele rzeczy. Nigdy nie mieliśmy problemów z przepełnieniem bufora. Takie podejście może dać zaskakujące rezultaty. Jestem zaskoczony, że ludzie nie korzystają z niego częściej. Wiem, że w języku C++ kilku m oich kolegów stworzyło generyczne klasy-szablony. Nie jestem pewien, czy są one często używane. Być może cały mechanizm był zbyt trudny do wykorzystania w C++, a może programiści byli po prostu zbyt leniwi, by się tego uczyć. Bardzo niewygodny jest dla mnie podział w świecie komputerów pomiędzy językami program ow ania a bazam i danych. Z jednej strony są specjaliści od języków programowania, którzy wykorzystują wskaźniki i mają obsesję na punkcie wydajności. Ludzie ci mają problemy z programami, w których znajduje się mnóstwo kłopotliwych błędów. Z drugiej strony są specjaliści od baz danych, którzy posługują się relacjami. Ludzie ci rozumieją pojęcie relacji. Stąd właśnie wziąłem pomysł relacji. Relacje nie tworzą takiej obsesji dotyczącej wydajności, jak wskaźniki. W pewnym sensie baza danych jest wysoce niewydajna z punktu widzenia programowania, ale dzięki temu zapewnia inne własności. Dane muszą być bezpieczne, nie mogą ulegać awarii. Wydaje się, że nie ma nikogo pośrodku. Być może jest to podobny układ do tego, z jakim m am y do czynienia w polityce — są ludzie w skrajnych frakcjach, natom iast brakuje ludzi środka. Próbowałem rozmawiać ze specjalistami w dziedzinie baz danych. Odniosłem wrażenie, że nie doceniają tego, co dzieje się w świecie języków program ow ania. W drugą stronę sytuacja jest podobna. Sądzę, że byłoby lepiej, gdyby te dwie strony się spotkały i gdyby nie istniał taki silny podział. I gdyby tak istniały języki, w których m ożna łatwo wykonywać operacje typowe dla relacyjnych baz danych. Można by łatwo wykonywać operacje proceduralne oraz bez trudu przechodzić od jednego do drugiego podejścia. M ożna by również w większości przypadków wykonywać operacje w bezpieczny sposób, z wykorzystaniem w budow anych m echanizm ów . W ten sposób moglibyśmy uzyskać wydajność obliczeniową nieosiągalną dla przeciętnego program isty i bez obsesji na punkcie samodzielnych m ikrooptym alizacji na poziomie bitów. Ludzie uważają, że ich w ydajność rośnie. W rzeczywistości tak nie jest, poniew aż nie zwracają uwagi na wydajność wysokopoziom ow ą. Nie widzą lasu z pow odu drzew. Zajmują się mikrooptymalizacjami, ale takie podejście grozi znacznym obniżeniem wydajności na wyższych poziomach.

UML

435

To chyba w książce „The Practice o f Programming" [Addison-Wesley Professional] Kernighan powiedział, że w jednym ze starych narzędzi uniksowych użyto prostego liniowego skanowania.

James: Tak, zgadza się. Dlaczego ciągle korzystamy z Uniksa? Który to mamy rok? 2009? Kiedy opracowano system Unix? M inęło czterdzieści lat od jego powstania, a w dalszym ciągu istnieją w nim takie same założenia — jak na przykład reprezentow anie znaku na jednym bajcie. Nie da się zakodować znaku za pomocą jednego bajtu. To się zmieniło, ale w dalszym ciągu niektórzy myślą o ciągach znaków jako o tablicach bajtów. Współcześnie to nie działa. Oczywiście niektóre pojęcia zostały zaktualizowane. Unix został pierwotnie stworzony dla kom putera PDP-7, który miał 64 kB pamięci i wykorzystywał 18-bitowe słowa (a nie mizerne 16 bitów). Pozwalał na dowolną wymianę z dyskiem całej zawartości pamięci. Nie m am pojęcia, ile pojęć ze starego Uniksa ciągle jest zaszytych w tym systemie i oczekuje na spowodowanie problemów. Problem ciągle jest ten sam: pewni ludzie próbowali stworzyć nowe systemy operacyjne, ale pow stały trudności z ich adaptacją. Dlatego właśnie używamy takich starych rozwiązań. Początki kodu Windowsa sięgają lat osiemdziesiątych. Z mojego punktu widzenia to tylko 20 lat. Cóż, starzeję się. Kiedy ktoś mówi: „Tylko 20 lat”, to oznacza, że jest na tym świecie już od jakiegoś czasu. Jeśli sięgniemy do historii, zauważymy, że wiele rzeczy, które dziś się pojawiają, wcale nie jest nowych. Mieliśmy z nimi już do czynienia trzy lub cztery razy. Ludzie przez cały czas dokonują tych samych wynalazków i myślą, że są one nowe. W przeszłości wielokrotnie byłem świadkiem tego rodzaju zjawisk. Zacytuję Elronda: „Moja pamięć sięga Starych Czasów. Widziałem wiele klęsk i wiele bezowocnych zwycięstw” . Spróbuję przytoczyć inną myśl. Znam wielu ludzi, którzy głoszą swoje poglądy na temat kryzysu oprogramowania. Wiele języków modelowania sprzedano pod tym hasłem. Sam jestem tem u winien. Z drugiej strony mogę wejść do serwisu Fry’s Electronics i kupić urządzenia czy oprogramowanie, które z roku na rok stają się coraz bardziej złożone. Z każdym rokiem kosztują one coraz mniej. Ludziom jakoś udaje się wymyślić nowe aplikacje. Być może nie istnieje żaden kryzys oprogramowania. Być może ktoś celowo go nagłaśnia, aby uzyskać efekt szoku. Można także dyskutować o takich sprawach jak bezpieczeństwo. O bezpieczeństwie mówimy wtedy, gdy ktoś zadecyduje, że jest ono wystarczająco ważne. Mówi się, że jest istotne, ale istnieją dowody na to, że ludzie nie uznają go za wystarczająco ważne, aby za nie płacić. Jeśli będzie wystarczająco ważne, producenci technologii zwrócą na nie uwagę. Nie ma znaczenia to, co ludzie mówią o bezpieczeństwie, jeśli nie są gotowi na ponoszenie większych wydatków w postaci pieniędzy, cykli pamięci i tym podobnych rzeczy.

436

ROZDZIAŁ

CZTERNASTY

Jeśli bezpieczeństwo jest dziś ważne, to czy na tyle, aby warto było zrezygnować z pisania w językach C lub C+ + z powodu wskaźników występujących w tych językach, a stwarzających zagrożenie dla bezpieczeństwa?

James: Oczywiście. Ludzie mają to, za co płacą. Weźmy na przykład bazy danych — takie produkty bazodanowe, które powodują utratę lub uszkodzenia danych, niezbyt długo pozostają na rynku. Kiedy programiści piszą menedżer bazy danych, poświęcają wiele wysiłku na to, by nie powodował on utraty danych. Awarie to jedno, ale jeśli menedżer danych powoduje ich utratę lub uszkodzenie, jest znacznie gorzej. W projektach często mówi się o nadaw aniu priorytetów błędom przed wydaniem. Błędy krytyczne — takie, które pow odują awarię aplikacji — zwykle są uznawane za najważniejsze i zostają poprawione w pierwszej kolejności. Myślę, że to bardzo niedobre podejście. Błąd, który powoduje uszkodzenie danych, ale nie powoduje awarii systemu, jest znacznie poważniejszy od błędu, który tylko uszkadza system, ponieważ w tym pierwszym przypadku nie zdajemy sobie sprawy z tego, że występuje problem, a tracimy dane. W rzeczywistości błąd, który powoduje pewną niewygodę, może być znacznie poważniejszy od błędu krytycznego. Kiedyś zbudowałem narzędzie modelowania. Stworzyłem listę priorytetów. Okazało się, że coś, co znalazło się nisko na liście, było trudne do zrealizowania, a należało to robić przez cały czas. Ta z pozoru niewielka rzecz była w praktyce tak denerwująca, że przeniosłem ją na pierwsze miejsce na liście. D robna niedogodność w często wykonywanej operacji może być znacznie poważniejsza od błędu, który powoduje zawieszenie systemu, ponieważ jeśli do tego dojdzie, możemy zrestartować system i kontynuować działanie. Jeśli coś denerwuje nas za każdym razem, kiedy klikniemy element na ekranie, z pewnością całkowicie zrezygnujemy z używania tego narzędzia. Większość podziękowań, jakie otrzymałem za poprawienie błędów, dotyczyło właśnie błędów tego rodzaju.

James: To prawda. Najgorsze są błędy, które najbardziej denerwują użytkowników. Nie wiem, czy czytał pan kiedyś książki Edwarda Tufte’a. Mówił on o wykresach-śmieciach — rodzaju wykresów w Excelu, które zajmują całą stronę, zużywają mnóstwo tuszu, jeśli się je drukuje, a prezentują bardzo niewiele informacji. Miał pomysł, aby zużywać mniej tuszu na ilość informacji na stronie. Tę samą koncepcję m ożna zastosować w odniesieniu do interfejsów użytkownika. Zawsze uważałem, że najlepsze aplikacje to takie, które wymagają jak najmniej klikania. Jeśli trzeba klikać w wielu miejscach, jeśli trzeba wykonać dwa kliknięcia wtedy, gdy wystarcza jedno, to jest zły projekt. To jedna z tych denerwujących rzeczy, które powodują, że przestajemy korzystać z narzędzia.

UML

437

Czego na podstawie pańskiego doświadczenia mogliby się nauczyć projektanci i programiści?

James: Ta najważniejsza lekcja to uświadomienie sobie faktu, że wszystko się zmienia. To jest zasada numer jeden w całym ludzkim życiu. Wszystko się zmienia. Kiedy tworzymy coś nowego, musimy pamiętać o zmianach. Kiedy piszemy aplikację, powinniśmy mieć świadomość, że z pewnością się zmieni w następnej wersji. Kiedy studiujemy, powinniśmy pamiętać, że to, czego nauczymy się na studiach, nie będzie jedyną rzeczą, którą będziemy robić do emerytury, i może się zdarzyć, że w naszym życiu będą następowały znaczące zwroty w karierze. Zmieniać się będą także potrzeby świata biznesu. Być może problemy dnia dzisiejszego całkowicie nie znikną, ale mogą stać się mało znaczące w relacji z innymi problemami. Zmiany następują wszędzie. Trzeba ich oczekiwać, uwzględniać je i uczyć się z nimi żyć. To wystarczy do osiągnięcia sukcesu. Ludzie, którzy potrafią radzić sobie ze zmianami, zarów no osiągają sukcesy, jak i wiodą dobre życie. Jeśli ktoś tego nie potrafi, będzie m iał kłopoty. Nie m a znaczenia, czy dotyczy to techniki obliczeniowej, czy czegokolwiek innego. W spółcześnie zasada ta sprawdza się w odniesieniu do wszystkich dziedzin.

UML W ja k i sposób zdefiniowałby pan UML?

Grady Booch: Zunifikowany język modelowania (ang. Unified Modeling Language — UML) to język graficzny służący do wizualizacji, specyfikowania, wnioskowania, dokum entow ania i konstruow ania artefaktów systemu oprogram ow ania. W tym zastosowaniu, w jakim ja go używam, nie jest językiem programowania, ale raczej językiem z głęboką semantyką, który przewyższa tradycyjne języki programowania i umożliwia pracę na poziomie abstrakcji równym i/lub powyżej kodu. Podczas której fazy wytwarzania oprogramowania stosowanie języka UML jes t najbardziej wydajne?

Grady: Język UML nadaje się do zastosow ania w całym cyklu życia systemu oprogramowania — od jego narodzin, poprzez śmierć, do kolejnych narodzin. Moim ulubionym zastosowaniem UML-a jest wykorzystanie go jako narzędzia wnioskowania na temat architektury systemu. Jestem również zwolennikiem wykorzystywania modelu K ruchtena 4+1. W mojej pracy zatytułow anej Handbook o f Software Architecture nie znalazłem ani jednego systemu, w którym UML nie wystarczałby do podejmowania cennych decyzji projektowych.

438

ROZDZIAŁ

CZTERNASTY

W jaki sposób język UML komunikuje się z innymi metodami inżynierii oprogramowania?

Grady: Chociaż Jim, Ivar i ja mieliśmy na myśli określone metodologie, to w języku UML nie ma niczego, co m ożna by powiązać z dow olnym sensownym procesem inżynierii oprogramowania. Przypuśćmy, że mam sceptyczne nastawienie do języka UML. W jaki sposób przekonałby mnie pan, że język ten jest przydatny?

Grady: Powiem dwie rzeczy: proszę spróbować wykorzystać go do udokumentowania systemu, który pan napisał, i sprawdzić, czy pomaga w zaprezentowaniu elementów znajdujących się na wyższym poziomie niż kod. Po drugie, proszę poszukać w internecie przykładów zastosowania języka UML i przestudiować sposoby wykorzystania go przez innych (serwis MediaWiki, systemy wbudowane oraz wiele rzeczy znajdujących się pomiędzy nimi). Co pan sądzi o używaniu języka UML do generowania kodu implementacji?

Grady: Język UML został zaprojektowany — i w dalszym ciągu się do tego nadaje — jako narzędzie wizualizacji, specyfikowania, konstruow ania i dokum entow ania systemów oprogramowania. Pod tym względem wytwarzanie sterowane modelami udow odniło przydatność w traktow aniu języka UML jako języka programowania, z którego m ożna generować program y wykonyw alne. Jednocześnie chciałbym podkreślić, że jestem zwolennikiem używania UML-a do wizualizacji projektu systemu podczas jego tw orzenia i rozwoju (inwersja projektow ania sterowanego m odelow aniem ). Czytałem, że w szpitalach są sale z dwoma lub czterema łóżkami, ponieważ dyskusje pomiędzy trzema osobami często przeradzają się w kłótnie dwóch na jednego, przy czym ta jedna osoba ciągle pozostaje nieprzejednana. Czy w pracy nad językiem UML w grupie trzech osób występowały podobne trudności?

Grady: Proszę pamiętać, że przez około rok było nas tylko dwóch (Jim i ja). Później przez kolejny rok było nas trzech (Jim, Ivar i ja), ale potem ludzi pracujących nad UML były dziesiątki i setki (ponieważ UML stał się publicznym standardem). Dynamika zm ian liczby osób zaangażow anych w projekt bardzo się zmieniała. W każdym przypadku interesująca była nie tyle natu ra kom prom isu, ile to, że osiągnęliśmy zbieżność poglądów wielu ludzi, którzy podchodzili do problemu z różnych perspektyw. Ta różnorodność oraz późniejsze udostępnienie języka szerszej grupie użytkowników spowodowały, że język UML zyskał tak powszechną akceptację. Gdyby miał pan przemyśleć język UML i stworzyć UML 3.0, w ja k i sposób podszedłby pan do tego problemu?

Grady: Mogę panu odpowiedzieć na to pytanie dokładnie, ponieważ sporo na ten temat myślałem. Na początek zacząłbym od zbioru przypadków użycia, które chciałbym

UML

439

zastosować w języku UML 3.0. Jedną z rzeczy, na które narzekałem w wersjach l.x i 2.0, jest to, że wiele z tego, co dziś występuje w języku UML, pochodzi z projektowania dół-góra i nie bazuje na przykładach form ułow anych w postaci stwierdzeń: „To są problemy. To są nasze bolączki. W jaki sposób je rozwiążemy? Oto jak to zrobiliśmy w bieżącej wersji języka UML. A tak możemy to poprawić” . Zacząłbym od wydzielenia zbioru przypadków użycia z dziedziny, aby uzyskać obraz tego, jakie systemy będziemy modelować. Następnie postarałbym się o to, by wersja 3.0 obsługiwała te przypadki użycia, a także celowo popracowałbym nad refaktoryzacją metamodelu w celu jego uproszczenia. Co by pan zmienił?

Grady: Język UML trzeba uprościć, ale to zawsze jedna z najtrudniejszych rzeczy do zrealizowania. Powodem jest to, że trudno zatrzymać dodawanie elementów w celu obsługi specyficznych problemów. Co więcej, zauważyłem, że język UML rozwija się w kierunku lepszego przystosowania się do roli języka systemowego. Czy to prawda, że większość użytkowników wykorzystuje tylko 2 0 % języka UML?

Grady: W łaśnie tak to wygląda. W książce, którą napisałem , dokładnie opisałem tę klasyczną regułę 80/20. Tylko około 20% języka UML potrzeba do codziennej pracy, a 80% to różne przypadki brzegowe i szczegóły. Rdzeń języka jednak wystarcza do osiągnięcia większości celów. Należy pamiętać, że istnieje wiele sposobów, na jakie ludzie wykorzystują język UML. Gdybym używał go jako inspiracji do tw orzenia kodu, potrzebow ałbym wiele szczegółów. Z drugiej strony, gdybym używał go tylko do wnioskowania na tem at systemu, w celu wizualizacji oraz podejmowania świadomych decyzji projektowych, to nie potrzebow ałbym wszystkich tych detali. Jest to rodzaj tyranii mniejszości. Ująłbym to w taki sposób: pow odem , dla którego UML 2.0 jest nadm iernie rozbudowany i złożony, są niektóre zastosowania tego języka, szczególnie w zakresie definiowania danych. To bardzo komplikuje używanie UML-a w pewnych innych zastosowaniach. Większość zastosowań, które ja dostrzegam, to dyskusje ad hoc przy tablicy. Oto modele moich klas. To sq modele moich encji. A to są relacje pomiędzy nimi.

Grady: Kiedy zaczynałem pracę nad m etodą Booch, nie miałem zamiaru tworzyć na tej podstaw ie języka program ow ania. Jeśli podążym y tym tokiem myślenia i stworzymy wizualny język programowania, okaże się, że istnieje wiele rzeczy, które powinniśmy zrobić, a które nie zostały zrobione. Na przykład zrozumienie semantyki notacji oraz m etam odelu. To nie istnieje. Zaskakuje mnie, że nie są prow adzone na ten tem at żadne prace.

440

ROZDZIAŁ

CZTERNASTY

Jeśli mam ramkę, co to oznacza? Nie istnieje formalna specyfikacja tego, co oznacza ramka. O ile skoncentrow ano się na formalnej semantyce m etam odelu UML, 0 tyle nie zrobiono zbyt wiele w dziedzinie sprzężenia notacji z metamodelem UML. Czy planowane są jakieś zmiany, które umożliwią wykorzystanie rozproszonego wytwarzania oprogramowania i pracy zespołowej?

Grady: Nic takiego nie przychodzi mi na myśl. Robiłem przegląd różnych wzorców projektowych dla systemów rozproszonych i nie potrafię wskazać pojęcia, którego nie byłoby w UML w aktualnej wersji. Jeśli chodzi o pracę zespołową, poświęciłem sporo czasu na poznaw anie problem ów zespołowych środowisk w ytw arzania oprogramowania, w tym na poznanie tak egzotycznych narzędzi, jak system Second Life. UML jest pomocny i nie ma niczego, co naprawdę chciałbym zmienić w samym języku. A to dlatego, że wytwarzanie rozproszone w sensie czasowym i geograficznym to siły społeczne, które nagłaśniają problemy techniczne. Jakie wnioski powinni wyciągnąć programiści z powstania, ewolucji i wykorzystywania języka UML?

Grady: Często słyszę zdanie, że całą historię inżynierii oprogram ow ania m ożna scharakteryzować jako proces podnoszenia poziom u abstrakcji. M ożemy to zaobserwować w naszych narzędziach, m etodach, językach lub frameworkach. W tym kontekście język UML jest po prostu naturalnym punktem tej ewolucji. W pierwszych generacjach kom puterów dom inującą rolę odgrywały platform y sprzętowe. W następnej generacji najważniejszy był wybór języka. W bieżącej generacji przetrwały problemy platformy programowej (głównie deklaracja wojny pomiędzy systemami operacyjnymi, zwłaszcza jeśli spojrzymy na problem szeroko i będziemy postrzegali internet jako platformę wymiany danych). Nie oznacza to, że nie ma śladów tych wcześniejszych problemów. Przeciwnie. W dalszym ciągu odgrywają one rolę we współczesnych systemach w takim lub innym stopniu. Wróćmy do UML-a. Język ten powstał w czasach, kiedy złożoność systemów oprogram ow ania znajdow ała się na takiej trajektorii, że niepowodzenie bądź sukces zależały w mniejszym stopniu od problem ów języka i program ow ania, a w większym stopniu od problem ów architektury i współpracy. Inne zdanie, które pow tarzam , mówi, że wytwarzanie oprogram ow ania było, jest 1 będzie trudne. Istnieje, jak błyskotliwie stwierdził dr Brooks, zasadnicza złożoność tworzenia oprogramowania, która nigdy nie zniknie. Wiadomo zatem, że tak daleko, jak możemy sięgnąć wzrokiem, postęp będzie zależał od oprogramowania, które jeszcze nie zostało napisane (choć będzie ono bazowało na oprogram ow aniu, które już napisano). Systemy bazujące na oprogramowaniu z przeszłości są dziś postrzegane przez nas jako trywialne, ale w swoim czasie były szczytem techniki. Na podobnej zasadzie systemy współczesne, które dziś są dla nas wielkim wyzwaniem, będą wyglądały blado w zestawieniu z systemami z przyszłości. To właśnie te problemy skłaniają nas do usprawniania praktyki i teorii inżynierii oprogramowania.

UML

441

Projekt języka Jakie jest powiązanie pomiędzy projektem języka a projektem oprogramowania napisanego w tym języku?

Grady: Pytanie, które pan zadał, jest stare, chociaż przyjmuje nową postać: lingwiści i specjaliści z dziedziny nauk poznawczych stawiali to pytanie od dziesięcioleci. Powstało na ten temat wiele kontrowersji, które podsumowuje hipoteza Sapira-Whorfa. W podobny sposób Edward Tufte wskazuje, że właściwa prezentacja problemu może wpłynąć na obniżenie jego złożoności. Dzięki temu powstaje możliwość skutecznego wnioskowania na tem at złożonych informacji w sposób abstrakcyjny. Mówiąc dokładniej, hipoteza Sapira-Whorfa (od nazwisk lingwistów Edwarda Sapira i Benjamina W horfa) postuluje istnienie związku pom iędzy używanym językiem a sposobem myślenia. Syntaktyczne i semantyczne elementy języka mówionego mają wpływ na sposób, w jaki człowiek postrzega rzeczywistość oraz wyciąga wnioski na jej temat. Z hipotezą Sapira-Whorfa zgadzają się współcześni lingwiści, na przykład George Lakoff (autor książki Women, Fire, and Dangerous Things [University of Chicago Press]). Praca Tufte’a (The Visual Display o f Quantitative Information [Graphics Press]) koncentruje się na wizualizacji złożonych danych. Na podstawie wielu przykładów autor stawia tezę, że dobra reprezentacja graficzna może przyczynić się do poprawy zrozumienia złożonych informacji. Skąd to wiem? Budowanie abstrakcji jest fundam entalną zasadą obiektowego wytwarzania oprogram owania. Abstrakcja jest w zasadzie problemem klasyfikacji. Na sposób, w jaki myślę o klasyfikacji, wpłynęły poglądy lingwistów, na przykład Chomsky’ego i Lakoffa. W każdym razie — w racam do pańskiego pytania, czy istnieje związek pomiędzy językiem a projektem oprogramowania — przy założeniu, że przez język rozumiemy zarów no klasyczny tekstow y język program ow ania, taki jak Java, jak i graficzny, taki jak UML, odpowiedziałbym: „Prawdopodobnie” . Języki zachęcające do algorytmicznej dekompozycji (jak FORTRAN lub C) prowadzą do określonych stylów organizacji programu. Style te są znacząco różne od tych, jakie stosuje się w językach zachęcających do obiektowej dekompozycji (na przykład Javie) czy też językach funkcyjnych. Z moich doświadczeń wynika jednak, że istnieje wiele innych czynników, które m ają istotny wpływ na projekt: środowisko zespołu projektowego, kontekst historyczny, określone siły, które wpływają na system w określonym momencie. Można by powiedzieć, że język stanowi rodzaj Przyczyny Pierwotnej, ale ja m am inne zdanie na ten tem at. Uważam, że dom inującą rolę odgrywają inne czynniki.

442

ROZDZIAŁ

CZTERNASTY

Jaka jest różnica pomiędzy tworzeniem języka programowania a pracą nad zwykłym projektem programowym?

Grady: Istnieje taka sama różnica, jak pomiędzy tw orzeniem projektu ustawy a jej publikowaniem: są to zagadnienia ze sobą związane, ale bardzo różne. Język — niezależnie od tego, czy jest to język naturalny, czy język programowania — nie ma nieograniczonej liczby poziomów swobody. Kształtują go czynniki techniczne, biznesowe, społeczne, historyczne i pragmatyczne. Zwłaszcza w przypadku języków programowania należy dokładnie określić ich składnię i semantykę, ponieważ języków tych używa się do tworzenia działających programów. Nie wiem, czy istnieje coś takiego, jak wspólny projekt oprogramowania. W każdym projekcie istnieje wiele niejednoznaczności. Język, kiedy się go raz zdefiniuje, jest stosunkowo stabilny. Projekt oprogramowania, jeśli w ogóle ktokolwiek się nim zainteresuje, żyje w środowisku, które znajduje się w ciągłym ruchu. A zatem, o ile zarów no języki, jak i projekty oprogramowania to w zasadzie problemy inżynierskie uwzględniające przy rozwiązaniu pewne czynniki skupione w okół nich, to czynniki występujące w projekcie oprogram ow ania są o wiele bardziej zróżnicowane i o wiele bardziej dynamiczne. Czy uważa pan, że należy wyjść od stworzenia specyfikacji formalnej rdzenia języka, a następnie go rozwijać?

Grady: Tak uważam. W łaśnie w taki sposób postąpiliśmy razem z Jimem, kiedy przystąpiliśmy do opracowania semantyki tego, czym będzie UML, oraz zunifikowania m etod OMT i Booch. Rozpoczęliśmy od napisania metamodelu z wykorzystaniem samego UML. To, co dla jednej osoby jest formalne, dla innej jest nieform alne. Dla nas zasadniczym problemem było, czy stworzyliśmy dostateczny opis formalny do tego, co mieliśmy zamiar zrealizować. Odpowiedź brzmiała: tak. W ja k i sposób rozpoznał pan, że był to wystarczający poziom formalizmu?

Grady: Słyszałem kiedyś pewną historię. Zapytano ministra sprawiedliwości, co to jest pornografia. Odpowiedział: „Będę wiedział, kiedy ją zobaczę” . A zatem tak to właśnie działa. Kiedy zaczynamy mówić na temat znaczenia treści, wdajemy się w niezwykle metafizyczną dyskusję. Trudno bowiem powiedzieć, co to jest formalizm. Można zadać inne, bardzo trudne pytanie: „Czym jest znaczenie?” . Nawet w przypadku opisu formalnego istnieje punkt, w którym trzeba się zatrzymać. Wiele osób może powiedzieć: „Możemy polegać na zupełności Turinga lub rachunku Lambda, a poza tym wiemy, w ja k i sposób stosować funkcję do argumentu".

Grady: Bardzo się cieszę, że są ludzie, którzy tym się przejmują. W yobraźmy sobie jednak, co by się stało, gdybyśmy zastosowali ten sam rygor do Javy lub systemu Vista. Zastanówmy się, ile jest pisanego w tym języku lub na tę platformę kodu, dla którego nie stw orzono żadnych definicji form alnych. To jest sem antyka operacyjna. U rucham iam y coś i wiemy, w jaki sposób działa. To w dużej części wystarczy

UML

443

w odniesieniu do programów, jakie współcześnie tworzymy. Nie oznacza to jednak, że nie ma miejsca na definicje formalne. Jest! Jest kilka takich miejsc — powiem to bardzo ostrożnie — kilka bardzo wąskich zaułków branży, dla których głębokie formalizmy są bardzo istotne. Czy warto rozgraniczać język, w którym można budować systemy, od takiego, dla którego jest potrzebny pewien stopień formalizmu?

Grady: Oczywiście. Ale czy istnieje formalna semantyka dla Linuksa? Czy istnieje formalna semantyka dla C++? Oczywiście mogę sobie wyobrazić, że ktoś opracowuje pewne form alne elementy definicji tych systemów, ale to nie wstrzymuje nikogo przed budowaniem praktycznych systemów. A zatem ma to wydźwięk bardzo praktyczny?

Grady: Tak. Ja jestem pragmatykiem. Jeśli coś działa i jest dobre, używam tego. Właśnie dlatego wykorzystuję około 20% UML-a. To zupełnie wystarcza dla moich celów. Pańskie 2 0 % może oznaczać coś innego niż moje 20% .

Grady: W yobrażam sobie, że istnieje pewna część wspólna. Być może pańskie 19% i moje 19% stanowi to samo. Mówiąc szczerze, to dość duża zgodność.

Grady: Tak. To prawda. Gdzie zacząłby pan szukać przypadków użycia dla tych 19%?

Grady: Zacząłbym przyglądać się sposobom , w jakie ludzie używają UML-a. Przeanalizowałbym praktyczne projekty, w których próbow ano stosować UML, i zadałbym ich twórcom pytanie: „Proszę podać standardowe przypadki. Podajcie mi najważniejsze sytuacje, w których skorzystaliście z UML-a. Sprawdźmy, czy wykonywanie prostych rzeczy w tym języku jest łatwe. Następnie przyjrzymy się niektórym przypadkom brzegowym”. Analizowałbym rzeczywiste wykorzystanie, w przeciwieństwie do oczekiwanego. Jest to czas na konsolidację i uproszczenia. Dla języka, który ma już trzecią wersję i którego używa się w tak wielu miejscach, jest to bardzo ważne. Jeżeli wprowadzanie innowacji ma swoją wartość, ma ją także refaktoryzacja. UML 2.0 do pewnego stopnia — powiem to dość mocno — cierpi na efekt drugiego systemu w tym sensie, że istniały doskonałe okazje i specjalne grupy zainteresowań, które żądały pewnych własności. To przyczyniło się do nadmiernego rozbudowania języka UML 2.0. Teraz nadszedł czas na to, by zrobić krok wstecz, zrefaktoryzować kod i go uprościć. W każdym pow ażnym systemie m ożna zaobserwować wzloty i upadki. Teraz nadszedł czas na uproszczenia.

44 4

ROZDZIAŁ

CZTERNASTY

Czy można zastosować tu wzorzec nieparzysty, parzysty, nieparzysty, parzysty?

Grady: Głośno myślę, jak to jest z wydaniami systemów operacyjnych Microsoft... Kiedy popatrzę na system Windows 7, to odpowiem, że tak — być może coś w tym jest. Albo efekt filmu Star Trek.

Grady: Ten efekt jest wart wielu formalnych badań. Do jakiego stopnia w języku UML 2 .0 ma znaczenie zgodność wstecz?

Grady: Należy pamiętać, że w UML 2.0 jest wiele rzeczy, które w przypadku ciągłego rozw ijania i dbania o zachowanie wstecznej zgodności z poprzednim i wersjami spowodow ałyby olbrzymi rozrost języka. Praw dopodobnie znalazłyby się pewne sytuacje, w których zdecydowałbym się na naruszenie wstecznej zgodności i stwierdziłbym: „Ta własność dodaje m nóstw o złożoności. Po prostu nie warto się męczyć. Przykro mi, ale będę zmuszony zawieść kilka osób. Oto możliwe obejścia” . Jeśli chodzi o zasadnicze elementy języka UML — o te 20%, których wszyscy używają — zadbałbym, aby nie naruszać zgodności wstecz dla tej części. Zapewnienie wstecznej zgodności dla tej części języka jest oczywiście bardzo ważne. Tak próbują robić niemal wszyscy twórcy języków programowania. Istnieje mnóstwo różnych podejść. Twórcy Lispa mówią: „N ie mamy zamiaru umieszczać w języku systemu obiektowego; można go sobie stworzyć. Nie umieścimy w języku pewnych struktur sterujących. Je także można stworzyć". W takiej sytuacji użytkownicy zrobili tak, ja k sugerowano, a następnie zaczęli współdzielić kod. Wtedy powstał projekt Common Lisp, w którym zaczęto wskazywać te elementy, których wszyscy powinni używać.

Grady: To dobry model opisujący tę sytuację. Do pewnego stopnia jest to podejście ewolucyjne, wykorzystujące algorytm wyżarzania. Społeczność wyznacza to, co jest dobre, a projektanci umieszczają to w rdzeniu języka.

Grady: Dokładnie tak. Dlatego właśnie podkreślałem wykorzystanie przypadków użycia z samej branży. W jaki sposób używa się określonej własności? Spójrzmy na to od strony korporacji, w której występują naprawdę bardzo rozbudowane systemy. Następnie przyjrzyjmy się także kilku mały systemom. Jaki jest pański pogląd na proces standaryzacji po tym, ja k przez niego przeszliście?

Grady: Byłem zaangażowany w różne procesy standaryzacji. To w spaniały i interesujący proces, który z chęcią przeanalizuje wielu socjologów. Trudno uogólniać go w kategoriach typowego procesu. Istnieją pewne standardy (nie m am zamiaru wymieniać żadnego z nazwy), które były popierane przez określone branże. Stały się one de facto standardam i w pew nych firmach. Istnieją też inne, które stanow ią

UML

445

prawdziwy wysiłek grupowy. Są też takie, które powstają z motywów politycznych — skakania sobie do gardeł przez różne podm ioty. A zatem istnieje cała paleta możliwości powstawania standardów. Naprawdę interesujące w odniesieniu do standardów jest to, że pomimo wszystkich narzekań na systemy standardowe mają one swoją wartość i się sprawdzają. W związku z tym jestem wdzięczny takim organizacjom, jak OMG, które są gotowe poświęcać swoje zasoby w celu promowania standardów oraz tworzą warunki do ich rozwoju. Bez standardów nie mógłby istnieć na przykład internet. To bardzo trudny proces, a wielu ludzi, którzy są w niego zaangażowani, to pasjonaci. Mają swój określony sposób widzenia świata. Wiedzą, że ten pogląd na świat jest właściwy. Taka jest właśnie natura ludzkiego doświadczenia. Czy odnosi pan wrażenie, że ze standardami je s t tak, że każdy jes t z nich trochę niezadowolony, ale ponieważ wszyscy inni też są trochę niezadowoleni, to standard się sprawdza?

Grady: Nie jestem pewien, czy jest aż tak źle, ale standardy wymuszają pewien poziom kompromisu. Każdy standard, jaki możemy sobie wyobrazić, jest takim przypadkiem. Zawsze będzie ktoś, kto odejdzie niezadowolony. W Ameryce w dalszym ciągu są ludzie niezadowoleni z wyboru Obamy na prezydenta i cieszą się, że Bush jest w pobliżu. Ale przyjrzyjmy się faktom. Są osoby, które lubią muzykę Britney Spears — na temat gustów się nie dyskutuje. To część ludzkiego życia — trzeba zaakceptować różnice występujące pomiędzy ludźmi. W jądrze systemu Linux poza POSIX-em nie ma prawdziwych standardów. Taki je s t gust Linusa Torvaldsa i jego poruczników.

Grady: Myślę, że to dobre porównanie. Jeśli chodzi o Linuksa, Linus od pewnego czasu jest liderem. W przypadku języka UML Jim Rumbaugh i ja odsunęliśmy się od procesu standaryzacyjnego i pozwoliliśmy, aby język powstał. Nie było siły kierującej. Myślę, że to wielka różnica. Spójrzmy na C++ — Bjarne w dalszym ciągu stoi na czele ewolucji języka C++. Jego silna ręka gwarantuje intelektualną integralność i spójność. W przypadku języka C+ + proces standaryzacyjny jest realizowany także w odniesieniu do nowej wersji.

Grady: Zgadza się. Jednak tak jak w przypadku Linuksa istnieje głos kogoś o olbrzymim doświadczeniu. Są też duże oczekiwania wobec tej osoby i to się sprawdza. Nie jest to jedyny głos, ale przynajmniej głos zdecydowany i czytelny.

446

ROZDZIAŁ

CZTERNASTY

Być może to powinny być dwa pytania. Jaką wartość ma dążenie do standaryzacji jakiegoś pomysłu? Do jakiego stopnia potrzebny je s t silny lider z silną wizją, który pomaga osiągnąć sukces?

Grady: Jeśli chodzi o tę drugą kwestię, to takie pytanie można zadać w odniesieniu do każdego ludzkiego przedsięwzięcia. Przyjrzyjmy się, w jaki sposób w świecie zachodzą zmiany. Spójrzmy, co zrobił Gandhi lub M artin Luther King (oczywiście nie porównuję naszych dokonań do działalności żadnej z tych osób). Siła jednostki w promowaniu zmian w świecie jest niezwykła. W świecie techniki można wskazać szereg liderów, którzy powodowali, że następował postęp. Spójrzmy na Larry’ego i Serge’a z firmy Google, którzy zaczęli realizować swoje pom ysły ze Stanford. Obecnie Google to prawdziwe imperium. Istnienie silnego w izjonera udowodniło swoją przydatność w bardzo wielu dziedzinach. Czy nie chodzi o rywalizację poglądów?

Grady: To po prostu przykład tego, że w wielu dziedzinach w promowaniu zmian wielkie znaczenie miała siła jednostki lub niewielkiej grupy ludzi. Przypuśćmy, że chciałbym zaprojektować język programowania. Jaką wartość mogę zyskać, jeśli podążę za standaryzacją?

Grady: Kiedy tw orzy się coś nowego, to rynek ostatecznie decyduje, czy z tego pow stanie standard. Wiele języków skryptowych nie pow stało w w yniku pracy komitetów standaryzacyjnych, ale po prostu wyrosły one z pracy ludzi. W pewnym m om encie osiągnęły masę krytyczną i ludzie zdali sobie sprawę z tego, że muszą dokładniej ustandaryzować ten język, ponieważ istnieje potrzeba interoperacyjności. Chodzi o to, że w przypadku tworzenia nowego produktu należy rozważyć, jaka jest wartość tego, co dostarczamy, oraz w jaki sposób zareaguje na to rynek. Proces standaryzacyjny pomaga w osiągnięciu pewnej masy krytycznej, ale ostatecznie to rynek jest decydentem. Problem z nowym językiem jest taki, że ktoś może opracować doskonały techniczny projekt, ale oprócz tego istnieją tysiące innych czynników, głównie społecznych, które mają wpływ na to, czy język osiągnie sukces. Czy można stworzyć społeczność wokół pewnej praktyki? Czy język rozwiązuje znane problemy, które są żywe i realne w danym momencie dla konkretnej społeczności? Czy firmy są wystarczająco zainteresowane finansowaniem projektu, gdy znajduje się on w fazie rozwoju? Jeśli na przykład nie ma dla niego modelu biznesowego, ale wydaje się, że jest to dobry produkt. Wiele wczesnych projektów zaczyna się od wiary oraz tego, czy znajdujem y się w odpowiednim miejscu i czasie. Z całą pewnością tak właśnie było w przypadku m etody Booch i praw dopodobnie, gdyby spytał pan o to samo Jima Rum baugha, tak było w przypadku projektów Objectory i OMT, z których UML czerpał inspiracje.

UML

447

Byliśmy we właściwym miejscu i w odpowiednim czasie oraz rozwiązywaliśmy problem, który był zmartwieniem rynku. Czy dzisiaj świat potrzebuje nowego języka? Można by spytać, co z językiem M, który firma Microsoft wprowadziła na rynek? Czy język M odniesie sukces? Z technicznego punktu widzenia jest on bardzo interesujący, ale czy odniesie sukces, czy nie, zależy od tego, czy uda m u się zdobyć pozycję na rynku. M imo że być może pow stanie standard, mimo że może on stać się standardem de facto z uwagi na dokonania firmy Microsoft, ostatecznie to rynek zdecyduje. Brzmi to tak, jakb y istniało wiele pragmatyzmu oprócz wizjonerstwa.

Grady: O, z całą pewnością! Wiem, że nawet najlepszy pomysł, najlepiej sformułowany pod względem technicznym, najbardziej przemyślany i kompletnie udokumentowany nie odniesie sukcesu, jeśli nie rozwiązuje realnych problem ów i nie dotyczy rzeczywistych projektów. Ludzie czasami nie chcą zdać sobie z tego sprawy. Istnieje grupa technicznych utopistów, którzy wierzą, że najlepszy projekt z punktu widzenia technicznego musi zwyciężyć.

Grady: Cieszę się, że istnieją tacy ludzie i podziwiam ich optymizm. Dzięki takiemu nastawieniu możliwy jest postęp. Ja jednak jestem w większym stopniu inżynierem rozwiązującym realne problemy i promującym pragmatyzm. Nie jestem komputerowym naukowcem. Jestem inżynierem. To fascynujące, że mamy obie postawy, ponieważ ten taniec, to napięcie, sprawia, że obie strony stają się bardziej uczciwe. Moją inspiracją jest pragmatyzm, ale z drugiej strony klasyczny komputerowy naukowiec naciska na mnie, bym w większym stopniu stosował m etody formalne — nie ma w tym niczego złego. Na podobnej zasadzie ja naciskam jego, by był bardziej praktyczny. Czy istnieje jakieś kreatywne napięcie pomiędzy tymi dwoma biegunami?

Grady: Oczywiście. Myślę, że powinno ono istnieć. W pewnym stopniu kreatywność wynika z istnienia napięcia, ponieważ pozwala skoncentrować się na rozwiązywaniu realnych problemów. W internecie istnieje doskonały serwis Gaping Void. Osoba, która go prowadzi, tw orzy rysunki na odwrocie wizytówek. Serwis ten pokazuje, w jaki sposób być kreatywnym. Warto polecić ten serwis czytelnikom2. Są tam bardzo interesujące informacje pokazujące, jak bardzo istotny jest ten rodzaj pragmatycznego napięcia.

2

448

RO ZDZIAŁ

www.gapingvoid.com.

CZTERNASTY

Szkolenie programistów Dlaczego tak powoli usprawniamy metody i proces programowania?

Grady: Cóż, nie zgadzam się z tym, co pan sugeruje w pytaniu. Być może proces ten sprawia wrażenie powolnego, ponieważ gdy patrzymy od wewnątrz, wiemy, że możemy zrobić znacznie więcej oraz zrobić to lepiej. Weźmy jednak pod uwagę, że nasza branża dosłownie przewróciła świat do góry nogami. Stało się to w ciągu jednego pokolenia. Dla mnie to jest szybko, a nie wolno. W ja k i sposób można dzielić się doświadczeniami w branży oprogramowania?

Grady: W średniowieczu to cechy rzemieślnicze przekazywały wiedzę na tem at rzemiosła. Dzisiaj brakuje takich organizacji w branży oprogramowania. Pomimo to liczne źródła doświadczeń można znaleźć w internecie (weźmy na przykład serwis Slashdot), w książkach, na blogach i konferencjach technicznych. Jest również tak, że surowy, działający, nagi kod źródłowy jest źródłem wiedzy z przeszłości — to jest jeden z powodów, dla których pracowałem w Computer History Museum — aby zachować kod klasycznego oprogramowania dla przyszłych pokoleń. Jakie zagadnienia studenci powinni dziś zgłębiać bardziej intensywnie?

Grady: Odpowiem na to pytanie na dwa sposoby. Jeśli rzecz dotyczy oprogramowania, dow olny dobry kurs nauczy podstaw ow ych um iejętności program ow ania i projektow ania. Ja jednak polecałbym trzy rzeczy: uczenie się tworzenia abstrakcji, uczenie się pracy w zespole oraz analizowanie kodu napisanego przez innych. Z szerszej perspektywy polecałbym studentom, aby uparcie podążali za swoją pasją, ale by nigdy nie zapomnieli o rozwoju swojej osobowości. W ażne jest studiow anie innych dziedzin (na świecie jest coś więcej niż tylko oprogram ow ania), rozwijanie umiejętności ciągłego uczenia się (ponieważ branża oprogramowania ciągle się zmienia) oraz iskra ciekawości i chęci do podejmowania ryzyka (ponieważ te cechy są źródłem innowacji). Jakiej rady wynikającej z pańskiego doświadczenia udzieliłby pan początkującemu programiście?

Grady: Takie pytanie zadał mi niedaw no student Uniwersytetu Stanu Kalifornia. Kilkanaście tygodni tem u m iałem cykl wykładów na Politechnice Kalifornijskiej (Cal-Poly) oraz Uniwersytecie Stanu Kalifornia (USC). Kilku studentów zadało mi w tedy pytania, pozwolę sobie teraz udzielić takiej samej odpowiedzi, jakiej wtedy im udzieliłem. Po pierwsze, należy podążać za własną pasją i robić to, co sprawia przyjemność. Oczywiście cenne jest robienie kariery i zarabianie pieniędzy, ale ostatecznie wszystko, co robimy, jest ludzkim doświadczeniem, a człowiek chce być w pełni człowiekiem.

UML

449

A zatem należy dążyć do tego, by życie było przyjemne. Właśnie w ten sposób trzeba zdobywać doświadczenia życiowe. Należy koniecznie podążać za pasją. Bardzo łatwo jest znaleźć złe zajęcie, kiedy nie można się odnaleźć w pracy, której po prostu się nie znosi. Nie należy podejmować takiej pracy. Oto do czego zachęcam. Druga rzecz, do której namawiam, to dążenie do zdobycia pewnego doświadczenia. W arto zaangażować się w jakiś projekt open source. W arto znaleźć coś, co nas interesuje, i po prostu się tym zająć. Nie należy obawiać się próbow ania now ych rzeczy i poznaw ania różnych tematów, jeśli chodzi o nowe zagadnienia z różnych dziedzin. Szczerze mówiąc, to zawsze się przyda, niezależnie od dziedziny. Wiele osób powtarza, że zdolności muzyczne są pomocne. Podobnie mówi się o innych dziedzinach twórczych — zwłaszcza o pisaniu.

Grady: Jeśli chodzi o pisanie, to często zadaję studentom takie pytanie: „Ilu z was czytało kurs programowania?” . Tylko dwie osoby odpowiedziały twierdząco. Studenci literatury angielskiej czytają dzieła mistrzów. Jeśli ktoś chce być architektem, czyta prace Vitruviusa, Franka Lloyda Wrighta, Christophera Rennina, Franka Gehry’ego i innych. W dziedzinie oprogram ow ania tego nie robimy. Nie oglądam y pracy mistrzów. Zachęcam wszystkich do oglądania pracy innych i uczenia się od nich. Byłoby doskonale, gdyby istniał w literaturze wzorzec, który można by podpisać „Tak wygląda doskonały program w Pascalu".

Grady: Taką właśnie próbę podjęli Andy Oram i Greg W ilson w książce Beautiful Code wydanej przez O’Reilly. Jest również książka autora z Nowej Zelandii, który dodatkowo stworzył listę literatury do przeczytania. To jedna z dwóch spotkanych przeze mnie osób, które zrobiły coś podobnego. Nie m am y odpowiedniej bazy wiedzy potrzebnej do konstruktyw nej krytyki oprogram ow ania. Praca K nutha dotycząca sztuki program ow ania była jedną z pierwszych prób tego rodzaju. Większość kodu jest pisana bardzo źle. To tak jak zdania ucznia trzeciej klasy, który dopiero uczy się gramatyki. N atom iast piękny kod jest pełen dramatyzmu, piękna i elegancji i jest dobrze napisany. Miałem okazję oglądać kod źródłowy programu MacPaint — około 10 000 wierszy kodu w języku Object Pascal. Ten kod był napisany pięknie. Nie m am y przykładów tego rodzaju — takich, które moglibyśmy pokazywać światu. Problem polega na tym, że jeśli ktoś ma do analizy system, który tak jak jądro Linuksa ma 10 milionów wierszy kodu, to nie będzie go czytał. To tak jakby w kółko czytać Wojnę i Pokój. Co należy zrobić, aby pokazać piękno i elegancję tego kodu? W styczniu 2010 roku będzie konferencja Rebooting Com puting prow adzona przez Petera Denninga i Alana Kaya. Weźmie w niej udział wiele osób — ja też tam będę. Będziemy na niej omawiać jeden z istotnych problemów. W jaki sposób można zademonstrować światu niewidoczne piękno intelektualnie złożonych systemów?

450

ROZDZIAŁ

CZTERNASTY

Podobny problem m am y w Com puter History M useum. Jestem w komitecie zarządzającym tym muzeum. Powołaliśmy komisję do spraw kolekcji oprogramowania {Software Collections Committee). Komisja ta zajmuje się zbieraniem doskonałych znalezisk. W jaki sposób zaprezentować piękno systemu Vista? Jest w nim piękno. W jaki sposób skłonić ludzi, by je dostrzegli, by przeczytali to, co jest w jego wnętrzu? W przypadku architektury można pokazać budynek. Można pokazać obraz. Można posłuchać fragmentu muzyki. W jaki sposób zaprezentować muzykę, która drzemie w oprogramowaniu? Być może powinniśmy udokumentować algorytmy i struktury danych.

Grady: Myślę, że to zbyt niski poziom. Moim zdaniem należy dokumentować wzorce, które są rozproszone po całym systemie.

Kreatywność, udoskonalanie i wzorce W ja k i sposób należy podejść do problemu oprogramowania odziedziczonego?

Grady: Często powtarzam zdanie, że o ile kod przedstawia prawdę, o tyle nie pokazuje całej prawdy: pomiędzy wizją a realizacją są tracone informacje. Z moich doświadczeń wynika, że ze starym oprogram ow aniem m ożna zrobić dziewięć rzeczy: porzucić, oddać, zignorować, poddać operacji podtrzymującej życie, przepisać, zbierać plony, opakować, przekształcić lub zachować. W każdej z tych czynności są elementy zarówno techniczne, jak i socjalne. Z technicznego punktu widzenia istnieją interesujące badania nad zbieraniem plonów w postaci wzorców kodu. Ze społecznego punktu widzenia techniki historii mówionej mogą wspomagać rozwiązywanie problemów. Gdzie pan szuka inspiracji do projektu?

Grady: Znajduję inspirację w elegancji złożonych systemów. Może to być oprogram ow anie (jednym z celów podręcznika jest kodyfikacja wzorców architekturalnych oraz objaśnienie ich piękna). Inne systemy to na przykład systemy organiczne (może to być dowolny system organiczny: w systemie, który ewoluował przez m iliony lat, z pewnością są pewne rzeczy, których w arto się uczyć), sztuka (istnieje wiele piękna w dziełach artystów wielu dziedzin sztuki); muzyka, produkty inżynieryjne, fizyka kwantow a... na tej liście może znaleźć się jeszcze wiele innych pozycji. Do jakiego stopnia pana zdaniem kreatywność w programowaniu jest konieczna?

Grady: Wiele zadań programowania nie wymaga zbyt wielkiej kreatywności, ponieważ dotyczy rozwiązywania znanych problem ów w interesujący sposób. To tak jakby ktoś rozbudowywał mój dom. Są pewne ograniczenia, które należy uwzględnić, oraz pewne dobre praktyki, które należy stosować. Nie powinienem zatrudniać kogoś, kto jest całkowitym nowicjuszem, a raczej kogoś, kto potrafi stosować te dobre praktyki.

UML

451

On lub ona będą musieli w prowadzać innow acje w trakcie realizacji projektu. Coś w guście: „Oj, to nie pasuje najlepiej. Potrzebuję pewnych nowatorskich sposobów wykonania czynności X”. Tak właśnie wprowadza innowacje indywidualny deweloper. Wiele rzeczy, które robimy podczas tworzenia oprogram owania, nie wymaga zbyt wielkiego nowatorstwa. Znamy technologię, zgodnie z którą budujemy. Powinniśmy umieć ją stosować w now atorski sposób. Powinniśmy ją kształtować, wygładzać krawędzie, dodawać nowe miejsca tu i tam. Postępowanie w taki sposób to część przyjemności z rozwiązywania tej łamigłówki. Nie ma co do tego wątpliwości. Oczywiście istnieją miejsca, w których konieczne są nieokiełznane innowacje. Nie znaliśmy właściwego sposobu budow ania oprogram ow ania dla dużych, skalowalnych systemów wyszukiwania. Pojawili się Serge i Larry, stworzyli prototyp i tak narodził się serwis Google. Nie znam y właściwych sposobów patrzenia na wszystkie źródła danych z dziesiątek tysięcy, jeśli nie m ilionów , kamer wideo umieszczonych we wszystkich zaułkach świata — w Londynie, N owym Jorku i Pekinie. Jaka jest prawidłowa architektura systemu realizującego takie działania? Nie mamy dobrych modeli, dlatego potrzebne jest nowatorstwo, nieokiełznane nowatorstwo. Kiedy jednak zaczniemy skupiać się na właściwym rodzaju architektury, będzie to oznaczało, że problem jest ograniczony i odtąd będziemy w prow adzać tylko niewielkie innowacje. Znów użył pan słów „innowacje" i „ograniczenia". Wygląda to na bezpośrednie nawiązanie do ewolucji języka.

Grady: Oczywiście. Artysta malarz, pisarz lub ktokolwiek, kto upraw ia tw órczą dziedzinę, najbardziej nie lubi całkowicie pustej kartki papieru, ponieważ wtedy nie ma żadnych ograniczeń. Nie ma niczego, co m ogłoby dać wskazówkę. M om ent, w którym zaczynamy wprowadzać ograniczenia, jest bardzo ciekawy. Twórca jest wolny, ponieważ od tego momentu może zacząć pracować w obrębie tych ograniczeń i stosować wszystkie nowatorskie umiejętności, które spełniają te ograniczenia. Gdybym był muzykiem, to m ógłbym powiedzieć: „Cudownie, wreszcie mogę coś zagrać”. Brak ograniczeń można porównać do sytuacji, w której muzyk mówi: „O rany! Czy powinienem grać na pianinie, czy na jakimś innym instrumencie? Co w ogóle powinienem robić?” . To jest problem bogactwa, który nie pomaga w koncentracji. Mógłbym powiedzieć: „Mogę zbudować własne pianino” . Albo zrobić tak, jak zrobił Don Knuth: „Piszę książkę i nie podoba mi się sposób jej składania. Zatrzymam się na kilka lat i napiszę język do składania książek” . Nie potępiam tego, co zrobił Don, ponieważ stworzył wspaniałe rzeczy, ale w przypadku braku ograniczeń naprawdę trudno o skupienie uwagi.

452

ROZDZIAŁ

CZTERNASTY

Czy sugeruje pan, że przystępując do budowania nowego systemu, najpierw identyfikujemy ograniczenia, a następnie je stosujemy?

Grady: Nie uważam, że zawsze trzeba to zrobić najpierw. Żyjemy w świecie zawierającym ograniczenia i możemy wybrać niektóre z nich. Każda decyzja, którą później będziemy podejmować, będzie musiała być zgodna z wcześniejszym wyborem — nie m a w tym nic złego. Bardzo wiele wczesnych decyzji, które podejm ujem y w projektach, ma na celu ustanowienie pewnych ograniczeń. Często ograniczenia te są przyjmowane na wiarę. Nie można zawczasu stwierdzić, jakie są te wszystkie ograniczenia. Trzeba podjąć próbę zrobienia czegoś, rzucenia na głęboką wodę, a następnie postępowania zgodnie z tym. Na przykład w bardzo prostym przypadku mógłbym powiedzieć: „Słuchajcie, chcę opracować nowy, graficzny język programowania” . W związku z tym przystąpiłbym do pracy. Nagle podjąłbym pewne decyzje, na przykład: „Być może będzie to język dwuwymiarowy, a nie trójwymiarowy” . Być może podjąłem decyzję, że kolory będą miały dla mnie znaczenie i nagle zdam sobie sprawę: „Ojej! W łaśnie wyłączyłem całą społeczność program istów nierozróżniających kolorów ” . Każda z tych decyzji staje się ograniczeniem, które muszę uwzględnić. Muszę poradzić sobie z konsekwencjami tych ograniczeń. Wygląda to na proces iteracyjny.

Grady: Dokładnie tak. Wszystko, co robimy w życiu, to procesy iteracyjne Przywodzi mi to na myśl pewną uwagę, którą przekazałem wcześniej. Zawczasu nie wiemy nawet tyle, aby zadawać poprawne pytania. Trzeba przyjąć coś na wiarę i działać w warunkach niedoskonałych informacji. Czy istnieje prawdopodobieństwo, że w ciągu najbliższych lO la t powstanie przełomowy wizualny język programowania?

Grady: Ależ on już istnieje. To język Lab View firmy N ational Instrum ents. To absolutnie najdoskonalszy wizualny język program ow ania, jaki kiedykolwiek spotkałem. Nie wiem, czy pan go zna. Jest absolutnie doskonały. Rysuje się prostokąty i linie, które reprezentują instrumenty i wirtualne urządzenia elektroniczne. Jeśli ktoś chce coś dopisać, może to zrobić przy użyciu kodu C lub C++. Ogólnie rzecz biorąc, można budować wirtualne instrumenty i łączyć je z rzeczywistymi. To jest naprawdę bardzo fajne. Taki język już istnieje. Jakiego typu instrumenty można definiować w tym języku?

Grady: Oscyloskopy, urządzenia m onitorujące. W yobraźmy sobie kom puter PC, do którego portu USB są podłączone pewne konw ertery analogowo-cyfrowe, konwertery transmisji równoległej na szeregową oraz tego typu urządzenia. Dzięki tem u mogę kom unikow ać się z fizycznymi urządzeniam i. Mogę teraz zbudować wirtualne urządzenia na moim PC — wystarczy je narysować. Mogę stworzyć rejestrator

UML

453

wykresu pasmowego, oscyloskop albo przetworzyć dane i zaprezentow ać je w interesujący sposób na różnych miernikach. W języku Lab View to bardzo proste. To świetne narzędzie. Charles Simonyi próbuje wykorzystywać inne narzędzie w prowadzonej przez siebie firmie Intentional. Charles tworzy system dla inżynierów elektryków, pozwalający na w prow adzanie schem atów elektrycznych do kom putera. Nie spotkałem się z w ielom a innym i językami graficznymi, ale te, które podałem , to klasyczne przykłady. Najlepsza wizualizacja jest związana z konkretną dziedziną. A co ze standardowymi aplikacjami biznesowymi? M am y jakiś silnik bazy danych. Są obiekty biznesowe. Jest jakaś warstwa prezentacji.

Grady: Większość systemów korporacyjnych pod względem architektury jest bardzo nudna, ponieważ są one pod tym względem stosunkowo proste. Istnieje zbiór decyzji projektowych, które trzeba podjąć, i wiemy, jakie są to decyzje. Trudności powstają dlatego, że istnieje wybór wspaniałych technologii, w związku z tym występuje problem bogactwa. Istnieje wiele sposobów budowania aplikacji. Zastanówmy się, co by było, gdybym dziś stwierdził: „H ej! Mam zamiar zbudować od podstaw system bankow y” . Prawdopodobnie istnieje zbiór standardów , które muszę uwzględnić, ale mogę wybrać spośród, co praw da skończonego, ale bardzo obszernego zbioru sposobów rozw iązania tego problem u. To właśnie jest moim zdaniem źródło problemów i chaosu, który występuje. Weźmy pod uwagę najbardziej ulotną część współczesnych aplikacji biznesowych. Jest nią warstwa prezentacji. W jaki sposób zaprezentować informacje ludziom? Przez około dekadę ludzie byli zadowoleni z informacji wyświetlanych na ekranie, teraz jednak istnieje szereg różnych sposobów prezentowania za pośrednictwem internetu i urządzeń mobilnych. Można zauważyć wiele now atorskich rozwiązań, ponieważ — jak dotychczas — nie ma zgodności co do właściwych modeli, poprzez które ludzie chcą współpracować z tymi systemami. Stąd bierze się ta różnorodność. Na drugiej pozycji listy najbardziej ulotnych elementów aplikacji biznesowych są reguły biznesu. W przeszłości, ze względu na to, że byliśmy ograniczeni przez możliwości naszych maszyn, reguły biznesowe musiały być przechowywane w różnych dziwnych miejscach, w postaci składowanych procedur ładowanych do przeglądarki. Wszystkie jednak były na miejscu. Zdajemy sobie sprawę, że tkwi w tym pewien problem , ponieważ z tego powodu trudno nam będzie reagować na zmiany, kiedy ulotne reguły biznesowe bardzo szybko się zmienią. Zmiana reguł biznesowych spowodowała konieczność refaktoryzacji wielu systemów korporacyjnych. Z najdujem y różne sposoby, dzięki którym system korporacyjny analizuje repozytorium reguł biznesowych i na nie reaguje. Ale nie mogliśmy tego wiedzieć, kiedy budowaliśmy te systemy po raz pierwszy. Nie mogliśmy wiedzieć, gdzie znajdują się najbardziej ulotne miejsca. Jest to jedna ze zmian, które obecnie zachodzą w tym obszarze.

454

ROZDZIAŁ

CZTERNASTY

Czy jest to system otwarty na wizualizację? Można się założyć! Obserwujemy próby znalezienia nowych języków do wyrażania reguł biznesowych. Moim zdaniem UML zupełnie do tego wystarczał. Pomimo to m iały miejsce pewne zdarzenia natury politycznej, które doprowadziły do powstania BPEL (ang. Business Process Execution Language), języka zupełnie odrębnego od UML. Być może język M firmy Microsoft także jest taką próbą?

Grady: Na pewno tak. M am jednak pewne wątpliwości, które zasygnalizowałem wcześniej: czy język M odniesie sukces? Rynek udzieli tej odpowiedzi. Ludzie mogą nawet nie zdawać sobie sprawy z tego, że potrzebują osobnej warstwy dla reguł biznesu.

Grady: Absolutnie. Jest świetny artykuł napisany przez naukowców z firmy IBM, zatytułowany „The Diary of a Datum”3. Dodajemy warstwy, które do pewnego stopnia ułatw iają nam , ludziom, myślenie o wizualizacji systemów, ale ostatecznie, pod względem wykonywalności naszych systemów, warstwy te są bardzo potrzebne. We wspomnianym artykule pokazano, w jaki sposób patrzymy na pojedynczy element danych. Niezwykłe jest to, ile razy ten element danych jest przekształcany, dzielony na strumienie wyjściowe i wejściowe, buforowany, aż wreszcie jest z nim wykonana jakaś realna operacja. Z warstwami abstrakcji dodawanymi do systemów są związane koszty. Wydaje mi się, że przeciętna złożoność i rozmiary programów wzrastają rok po roku. Czy stosowanie technik obiektowych może w czymś pomóc?

Grady: Jeśli przez techniki obiektowe rozumie pan określoną klasę języków, to można by powiedzieć, że te języki są rzeczywiście bardziej ekspresywne niż inne i w związku z tym reprezentują wyższy poziom abstrakcji (a zatem wymagają mniejszej liczby linii kodu, żeby coś zaprezentować). Jeśli zaś pod pojęciem „techniki obiektowe” rozumiemy pewną prostą filozofię dekompozycji — w odróżnieniu od abstrakcji algorytmicznych lub funkcjonalnych — to powracamy do jednego z problemów, na jakie wskazał Arystoteles w swoim traktacie Kategorie: do wyrażenia złożonych rzeczy potrzeba wielu form dekompozycji. Moim zdaniem złożoność nie jest tożsama z rozmiarami kodu (liczbą wierszy). Istnieje inny rodzaj złożoności, praw dopodobnie najlepiej mierzy się ją przez coś, co ja nazywam semantyczną gęstością (stosunek znaczenia do ekspresji oraz miernik semantycznych związków pomiędzy różnymi elementami). Czy gęstość semantyczna zwiększa się? Myślę, że tak, ale sądzę, że w sposób ortogonalny do sposobu wyrazu (obiektowego bądź nie) używanego do zaprezentowania danej semantyki.

3 Mitchell N. et al., The Diary of a Datum: Modeling Runtime Complexity in Framework-Based Applications, IBM Research, 2007.

UML

455

Współcześnie w oprogramowaniu ważną rolę odgrywa współbieżność.

Grady: To jest problem , który istnieje już od dłuższego czasu. Symulowana współbieżność (wielozadaniowość) na pojedynczym procesorze to stara koncepcja. Od chwili, gdy na świecie istnieje więcej niż jeden kom puter, ludzie myślą nad możliwościami ich współpracy. Dziś mamy mnóstwo komputerów, ale bardzo często wykorzystujemy luźno związaną rozproszoną współbieżność (na przykład w internecie) lub wewnętrzną współbieżność (wielordzeniowe procesory równoległych komputerów). Krótko mówiąc, to zawsze był ważny temat, i szczerze mówiąc, to naprawdę trudny problem . Przeciętny program ista nie wie, w jaki sposób budow ać rozproszone, w spółbieżne i bezpieczne systemy, ponieważ te właściwości wymagają rozwiązań systemowych. A co ze współbieżnością w wytwarzaniu oprogramowania? Czy kiedykolwiek będziemy potrafili skrócić czas tworzenia oprogramowania poprzez zaangażowanie większej liczby ludzi?

Grady: Znów muszę zakwestionować podtekst, jaki występuje w pańskim pytaniu. Skrócenie czasu wytwarzania to jedna z możliwych korzyści, ale istnieją inne wymierne korzyści, takie jak poprawa jakości, rozszerzona funkcjonalność, zmniejszona złożoność oraz inne efekty zaangażowania większej liczby ludzi. Nie da się zaprzeczyć, że zwiększa to liczbę dostępnych cykli pracy, ale także podnosi poziom szumu informacyjnego, koszty kom unikacji oraz liczbę wniosków projektowych. Rzeczywistość jest taka, że większość oprogramowania interesującego pod względem ekonomicznym wymaga jedynie kilku ludzi — a napraw dę interesujące w ym agają setek, jeśli nie więcej akcjonariuszy. A zatem w pewnym sensie zadał pan mało interesujące pytanie. © Co ogranicza skuteczność współpracy podczas wytwarzania oprogramowania?

Grady: Z mojego doświadczenia wynika, że istnieje w codziennym życiu programisty szereg punktów niezgody, które indywidualnie lub w połączeniu ze sobą wpływają na wydajność zespołu. Są to: •

Koszt stworzenia i organizacji przestrzeni roboczej.



Niewydajna współpraca nad produktem.



Utrzymywanie skutecznej komunikacji w grupie, włącznie z przekazywaniem wiedzy i doświadczenia, informacji o stanie projektu oraz wniosków z projektu.



Niewystarczająca ilość czasu przeznaczonego na różne zadania.



Negocjacje pomiędzy akcjonariuszami.



Niedziałające fragmenty systemu.

O wszystkich tych aspektach napisałem w artykułach, które m ożna znaleźć pod adresem : http://www.booch.com/architecture/blog.jsp?part=Papers.

456

ROZDZIAŁ

CZTERNASTY

W ja k i sposób rozpoznaje pan prostotę systemu?

Grady: Takie samo pytanie zadał mi Dave Parness. Ostatnio napisałem artykuł dla magazynu IEEE software w mojej kolumnie poświęconej sposobom rozwiązywania problemów złożoności za pomocą architektury. Zacząłem od następującego zdania: „Spójrzcie na wielotonowy granitowy głaz. Jest wielki, ale bardzo prosty. Spójrzcie na łańcuch DNA. Jest malutki, ale bardzo złożony” . Poproszono mnie wtedy: „Powiedz, dlaczego tak sądzisz” . Odpowiedziałem cytatem z pracy Herberta Simona The Sciences o f the Artificial [MIT Press]. Simon zauważył, że jeśli spojrzeć na złożone systemy — na tyle złożone, że można je zrozumieć — to zwykle mają one kilka wspólnych cech. Zazwyczaj są hierarchiczne, ułożone w warstwy w taki bądź inny sposób. Wewnątrz nich występują niezliczone powtórzenia. Kiedy Simon pisał tę pracę, postrzegał to w kategoriach pow tórzeń w strukturze. Uważam, że należy zaktualizować ten pogląd — nie chodzi tylko o pow tórzenia struktury. Chodzi także o powtarzające się wzorce projektow e, które m ożna zaobserwować w systemach. Miałem okazję pracować nad bardzo wieloma projektami niem al w każdej dziedzinie, jaką m ożna sobie wyobrazić. Muszę powiedzieć, że w idziałem naprawdę niezwykle rozbudowane systemy. Spotkałem również kilka pięknych systemów. Te, które były piękne i proste, zwykle m iały część w spólną w postaci zbioru wzorców projektowych, które górowały nad systemem — scalały wiele indywidualnych kom ponentów systemu i zapewniały jego prostotę. Jeśli spojrzymy na działania naukow ców zajmujących się DNA, to zauważymy, że próbują oni znaleźć te wspólne elementy. Zwykli ludzie mogliby sądzić, że wewnątrz DNA można znaleźć wiele bezużytecznych śmieci. Często myślą oni: „To jest śmieć pozostały z czasów ewolucji. Jest bez znaczenia” . Kiedy zaczynamy wchodzić głębiej, nagle zdajemy sobie sprawę, że ten śmieć to wcale nie jest śmieć. Nazwaliśmy go śmieciem, ponieważ nie potrafiliśmy go zrozumieć. To jest problem boga niedostatków wiedzy, ale nie będę tu rozwijał tematów metafizycznych. W miarę jak ludzie zaczną odkrywać piękno i elegancję złożonych systemów, wyłonią się z nich fascynujące wzorce. Właśnie w taki sposób rozpoznaję prostotę i elegancję. Co pan rozumie przez pojęcie „wzorca projektowego, który góruje nad systemem"?

Grady: Przytoczę przykład systemu Wall Street. Nie będę wymieniał jego nazwy. Ostatnio prowadziłem z tymi ludźmi prace wykopaliskowe. Próbowaliśmy odkopać pewne podjęte decyzje. To olbrzymi system. Wiele milionów wierszy kodu napisanych w każdym języku programowania, jaki można sobie wyobrazić — od języka asemblera po współczesne języki wysokopoziomowe. Jeśli przyjrzeć się ogólnej architekturze systemu, m ożna zauważyć, że występują pewne wiodące zasady, które są bardzo popularne. Istnieje aspekt bezpieczeństwa systemu. Nie chcemy, aby ktoś m ógł anonimowo wejść do systemu i usunąć tysiące transakcji. Jeśli ktoś codziennie realizuje transakcje warte wiele miliardów dolarów, to nie chce, aby można to było zauważyć.

UML

457

W jaki sposób przeciwdziałać wyciekom informacji? Trzeba zastosować bardzo prostą regułę biznesową. Wszystkie zmiany stanu powinny być zapisane w składowanych procedurach w bazie danych, tak aby nie było sposobu, ponieważ operacje te są sprawdzane za pom ocą różnych mechanizmów formalnych. Nie ma sposobu wstrzyknięcia kodu zmieniającego stany bez przejścia przez te mechanizmy. To bardzo prosta, elegancka zasada, która góruje nad całym kodem tego systemu. O tego typu górowanie mi chodzi. Wzorce projektowe to nazwy społeczności klas działających ze sobą w harm onijny sposób. Analizowanie indywidualnych wierszy kodu nie pozwala widzieć tego rodzaju schematów. Na tym polega wyzwanie architekturalnych wykopalisk, ponieważ kod nie pokazuje całej prawdy i nie umożliwia znalezienia prawidłowości i odkrycia wzorców. Często wzorce te są zamknięte w głowach indywidualnych osób. Oznacza to, że nie zawsze można spojrzeć na system, zrozumieć go i zauważyć jego prostotę lub wewnętrzną organizację. Często nie jest to możliwe do chwili zrozumienia ograniczeń, reguł lub decyzji projektowych.

Grady: Oczywiście, że tak. Uczymy się także przez w nioskow anie. Przyglądamy się całości i zaczynają się wyłaniać wzorce. Nie da się spojrzeć na jeden egzemplarz i powiedzieć: „Widzę wzorzec”, ponieważ nie taka jest natura wzorców. Jest to jeden z obszarów, w którym często używam UML-a do wizualizacji architektury budowanych systemów. Nie ma wątpliwości, że to jest w ytw arzanie typu „green field” , choć najczęściej — tu zacytuję Chrisa W intera — większość oprogramowania na świecie jest wytwarzana na zasadzie inwestycji „brown field” . Brzmi to dość nieprzyjemnie. Rzeczywiście, do pewnego stopnia tak jest, ale większość interesujących systemów, które tworzymy, to adaptacje lub rozszerzenia systemów istniejących. Być może powinniśmy zastosować termin „wytwarzanie typu brown field".

Grady: Myślę, że to dobry pomysł. Moim zdaniem w tym obszarze do gry wchodzi UML, ponieważ pozwala na wizualizowanie rzeczy, których nie m ożna zobaczyć w samym kodzie. Wzorce uwidaczniają się na poziomie abstrakcji, który znajduje się wyżej niż kod. Wyobrażam sobie, że to wielka satysfakcja, kiedy podczas rysowania diagramów lub tworzenia schematów UML istniejącego systemu można ujednolicić pewne pojęcia.

Grady: Zgadza się. I pan, i wielu innych moich rozmówców podkreśla znaczenie technik obiektowych dla tworzenia prawidłowego projektu. W jakim stopniu stosowanie obiektów ma zasadnicze znaczenie?

Grady: Być może zabrzmi to dziwnie w moich ustach, ale według mnie programowanie obiektowe jest efektem, a nie pierw otną przyczyną. M iałem okazję analizow ania

458

ROZDZIAŁ

CZTERNASTY

wielu złożonych systemów z wielu dziedzin. Zaobserwowałem, że najlepsze z nich — najbardziej użyteczne, najbardziej eleganckie, najlepsze pod każdym względem, jaki można sobie wyobrazić — miały pewne wspólne cechy w sposobie ich wytwarzania — mianowicie koncentrację na wyraźnych abstrakcjach, dobrą separację problemów oraz zrównoważoną dystrybucję odpowiedzialności. Abstrakcja to w dużej mierze problem klasyfikacji, a mechanizmy obiektowe szczególnie dobrze sprawdzają się w klasyfikowaniu świata. Ward Cunningham i Kent Beck na konferencji OOPSLA około 2 0 lat temu po raz pierwszy zaproponowali stosowanie wzorców projektowych w programowaniu, bazując na pracach architekta Christophera Alexandra. Czy był to znaczny, ery też umiarkowany sukces?

Grady: Moja osobista opinia, nie kogoś żyjącego czy umarłego, czy też mojej firmy, albo kogoś, kto ma się urodzić, jest taka, że prace Alexandra były interesujące. Stały się inspiracją dla wielu osób dzięki ich przeniesieniu do dziedziny oprogramowania. Pewna niewielka społeczność z nich skorzystała, ale większe grono nie. Jest to potwierdzenie, że język wzorców, choć można go spotkać w wielu miejscach, nie jest prawdopodobnie tak dominujący, jakby mógł być lub powinien być. Kiedy mówi pan „język wzorców", ma pan na myśli coś bliskiego temu, co powiedział Alexander, czyli „Stwórzmy słownictwo, za pomocą którego będziemy mogli rozmawiać o

powtarzających się ideach projektu"?

Grady: Całkowicie się z panem zgadzam. W wielu branżach, w jakich pracowałem, w wielu projektach, w jakich uczestniczyłem, słyszano o pojęciu wzorców Gangu Czterech, ale nie stosowano ich w praktyce do budowania systemów czy tworzenia architektury. Istnieją miejsca, w których zastosowanie wzorców projektow ych przyniosło duże korzyści, ale uważam, że nie zyskały one takiej popularności, jaką chciałbym widzieć. Tu nie chodzi o szczegóły. Chodzi o sposób myślenia o problemie. Co może rozwiązać ten problem?

Grady: Zwykle, kiedy przystępuję do projektów, staram się pomóc w opracowaniu języka wzorców. Członkowie zespołu muszą go zrozumieć. Muszą zdobyć podstawową wiedzę na temat tego, czym w ogóle są wzorce. Następnie zachęcam ich do wyszukania wzorców i nazw ania tych wzorców, które opracowali w swoich systemach, oraz udokumentowania ich — tak by stały się ich własnymi wzorcami. Niektóre z tych wzorców mogą odzwierciedlać reguły biznesowe i ograniczenia.

Grady: N iektóre tak, a niektóre nie. W szystko zależy od natury danej dziedziny. Jakie są unikatow e sposoby, now atorskie pom ysły wykorzystane do rozwiązania problemów? To właśnie są wzorce projektowe.

UML

459

Obecnie pracuję nad pewnym projektem. Ma tylko jakieś 50 000 wierszy kodu w języku Ada. Występują w nim wspaniałe wzorce, w których drzemie duży potencjał. Wzorce te istnieją w umysłach programistów. Wyzwanie polega na odpowiednim nazwaniu tych wzorców, aby możliwa stała się skuteczna komunikacja. Są to doskonali architekci, ale jeśli nie potrafimy czegoś nazwać, trudno o tym mówić, manipulować tym oraz przekazywać innym. Weźmy na przykład język APL Niektórych konstrukcji przepływu danych nie da się przetłumaczyć bezpośrednio na inne języki.

Grady: To prawda. Ada jest doskonałym językiem pod wieloma względami. Spójrzmy na konstrukcje, które są dodaw ane do języka Java, C++ i innych. Obsługa współbieżności wbudowana w języku, mechanizm obsługi wyjątków, typy generyczne, abstrakcyjne typy danych. Język Ada wyprzedził swoje czasy.

460

ROZDZIAŁ

CZTERNASTY

ROZDZIAŁ

PIĘTNASTY

Perl

Fani Perlą nazywają go „patologicznie eklektycznym narzędziem do sortowania śmieci" (Pathologically Edectic Rubbish Lister) lub „piłą łańcuchową ze Szwajcarskiej Arm ii" (Swiss-Army Chainsaw). M o tto dla języka Perl brzmi: „Istnieje więcej niż jeden sposób realizacji celu!". Twórca Perlą, Larry W all, czasami opisuje ten język jako język-klej pierwotnie zaprojektowany jako rodzaj łagodnego przejścia pomiędzy powłoką Uniksa a językiem C. Perl miał być narzędziem ułatwiającym użytkownikom realizację swoich zadań. Wykorzystuje zasady lingwistyczne oraz decyzje projektowe z Uniksa. Dla Perlą dostępne jest prawdopodobnie największe repozytorium bibliotek dostępne w jakimkolwiek języku — tzn. CPAN. Wielu programistów z niecierpliwością oczekuje Perlą w wersji 6 — języka zaprojektowanego w taki sposób, by przetrwał co najmniej 20 lat.

461

Język rewolucji W ja k i sposób zdefiniowałby pan język Perl?

Larry Wall: Perl to ciągły eksperyment, którego celem jest sprawdzenie, w jaki sposób najlepiej zastosować niektóre zasady języka naturalnego do języka komputerowego. Ma to nastąpić nie na płytkim poziomie syntaktycznym, jak w przypadku COBOL-a, ale na znacznie głębszym poziomie praktycznym . Podstawowe zasady języka naturalnego są następujące (pozwólcie, że wkleję listę): •

Możliwości ekspresji są ważniejsze niż łatwość nauki.



Jeśli ktoś jest dzieckiem, może posługiwać się dziecinną wersją języka.



Język może być przydatny, zanim poznamy go w całości.



Zazwyczaj istnieje kilka prawidłowych sposobów na to, by powiedzieć mniej więcej to samo.



Każda lingwistyczna wypowiedź uzyskuje znaczenie w wielu kontekstach jednocześnie.



Język jest agnostyczny co do tego, który kontekst należy zoptymalizować w określonym momencie.



Język nie wymusza żadnego konkretnego paradygmatu, którego zastosowanie wykluczałoby użycie innych paradygmatów.



Wydajna komunikacja wymaga określonego poziomu lingwistycznej złożoności.



Sieci semantyczne ogólnie niezbyt dobrze dają się odwzorować na przestrzenie ortogonalne.



Istnieje możliwość stosowania skrótów — często używane wyrażenia powinny być krótsze niż wyrażenia używane rzadziej.



Nie wszystko jest łatwe do wyrażenia. Wyrażenie czegoś może być trudne, ale musi być możliwe.



Języki mają naturalne miejsca na czasowniki, rzeczowniki, przymiotniki, przysłówki itd.



Ludzie potrafią dobrze rozstrzygać niejednoznaczności, kiedy miejsca te są oczywiste.



W językach w natu ralny sposób w ystępują znaki przestankowe: pauzy, znaki intonacji, akcentu, tempa itd.

462



W językach wykorzystuje się zaimki, w przypadku gdy temat konwersacji jest oczywisty.



Języki powinny w idealny sposób wyrażać rozwiązania, a nie mówić o własnych konstrukcjach.

ROZDZIAŁ

PIĘTNASTY



Dla sukcesu języka większe znaczenie ma zdrowe środowisko niż specyficzna technologia.



Zasadniczym celem języka jest komunikacja z ludźmi, którzy różnią się między sobą.



Mówienie z akcentem jest dopuszczalne, o ile inni potrafią nas zrozumieć.



Specyficzne problemy dotyczą subkultur, które często generują użyteczne dialekty lub odmiany języka.



Ludzie uczą się przełączać kontekst (ang. frame shift) w przypadku zetknięcia się z różnicami w dialekcie lub akcencie.



Przełączanie kontekstu jest wydajniejsze, jeśli możemy łatw o stwierdzić, z jakim podzbiorem języka mamy do czynienia.



W dłuższej perspektywie żaden żywy język nie uniknie ewolucji.



Dla większości kom unikacji sprawdza się zasada „im gorzej, tym lepiej” , ale czasami lepsza jest zasada „im lepiej, tym lepiej” .



Szczególnie istotna dla pisanych dokum entów jest ich ocena w określonym kontekście.

Każda z tych zasad na przestrzeni lat miała olbrzymi wpływ na projekt Perlą. Nie ulega wątpliwości, że każdy z tych punktów m ożna by rozwinąć do objętości akapitu, rozdziału, a naw et rozprawy naukowej. Nawet jeśli pom inąć języki komputerowe, okazuje się, że lingwistyka jest obszerną dziedziną obejmującą wiele specjalności. Z drugiej strony większość tych zasad zignorowano w innych językach programowania. Z różnych pow odów historycznych wielu projektantów języków założyło, że programowanie komputerów jest zajęciem bardziej podobnym do matematycznego dowodzenia aksjomatów niż do próby komunikacji między kulturami. Żeby była jasność — to działa także w drugą stronę: koncentracja na tych lingwistycznych zasadach spowodowała, że zacząłem ignorować pewne istotne kwestie informatyczne. Obecnie pracujemy nad poprawieniem niektórych niedociągnięć. Jakie kwestie informatyczne ma pan na myśli?

Larry: Jednym z ważniejszych miejsc, w których pierwsze projekty Perlą zawiodły — lub być może powinienem uczciwie przyznać: w których ja zawiodłem — jest stosowanie zasięgów dla zm iennych. To prawda, w Perlu 5 istnieje pojęcie leksykalnych zasięgów dla zm iennych, ale są miejsca pozbaw ione prawidłowej obsługi zasięgów. W dalszym ciągu występuje mnóstwo zmiennych globalnych. W Perlu występuje także sporo operacji na odległość. Popełniłem podobny błąd, jaki popełniono w takim języku, jak Ruby. Chodzi o tzw. małpowanie typów, czyli sięganie i manipulowanie wnętrznościami, czego efektem są operacje na odległość. Nauczyliśmy się, że powinno istnieć kilka prawidłowych zasięgów zmiennych. Zgodnie z podejściem klasycznym oczywiście można dołączać informacje do obiektu lub zasięgu

PERL

463

leksykalnego, albo zasięgu dynamicznego, ale istnieją również zasięgi plików, procesów, wątków, typów, metaklas, ról, prototypów, zdarzeń, gramatyk i transakcji, by wymienić tylko kilka. Specyficznym rodzajem zasięgów są nawet takie elementy, jak poziomy priorytetów . Każda lokalizacja w przestrzeni lub czasie ma elementy, które są w naturalny sposób z nią powiązane lub do niej dołączone. Elementy te znalazłyby się w złym miejscu, gdyby dołączono je gdzieś indziej. Jest to coś, czego powoli się uczyłem przez cały czas. Można by nawet powiedzieć, że zbyt powoli. Nie uwzględniłem także oceny tego, które struktury danych są zmienne, a które niezmienne. Jest to cecha, która ze względu na współbieżność nabierze jeszcze większego znaczenia w najbliższych latach. Nie m ożna popraw nie obsługiwać współbieżności, jeżeli nie potrafi się śledzić, co się może zmienić i kiedy. To jest coś, co Perl z powodów historycznych zamiótł pod dywan. Wszystkie struktury danych traktowaliśmy jako zmienne. Zmienne nawet wtedy, gdy się je tylko czasami obserwuje.

Larry: Tak. To sposób myślenia, który dobrze się sprawdza w przypadku małych program ów i naiwnych użytkowników — nie narusza oczekiwań początkujących, jeśli nie są zbyt zaawansowani. Z drugiej strony takie podejście utrudnia skalowanie programowania na duże systemy, gdy trzeba zwracać więcej uwagi na zmienność i niezm ienność, dane publiczne i prywatne. Jest to rodzaj rozgraniczenia, które informuje nas o tym, kiedy możemy coś zmienić, a kiedy nie. Te sprawy nabrały większego znaczenia w projekcie Perlą, zwłaszcza w Perlu w wersji 6. Jednocześnie chcemy utrzymać te same odczucia i tam, gdzie to możliwe, ukryć pewne nowe pojęcia, tak aby nowi użytkownicy mogli je w pewnym sensie zignorować. Ale to, że owe własności są ukryte, nie oznacza, że ich tam nie ma. Gdyby w Perlu rzeczywiście pod spodem wszystko działało prawidłowo, moglibyśmy mieć nadzieję na wykrycie, kiedy coś dzieje się źle. Moglibyśmy również mieć nadzieję na to, że będziemy wiedzieć, kiedy ma sens dystrybucja pracy na wiele rdzeni i nie trzeba się martwić, że operacje zostaną wykonane w niewłaściwej kolejności. Aby to było możliwe, trzeba śledzić zależności pomiędzy danymi. To w rzeczywistości implikuje konieczność wiedzy o tym, kiedy wskaźnik m ożna traktować jak wartość, a kiedy trzeba go traktować jak obiekt. Perl narodził się jako kolekcja narzędzi do manipulowania tekstem oraz sposób na uproszczenie zadań administrowania systemem. Czym to narzędzie jest teraz?

Larry: Obecnie Perl spełnia dwie role. Po pierwsze, Perl w wersji 5 jest bardzo stabilnym przykładem tego, czego był na początku: językiem do klejenia API, który świetnie się nadaje do przetwarzania tekstu (uzupełniony wielką liczbą rozszerzeń stworzonych przez patologicznie pom ocną społeczność). Dlatego właśnie prototyp internetu został stw orzony głównie w Perlu — HTML to form at tekstowy,

464

ROZDZIAŁ

PIĘTNASTY

a użytkownicy chcieli pisać HTML, który skleiłby dane z różnych źródeł — włącznie z bazami danych. Odpowiednie rozszerzenia Perlą były już gotowe albo były łatwe do napisania. Ale Perl to także Perl 6, w którym próbujem y popraw ić wszystko to, co było złe w Perlu 5 bez naruszania niczego, co było dobre w Perlu 5. Wiemy, że to jest niemożliwe, ale i tak zamierzamy to zrobić. Całkowicie zmieniamy projekt języka, a jednocześnie staram y się zachować te same zasady projektow e, które rządziły wcześniejszymi wersjami. Nawet w swojej bieżącej, częściowo zaimplementowanej postaci Perl 6 w opinii wielu jest już spektakularnie dobrym językiem. Kiedy język zostanie zrealizowany, będzie on — miejmy nadzieję — zarówno samoopisującym się, jak i samoparsującym językiem korzystającym z gram atyk dających się wywnioskować z kontekstu. Dzięki tem u będzie zoptymalizowany w kierunku łagodnej ewolucji do postaci dowolnego języka, jakiego m ożna oczekiwać za 20 lat. Będzie wyposażony w „gałki” do regulowania go w wielu różnych w ym iarach, włącznie z możliwością ukrycia wszystkich tych wymiarów, którymi nie będziemy chwilowo zainteresowani. Wszystko będzie zależało od paradygmatu, który wybierzemy w celu rozwiązania konkretnego problemu. Cóż, takie przynajmniej jest nasze marzenie... W ja k i sposób udało się pana zespołowi przejść od narzędzia do manipulowania tekstem, stworzonego, by ułatwić życie administratorom systemów, do kompletnego języka programowania? Czy był to celowy krok, czy stopniowa transformacja?

Larry: Hm, te dwa scenariusze wzajemnie się nie wykluczają. To dobrze, ponieważ ja scharakteryzowałbym proces rozwoju języka jako celową stopniową transformację. Język to doskonałe miejsce do eksperym entowania. Było dla m nie oczywiste od początku, że będę starał się ciągle rozwijać go do postaci, jaka będzie mi potrzebna na dziś. Ale z konieczności taki proces musi być stopniowy, niezależnie od tego, jak bardzo jest celowy i przemyślany. Jeśli nie z innych powodów, to choćby dlatego, że ze zmianą trzeba czekać do danego m om entu. Dopiero wtedy wiadomo, czego naprawdę chcemy w następnej kolejności. Był taki m om ent, kiedy zdałem sobie sprawę, że Perl nie służy tylko do tego, aby ułatw iać w ykonyw anie rzeczy łatw ych, ale także do tego, aby umożliwiać wykonywanie rzeczy trudnych. Perl 2 pozwalał wyłącznie na przetwarzanie danych tekstowych, dlatego powiedziałem sobie: „Perl jest językiem wyłącznie do przetwarzania tekstu. Jeśli nauczę Perlą sposobu przetw arzania danych binarnych, to kto wie, gdzie zatrzyma się jego rozwój” . Następnie zdałem sobie sprawę, że istnieje bardzo wiele problemów w świecie, które w większości mają charakter przetwarzania danych tekstowych, ale wymagają niewielu operacji na danych binarnych. Dodanie rozwiązań dla tych problemów spowodowałoby znaczne rozszerzenie zakresu stosowania Perlą. Nawet gdyby obsługa danych binarnych była jedynie podstawowa. A zatem Perl 3 potrafił przetwarzać dane binarne i kto wie, gdzie się zatrzyma jego rozwój.

PERL

465

Cały proces rozwoju Perła postrzegałem jako kontynuację mojego wcześniejszego poglądu, że Perl nie będzie miał żadnych ograniczeń podobnych do tych, które były plagą dla pierwszych wersji narzędzi Uniksa. Obcięcie ciągu znaków tylko dlatego, że zawiera on przypadkowy znak nul 1 , jest prawie tak samo złe, jak obcięcie go ze względu na to, że jego bufor jest zbyt mały. Można nawet powiedzieć, że proces uogólniania języka polega na usunięciu różnego rodzaju ograniczeń na takim lub innym poziomie. Czy woli pan wolność, czy porządek? Czy według pana lepiej jest, jeśli istnieje jeden sposób wykonania działania, czy też tysiące sposobów osiągnięcia tego samego celu?

Larry: To pytanie nabiera znaczenia dopiero wtedy, kiedy zdefiniujemy, co mamy na myśli, mówiąc „sposób” i „osiągnięcie celu” , jakie optymalizacje są dozwolone oraz czy m ożna stosować w spólnie różne perm utacje i kom binacje opcji. Języki naturalne są jak miasta, w których na rogu spotyka się kilka ulic oraz zwykle istnieje kilka dobrych sposobów dojścia do obranego celu. Oczywiście, jeśli policzymy wszystkie możliwe sposoby dotarcia do określonego miejsca, a nie tylko najlepsze z nich, może się okazać, że jest ich bardzo dużo. N awet w takim mieście, w którym ulice są prostopadłe, do celu m ożna dotrzeć na niezliczoną liczbę różnych sposobów. No chyba że ograniczymy rozwiązanie w taki sposób, aby cel był interpretow any jako w ektor m atem atyczny określający lokalizację w przestrzeni oraz dodamy wymaganie, że interesuje nas najkrótsza ścieżka. Ale ruchu pieszego po mieście nie m ożna zdefiniować w postaci wektora. Należy wprowadzić optymalizację dla różnego rodzaju warunków zewnętrznych, w miarę jak poruszam y się po mieście bądź po języku. Może (lecz nie musi) istnieć jedno, najlepsze rozwiązanie. Może też być tak, że rozwiązanie poprawne jednego dnia może nie być takie w innym dniu. Możemy zoptymalizować rozwiązanie dla przypadku odwiedzin wszystkich parków znajdujących się po drodze lub możemy trzymać się z dala od rzeki. Albo iść wzdłuż rzeki. A zatem, żeby odpowiedzieć na pańskie pytanie, trzeba przyjąć jakieś ograniczenia, w przeciwnym wypadku nie będzie to optymalna odpowiedź. Podejrzewam, że rząd w ymiaru fraktalnego języka naturalnego jest większy niż 1, ale znacznie mniejszy niż 1000. Jeśli istniałby tylko jeden sposób na wyrażenie czegoś, można by zastąpić program istę robotem . Jeśli napraw dę istnieją tysiące pozornie rów now ażnych sposobów zrobienia jakiejś czynności, bardzo szybko znajdziemy rodzaj sita, które powoduje odrzucenie większości z nich, tak by liczba opcji możliwych do wyboru była sensowna.

466

ROZDZIAŁ

PIĘTNASTY

Język Wiele osób chwali Perlą za jego niezwykłe możliwości przetwarzania tekstu. Czy istnieje związek pomiędzy tymi możliwościami a względami lingwistycznymi, które miał pan na myśli podczas tworzenia języka?

Larry: To jest dobre pytanie. Z jednej strony chciałbym odpowiedzieć, że tak, ale z drugiej prawdopodobnie nie będę miał racji. Są jasne dowody na to, że chociaż język Perl został zaprojektowany tak, by do pewnego stopnia działał jak język naturalny, niezbyt dobrze nadawał się do parsowania kodu Perlą (używał do tego celu narzędzia yacc). Podejrzewam, że gdyby odpowiedź na to pytanie była twierdząca, to Perl 1 byłby znacznie bardziej ukierunkow any na operacje parsow ania języków niż Perl 6 jest ukierunkow any teraz. Tak jednak nie było. Może pan to nazwać hermetyzacją, jeśli pan chce, ale zadanie, które próbował rozwiązywać Perl 1 było znacznie bardziej ograniczoną formą przetwarzania tekstu niż to, co się dzieje w naszych umysłach, kiedy myślimy o języku naturalnym. Kilka lat temu przeglądałem zestaw testów dla Perlą 1. N aw et tamten kod m iał typowe cechy dla Perlą, mimo że wiele pojęć Perlą, takich ja k kontekst, ewoluowało z biegiem lat. Ile wewnętrznej perlowości m iał pan na myśli od początku pracy nad językiem?

Larry: Rzeczywiście pojęcie kontekstu było ważne od samego początku, a w Perlu 4 było ono naw et dobrze zaim plem entow ane. Myślę jednak, że to, jak ważny jest kontekst, docierało do mnie powoli. Jako lingwista zawsze zdawałem sobie sprawę z istnienia kontekstu. W edług tagmemiki, jednej z m oich ulubionych teorii lingwistycznych, bardzo duże znaczenie ma wiele poziom ów kontekstu. Sposób leksykalnej klasyfikacji słowa bardzo się różni od sposobu jego wykorzystywania. Wiemy, że słowo „czasownik” jest rzeczownikiem, nawet jeśli utworzymy z niego czasownik. W związku z tym zawsze pamiętałem, że określonej konstrukcji można użyć na wiele sposobów w zależności nie tylko od jej własnej struktury i typu, ale także od semantyki i kontekstu środowiskowego. Myślę, że projekt kontekstowy najbardziej uwidacznia się w pierwszych wersjach Perlą nie tyle w niskopoziomowej składni — jak na przykład wielkości skalarne a listy — ile bardziej w tym, że kiedy próbujemy wykonać zadanie, programujemy w kontekście czynności, którą próbujemy wykonać na zewnątrz programu. Z tego względu dobrze jest, kiedy język pozwala wyrażać to samo na wiele sposobów. Dzięki temu można zoptymalizować kod pod kątem w arunków zewnętrznych. Innymi słowy, program powinien bardziej reprezentować kontekst problemu, niż próbować wyrażać problem w kontekście języka.

Larry: Tak. Pytanie, kto ma być panem, a kto sługą — to wszystko. Zazwyczaj istnieje kilka różnych sposobów reprezentacji określonego problemu, zwłaszcza jeśli weźmie

PERL

467

się pod uwagę różne paradygm aty program ow ania. N iektóre z nich pozwalają na bardziej naturalne odwzorowanie na niektóre rodzaje problemów, natomiast inne będą lepiej odwzorowywały się na inne rodzaje problemów. Jeśli myślimy o problemie jako o rodzaju m atem atycznego dow odu, wtedy lepszy będzie język zbliżony do program ow ania funkcyjnego. W przypadku gdy elementy m ają uniw ersalne znaczenie, które nigdy się nie zmienia — kiedy istnieje wiele niezmiennych stanów w budow anych w pojęcia, z którym i bezpośrednio pracujemy — wtedy potrzebny jest język bardziej deklaracyjny. Jeśli natom iast nasz problem dotyczy symulacji, to będziemy go rozważać bardziej w kategoriach obiektów, które zmieniają się w czasie. Dla konkretnego problem u, który próbujem y rozwiązać, m ogą to być postacie izomorficzne, ale różne paradygm aty program ow ania zmuszają do umieszczania zmiennych stanów w takim bądź innym miejscu. Jeśli stan dotyczy obiektów, jest to oczywiste — to znaczy dokładnie wiadomo, do czego obiekty służą. Nie jest to tak samo oczywiste, kiedy stan dotyczy programowania funkcyjnego — stan jest niejawnie ukryty na stosie oraz w sposobie ułożenia na nim różnych m onad i wywołań funkcji. A zatem jedna z form kontekstu dotyczy preferowanego sposobu widzenia tych rzeczy przez programistę. Jest to rodzaj słabej hipotezy Sapira-Whorfa. Nie wierzę w silną wersję tej hipotezy. Był pan predestynowany do tego, by nie wierzyć w tę silną wersję.

Larry: Zgadza się. W ybrałem bycie predestynowanym. A może to jest tak, że mój mózg nie jest okablow any lingwistycznie, zatem jest w mojej głowie sporo nielingwistycznego sposobu myślenia. Dlatego właśnie nie uważam, żeby język miał kontrolę nad moim mózgiem. Tak czy owak, zgadzam się ze słabszą formą hipotezy: język, który wybieramy do wyrażenia czegoś, z całą pewnością ma pewien wpływ na sposób tego wyrażenia. Jeśli zatem ktoś naprawdę chce mieć jeden język, który będzie lepiej dopasowany do różnych rodzajów problemów, to język ten nie może zmuszać do myślenia w jeden konkretny sposób. Istnieje kilka subtelnych kontekstów: uniksowy program jednowierszowy pozwalający na wyrażenie użytecznego, działającego programu w Perlu. Istnieje kontekst skryptu powłoki, gdy mamy coś, co wygląda ja k skrypt powłoki, a stoją za tym wszystkie możliwości Perlą 5. Kontekst samodzielnego, jednostronicowego programu CGI. Do pewnego stopnia jest to osobny kontekst pozwalający na realizację określonych operacji. W obu tych postaciach można rozpoznać Perlą oraz jeg o określony styl. Jednowierszowy program w Perlu albo skrypt powłoki napisany w Perlu.

Larry: W teorii lingwistycznej nazywamy to pragmatyką, która znajduje się o krok lub dwa od sem antyki w kierunku socjologii. Niektórzy lingwiści koncentrują się na niskopoziomowej fonologii lub składni. Tę samą tunelow ą wizję m ożna zaobserwować w projektach wielu języków programowania. Projektanci nie myśleli zbyt dużo o sposobie wykorzystania wypowiedzi do realizacji praktycznych działań.

468

ROZDZIAŁ

PIĘTNASTY

Jeśli rozpatrujemy problem w kategoriach jednowierszowych programów i skryptów powłoki, to w zasadzie możemy powiedzieć, że podobne zjawiska występują w języku naturalnym. Istnieją wypowiedzi, które możemy skierować do kogoś na przystanku autobusowym — to jednowierszówki, które będą zrozumiałe. W języku naturalnym jest wiele takich krótkich wypowiedzi. Na drugim krańcu mamy do dyspozycji wiele różnych gatunków literackich, które m ożna porów nać do paradygm atów programowania. Istnieją różne poziomy dyscypliny, które można uprawiać podczas tworzenia literatury. Gatunki literackie rządzą się określonymi regułami, które od czasu do czasu można łamać. Czasami popełniamy głupotę i je łamiemy, ale sam język nie stara się wymuszać na nas jakiegoś określonego stylu. Język naturalny jest neutralny pod tym względem. Język jest sługą poety — jest artystycznym medium, za pomocą którego artysta próbuje zrobić Coś Innego. To Coś Innego kieruje całym procesem i tak powinno być. W pewnym sensie języki naturalne są niezwykle skromne. Nie mówią, w jaki sposób należy rozmawiać. Nauczyciele gramatyki podpowiadają nam, jak powinno się mówić, ale w większości przypadków ignorujemy to i nie ma w tym niczego złego. Niemniej jednak istnieje odpowiednik szkolnego nauczyciela gramatyki dla języków program ow ania. W przypadku pewnego rodzaju wypowiedzi należy postępować zgodnie z regułami, chyba że zdajemy sobie sprawę z tego, dlaczego je łamiemy. Języki kom puterow e muszą być zrozumiałe dla kom puterów . To w prow adza dodatkow e ograniczenia. W szczególności nie m ożna do tego celu użyć języka naturalnego, ponieważ w większości przypadków podczas kom unikow ania się z użyciem języka naturalnego zakłada się bardzo dużą inteligencję po stronie słuchającego, a ten z kolei zakłada bardzo dużą inteligencję po stronie mówiącego. Jeśli ktoś spodziewa się takiej inteligencji od komputera, będzie srodze rozczarowany. Jak do tej pory nie potrafimy jeszcze programować komputerów w taki sposób. Mimo że Perl jes t pierwszym, postmodernistycznym językiem komputerowym, komputery są naprawdę kiepskie w rozumieniu ironii.

Larry: To prawda. One naprawdę nie rozumieją, kiedy powinny prosić o informacje. A to dlatego, że nie rozumieją, w którym momencie powinny być niepewne. Trzeba jednak przyznać, że istnieje wielu ludzi, którzy również nie rozumieją, kiedy powinni być niepewni, ale to jest kwestia skali. Komputery bardzo szybko promują się na poziom swojej niekompetencji. Często powtarza pan, że projektanci języków komputerowych powinni zwracać znacznie więcej uwagi na lingwistów niż na m atem atyków, ponieważ lingwiści wiedzą, w ja k i sposób komunikować się z ludźmi.

Larry: Powiedzmy, że wiedzą, w jaki sposób porozumiewają się prawdziwi ludzie.

PERL

469

M atematycy wiedzą, jak kom unikować się wzajemnie ze sobą, ale tu nie staramy się udowodnić, czy m atem atycy są prawdziwymi ludźmi, czy nimi nie są. Jestem pewien, że matematycy potrafią znaleźć jakiś sposób i poszukać teorii, która pozwoli im udow odnić, że są prawdziwi. Niemniej jednak uważam, że lingwiści zwracają nieco więcej uwagi na aspekty psychologiczne i pragmatyczne, niż zazwyczaj robią to matematycy. Być może zatem to lingwiści mogą pomóc komputerom stać się nieco inteligentniejszymi w tym zakresie. Czy sądzi pan, że idea niewielkiego, rygorystycznego i sprawdzalnego modelu języka programowania może nie przyjąć się w rzeczywistym świecie, w którym ludzie muszą go praktycznie wykorzystywać?

Larry: Zawsze znajdzie się pewna grupa ludzi, którzy będą chcieli przefiltrować swoje umysły przez bardzo ścisły filtr. W takim stopniu, w jakim można znaleźć ludzi, którzy są do tego chętni, określony język będzie się dobrze nadawał do dziedziny problemów, do której jest przeznaczony, o ile nie zawiedzie coś innego. Twierdzenie przeciwne nie zawsze jest prawdziwe. Język, który jest wystarczająco rozbudowany, z wielu powodów może nie osiągnąć sukcesu. Może być zbyt trudny do zaimplementowania. Może być za trudny do tego, aby ludzie chcieli podjąć trud nauczenia się jego użytecznego podzbioru. Przypuszczam, że są to dwie najważniejsze trudności w zaakceptowaniu bardziej rozbudowanego języka. Niemniej jednak żaden z nowoczesnych języków komputerowych, które do tej pory wynaleziono, nawet się nie zbliżył do poziomu złożoności języka naturalnego. Ogólnie rzecz biorąc, ludzie — być może nie Amerykanie, ale ludzie, dla których angielski nie jest językiem urzędowym — bardzo chętnie uczą się obcych języków. Próbowałem uczyć się kilku języków naturalnych. Muszę przyznać, że są naprawdę trudne. Złożoność leksykalna, zawiłe reguły gramatyczne, które są różne dla różnych języków, wymuszanie myślenia o różnych rzeczach w różnej kolejności, różne sposoby wypowiadania tych samych treści w różnych językach itd. Nauczenie się języka naturalnego jest trudne. Powstaje pytanie, czy można rzeczywiście stworzyć język komputerowy, który będzie na tyle bogaty, aby użytkownicy chcieli pokonać tę barierę nauki? Czy można tak skonstruować język, aby użytkownicy mogli szybko nauczyć się jego podzbioru? W językach naturalnych można zaobserwować tworzenie mieszanek różnych języków. Powstają one wtedy, gdy ludzie m ają wystarczającą motywację do znalezienia możliwości porozumiewania się. Czy możemy sięgnąć po taką dynamikę? Czy m ożna zaprojektow ać taki język, aby nie był bardziej skomplikowany, niż rzeczywiście powinien? To dobre pytania, na które każdy projektant języka odpow iada w inny sposób. W Perlu występują hierarchiczne przestrzenie nazw, ale w niektórych językach komputerowych istnieje płaska przestrzeń nazw i wszystko znajduje się w jednym słowniku. M ożna by się spierać, czy w przypadku języka angielskiego jest podobnie. Zwłaszcza jeśli ten słownik nazywa się Webster lub Johnson.

470

ROZDZIAŁ

PIĘTNASTY

W językach naturalnych występuje pojęcie żargonów oraz form komunikacji niebezpośredniej.

Larry: Zgadza się. To prowadzi do pojęcia zasięgu leksykalnego, który okazuje się bardzo istotny dla zarządzania różnorodnością językową w Perlu 6. Jest bardzo ważne dla nas w każdym punkcie leksykalnego zasięgu, abyśmy dokładnie wiedzieli, jakim językiem mówimy. Może nie do końca dlatego, że jest to tak istotne dla ludzi — chociaż rzeczywiście jest — ale ludzie są wystarczająco inteligentni, aby potrafili to wywnioskować. Kompilator nie jest równie inteligentny, dlatego kluczowe znaczenie dla kompilatora ma śledzenie tego, który język parsuje. Jeśli uda nam się tak zaprojektować kompilator, by się nie gubił, to sądzę, że ludzie również nie będą się gubić. Różne zasięgi leksykalne, różne akapity w literaturze, różne ramki, jak nazywają je w psycholingwistyce — używamy do tego różnych terminów. Ludzie cały czas przełączają kontekst. Wiedzą, kiedy mogą używać języka potocznego, a kiedy powinni mówić oficjalnie. Ludzie przez cały czas dostosowują swój język, nawet nie zdając sobie z tego sprawy. To jest część przybornika języka naturalnego. Skąd pan wie, czy problem, który próbuje pan rozwiązać, wymaga do rozwiązania narzędzia, czy języka?

Larry: To napraw dę rozm yta granica. Pojęcie pojedynczego narzędzia zlewa się z pojęciem zestawu narzędzi. Przypuszczam, że można patrzeć na język jako na zbiór narzędzi, a zatem nie jest to trudne i szybkie rozgraniczenie. Czy szwajcarski nóż jest jednym narzędziem, czy wieloma? Być może ważniejszym rozgraniczeniem jest to, jak dobrze zbiór narzędzi do siebie pasuje. Nóż szwajcarski może być przydatnym zbiorem narzędzi, ale raczej trudno używać różnych części tego noża jednocześnie. Uważam, że języki różnią się od narzędzi czy naw et zestawów narzędzi tym, jak uniwersalne mogą być (choć oczywiście istnieją zarówno języki specjalizowane, jak i języki ogólnego przeznaczenia). Choć języki m ożna postrzegać jako zwykłe narzędzia, są one doskonałe do łączenia różnych myśli w trudny do przewidzenia sposób. Jeśli problem wymaga takiej kompozycji i nie szkodzi m u zbytnio liniowa natura języka, zdefiniowanie języka powinno być dobrym wyborem dla rozwiązania specyficznego problemu — lub przynajmniej pewnego sensownego podzbioru problemu. Oprócz tego, że stanowi rozwiązanie dla pierwotnego problemu, język może spełniać rolę warsztatu pozwalającego na łatwe produkowanie innych narzędzi. Jeśli uda mu się spełnić tę rolę, to usprawni nasze życie w sposób trwały. Czasami zdarza się, że jeśli wiemy to zawczasu, decydujemy się na opracowanie języka nawet wtedy, kiedy nie jest to najszybszy sposób rozwiązania pierwotnego problemu. Jeśli jednak ktoś jest Naprawdę Leniwy, znajdzie sposób amortyzacji dodatkowych wysiłków, ponieważ zostaną one wynagrodzone dodatkowymi zastosowaniami języka.

PERL

471

W jakim stopniu zmienia się język, gdy zaczyna odchodzić od języka specjalizowanego? Wcześniejsze wersje Perlą można śmiało scharakteryzować jako oczyszczone dialekty Uniksa zaprojektowane jako API do klejenia ze sobą różnych mechanizmów. Jak bardzo zmienia się język, gdy przechodzi od narzędzia specjalnego przeznaczenia do zastosowań bardziej uniwersalnych?

Larry: Z mojego doświadczenia wynika, że jeśli mamy tego rodzaju język dziedzinowy, to w ystępują w nim różnego rodzaju konstrukcje sprawiające bardzo naturalne wrażenie, ale które w pewnym sensie są zdefiniowane ad hoc. Wydają się one bardziej racjonalne, niż rzeczywiście takie są. Czy to ludzki mózg próbuje poszukiwać związków pomiędzy różnymi elementami?

Larry: Tak. Kiedy to się zdarza, ludzie wyciągają wnioski, które niekoniecznie są słuszne. Prowadzi to do powstawania rozbudowanych list FAQ (od ang. Frequently Asked Questions — często zadawane pytania). Jeśli przyjrzeć się zbiorowi pytań FAQ dla Perlą, można znaleźć w nim kilka pytań, które stanowią dowód na istnienie procesu, który m ożna nazwać fałszywym uogólnianiem. Kiedy rozwijamy język do postaci narzędzia ogólnego przeznaczenia, wracamy do wszystkich tych miejsc i zadajemy pytania. Dlaczego ludzie stosują tego rodzaju uogólnienia? Czy powinny one znaleźć się w języku? Jakie są m inim alne zmiany, które możemy wprowadzić do sposobu obecnego działania języka, aby zaczął on działać tak, jak spodziewają się tego użytkownicy, a nie inaczej? W łaśnie takim procesem jest projekt Perlą 6. W taki sposób myślimy od kilku ostatnich lat. Czy może pan podać jakiś przykład?

Larry: W Perlu 5 $var jest wielkością skalarną, @var jest tablicą, a ^var tablicą asocjacyjną. Nowi użytkownicy uogólniają te zasady i sądzą, że indeksowane postacie należy zapisywać jako @var[$index] i ^foo{$key}. Jednak z historycznych względów w Perlu 5 tak to nie działa. W dokum entacji stosowaliśmy różne wybiegi w celu wyjaśnienia, dlaczego nie jest tak, jak ludzie oczekują. W Perlu 6 zdecydowaliśmy, że lepiej poprawić język, niż próbować poprawiać użytkowników. W Perlu 5, w różnych funkcjach w przypadku braku jawnego argumentu występuje domyślnie argument $ (bieżący temat). W zasadzie trzeba zapamiętać listę funkcji, w których tak jest. Jeśli nie zapam iętam y tej listy, niepraw idłow o uogólnimy, że własność ta dotyczy wszystkich funkcji. W Perlu 6 zdecydowaliśmy, że żadna z funkcji nie będzie domyślnie m iała tej własności. W związku z tym nie m a już niebezpieczeństwa fałszywego uogólniania. Zamiast tego występuje lekka, ale jawna składnia wywołania metody dla bieżącego tematu. Za każdym razem, kiedy język zmusza do zapamiętania jakiejś listy, może się zdarzyć, że ktoś źle założy, że coś powinno znaleźć się na liście, a w rzeczywistości tak nie jest,

472

ROZDZIAŁ

PIĘTNASTY

lub odwrotnie (będzie myślał, że danego elementu nie ma na liście, a on tam będzie). Takie elementy często wkradają się do projektu. Kiedy społeczność Uniksa po raz pierwszy wynalazła składnię wyrażeń regularnych, było to zaledwie kilka metaznaków, a zatem lista wyrażeń regularnych była łatwa do zapamiętania. W miarę dodawania coraz to nowych własności do m echanizmu dopasowywania wzorców ludzie albo używali więcej symboli ASCII niż metaznaków, albo — w celu zachowania wstecznej zgodności — używali dłuższych sekwencji, które wcześniej były niepraw idłow e. Nic dziwnego, że w efekcie powstał chaos. Zjawisko fałszywego uogólniania można znaleźć w wielu programach. Spanikowani użytkownicy stosują lewy ukośnik przed wszystkimi symbolami w wyrażeniu regularnym, ponieważ nie potrafią zapamiętać, które znaki są rzeczywiście metaznakami. W Perlu 6, podczas refaktoryzacji składni dopasowywania wzorców, zdaliśmy sobie sprawę, że większość symboli ASCII to i tak były metaznaki. W związku z tym zarezerwowaliśmy wszystkie niealfanumeryczne metaznaki, by ułatwić programistom zapamiętanie. Nie ma już listy metaznaków. Dzięki temu składnia jest znacznie bardziej przejrzysta. Do opisania projektu Perlą często używam słowa „synkretyczny". Wybieramy różne fragmenty z innych miejsc i próbujemy połączyć je w spójną całość. W ja k i sposób równoważy pan synkretyzm z uniwersalnością pomysłów oraz spójnością pomiędzy pomysłami a własnościami?

Larry: Można by powiedzieć, że źle. Smutną rzeczywistością dla projektantów języka jest to, że ta równowaga może wynikać tylko z ich wewnętrznych odczuć. Z doświadczeń historycznych również nie można wyciągnąć odpowiednich wniosków.

Larry: Zgadzam się, przynajmniej w ogólnym zarysie. W przypadku Perlą mieliśmy rzadki przywilej istnienia pomyślnego eksperymentu, jakim był Perl 5. Dzięki temu mogliśmy rozpocząć inny eksperyment znany jako Perl 6. Obecnie aktywnie poszukujemy innego rodzaju równowagi. Tym razem na podstawie tego, czego się nauczyliśmy. Oczywiście przyjęto wiele błędnych założeń. Jeśli nie w odniesieniu do zestawu własności, to przynajmniej w odniesieniu do tego, jakie powinno być domyślne działanie określonej własności. Tym razem chcemy popełniać innego rodzaju błędy. Ciekawe będzie przekonanie się, czy to się sprawdzi. Kiedy uczyliśmy wcześniejszych wersji Perlą, musieliśmy sporo wyjaśniać — dlaczego niektóre mechanizmy musiały być takie, jakie były. Teraz musimy powrócić do tego i powiedzieć: „Cóż, to, jak wcześniej myśleliśmy, było złe. Co oznacza, że to, jak teraz myślimy, także jest złe” . Interesującym problem em kulturow ym jest to, czy i do jakiego stopnia m ożna wyprowadzić ludzi z miejsca, do którego ich zaprowadziliśmy. Użyję metafory biblijnej: zaprowadziliśmy ich do Egiptu, a teraz próbujem y zaprowadzić ich z pow rotem do Ziemi Obiecanej.

PERL

473

Niektórzy ludzie za nami podążą, a inni zatęsknią za porem i cebulą.

Społeczność Zawsze zachęcał pan społeczność do uczestnictwa w projektowaniu i implementacji. Czy to było konieczne ze względu na wykonanie zadań? Czy to jest odzwierciedlenie pana stylu pracy, czy też pańskie poczucie estetyki?

Larry: Jestem pewien, że to musi być kombinacja czynników. Oczywiście pierw otną motyw acją do budow ania społeczności jest nakłonienie użytkowników do przekazywania pozytywnych i negatywnych uwag. Przyjemniej jest, gdy są to uwagi pozytywne, ale negatywne uwagi także pomagają. Z perspektywy lingwistycznej język który ma bardzo nieliczną społeczność, nie ma najlepszej przyszłości. Jest dość oczywiste, że społeczność rzeczywiście pomaga w żywotności projektu. Co więcej, ja chciałem, aby moje oprogramowanie było używane przez wielu ludzi, ponieważ lubię pomagać ludziom. W miarę upływu czasu projekt rozrósł się do takich rozm iarów , że sam także potrzebow ałem pomocy. Pod koniec pracy nad Perlem w wersji 4 zdałem sobie sprawę, że projekt rozchodzi się w różnych kierunkach, a użytkownicy kom pilują różne wersje program u wykonywalnego Perlą. W tym momencie stało się oczywiste, że będzie potrzebny mechanizm modularnych rozszerzeń oraz że za rozwój różnych m odułów będą odpowiedzialne różne osoby. Niedługo po opublikowaniu Perlą 5 stało się oczywiste, że sam Perl bardzo się rozrósł. Rozrósł się do tego stopnia, że żadna pojedyncza osoba nie była w stanie przez dłuższy czas spełniać roli menedżera integracji (nie mówiąc już o roli projektanta) bez obawy o całkowite wypalenie. To zadanie należało przekazać innym ludziom. Właściwie już w początkowej fazie projektu Perlą 5 zdałem sobie sprawę, że muszę nauczyć się delegować zadania. Niestety największy problem, związany z tą sytuacją, polega na tym, że nie mam charakteru kierownika. Nie potrafię delegować zadań. W związku z tym pozbyłem się naw et obowiązku delegowania, co jak się wydaje, dość dobrze się sprawdza. Nie zlecam ludziom wykonywania zadań. Inni ludzie przystępują do działania i wywierają odpowiedni wpływ na kierownictwo. W ten sposób powstają listy zadań do wykonania, tak też koordynowane są działania innych osób. Interesujące jest to, że pomimo mojego braku zdolności do mikrozarządzania społeczność jest pod pewnymi względami zdrowsza. Możliwe, że posiadam jedną umiejętność związaną z zarządzaniem: nigdy nie waham się powiedzieć ludziom, co powinni robić. Choć zwykle mówię w tak abstrakcyjny sposób, że ludzie nie mają nawet mglistego pojęcia, o czym mówię.

474

ROZDZIAŁ

PIĘTNASTY

Powiedział pan, że nie jest zainteresowany tym, żeby być menedżerem, oraz że nie ma w tym kierunku zdolności. Pomimo to przewodzi pan społeczności Perlą. Czy to jest pańskie najważniejsze zajęcie, czy też chce pan, aby w społeczności panowały dobre stosunki, i po prostu robi pan to, co jest konieczne?

Larry: Chcę utrzymać dobre stosunki w społeczności na nieco głębszym poziomie, niż miał na myśli Rodney King. Uważam, że moim zadaniem jest reagowanie, gdy dzieje się coś złego, i wywieranie pewnej presji, tak aby nie było jeszcze gorzej. Niezbyt często dochodzi do sytuacji, w której podejmuję drastyczne działania. Tylko raz zdarzyło mi się zadzwonić do jednej z osób należącej do społeczności Perlą i nakrzyczeć na nią. Nawiasem mówiąc, zadziałało. Oczywiście gdyby moje zdolności przywódcze były większe, mógłbym zminimalizować niektóre szkodliwe prądy, które płyną przez pewne elementy kultury Perlą. Są takie miejsca w mieście Perlą, do których nie w arto wchodzić po zm roku, ale naw et najbardziej życzliwy, wszechwiedzący i wszechmocny sędzia nie ma możliwości, by ciągle wszystkim powtarzać, co powinni, a czego nie powinni robić. Nawet Bóg tak z nami nie postępuje — w filmie Bandyci czasu Bóg mówi: „Myślę, że ma to związek z wolną wolą” . Jednym z największych sukcesów Perlą 5 jest repozytorium CPAN. Wydaje mi się, że jest ono podstawowym instrumentem rozszerzalności. Czy podjął pan specjalne decyzje projektowe, które zachęciły do powstania takiego mechanizmu, czy też był to tylko szczęśliwy przypadek w historii?

Larry: Tak jak zwykle w przypadku tego rodzaju historycznych pytań, odpowiedź brzmi dwojako: „Tak, podjąłem takie decyzje” i „Nie, nie podjąłem ich” . Z projektu systemu modułowego pośrednio wynika to, że znajdą się osoby, które stworzą moduły i je opublikują, oraz że będą istniały repozytoria takich modułów. W innych językach również istnieją repozytoria różnego rodzaju oprogramowania wielokrotnego użytku. Oczywiście nie przewidywałem skali repozytorium oraz tego, do jak niezwykłych zadań użytkownicy zaprzęgną Perlą. Od samego początku zawsze dążyłem do tego, by przystosować Perlą do kom unikow ania się z jak największą liczbą interfejsów API — za pośrednictwem powłoki, zm iennych środowiskowych, bezpośrednio systemu operacyjnego lub terminala. Byłem pierwszą osobą, która stworzyła parser XML. W różnych stadiach projektu zawsze było dla mnie ważne, aby Perl nie był językiem sam dla siebie, lecz by pozw alał łączyć się ze światem zew nętrznym na jak najwięcej sposobów. Na tym polega istota języka-kleju. Spróbujmy porównać to z takim językiem jak Icon, który próbuje zdefiniować wszystko wewnętrznie w odseparowany sposób. Nie chcę wszystkiego zbytnio upraszczać, ale w mniejszym lub większym stopniu wszystkie języki przynajmniej częściowo odkrywają koło na nowo.

PERL

475

Zawsze istnieje presja, aby rozwiązania były w 100% w Perlu lub w 100% w Javie, lub w 100% w jakimś bliżej nieokreślonym języku. To upraszcza takie zadania jak konfiguracja i testowanie. Dzięki temu dystrybucja staje się łatwiejsza. Łatwiej również zatrudniać programistów. Z drugiej strony, jeśli język w całości jest właśnie taki oraz jego kultura jest taka, to jest bardzo destrukcyjna forma. Musi istnieć równowaga. W Perlu zewnętrznych API było zawsze raczej za dużo niż za mało. Najlepiej jednak wspierać obydwa podejścia. Ja wbudowałem w Perlą filozofię możliwości połączeń oraz pragmatykę. Kiedy pojawiła się sieć WWW, nie spodziewałem się, że przyjmie ona taką skalę. Perl napraw dę rozwinął się do rozmiaru, którego nigdy nie przewidywałem, ale w pewnym sensie wynikało to pośrednio z jego projektu. Celowo umieścił pan mechanizmy, które umożliwiły przypadkowy rozwój repozytorium lub wykorzystanie go w sytuacji, gdyby powstało.

Larry: W pewnym sensie słowa „celowo”, które może oznaczać różne rodzaje zaplanowanych i przemyślanych działań. Zwykle projekty powstają z kilku różnych pom ysłów rodzących się w różnych częściach mózgu. Wiele z nich narodziło się na zewnątrz mojego mózgu w tym sensie, że duża część Perlą od samego początku powstawała w umysłach innych osób. W wielu językach występują repozytoria bibliotek, ale CPAN ma tę przewagę, że jego twórcy (Jarkko, Andreas i inni) zbudowali wystarczającą strukturę do tego, by zachęcała do tworzenia i by nie ograniczała rozwoju. Czy stało się to możliwe dzięki własnościom języka, czy dzięki cechom społeczności, którą pan zainspirował do działania?

Larry: Nie mogę brać odpowiedzialności za dobre wybory dokonane przez twórców repozytorium CPAN (za złe również nie odpowiadam). Myślę jednak, że CPAN dotarł do czułego punktu zastosowania prawa Sturgeona („90% wszystkiego jest w stanie surow ym ”). Zwłaszcza kiedy tworzymy prototyp czegoś nowego, zdarza się, że przesadzamy z projektem i próbujemy wyrzucić większość z 90% kodu. W efekcie wyrzucamy większość z pozostałych 10%. Oczywiście zdarza się, że ten surowy kod później rozwija się i przestaje być surowy, dlatego często opłaca się być cierpliwym i przyjąć podejście „im gorzej, tym lepiej’. Niektórzy ludzie po prostu mają braki w wiedzy. Uczymy się podczas realizacji projektu. Jeśli zaś chodzi o własności języka, które to umożliwiły, trzeba powiedzieć tak: umożliwienie użytkownikom rozszerzania języka za pomocą modułów było jednym z najważniejszych celów projektow ych Perlą 5. Z perspektywy prac nad Perlem 6 muszę przyznać, że sknociłem projekt modułów Perlą 5 pod pewnymi względami, ale był on Wystarczająco Dobry, a repozytorium CPAN jest tego rezultatem. Przypuszczam, że powiedziałem też kilka rzeczy, które zachęciły społeczność do udziału w tym przedsięwzięciu, ale tylko w odniesieniu do najbardziej ogólnych pojęć. Jednak w większości wszystko i tak sprowadza się do tego, że większość programistów Perlą

476

ROZDZIAŁ

PIĘTNASTY

to patologicznie uczynni ludzie. Trochę pom achałem chorągiewką na początku, aby pom óc w zainspirow aniu procesu. Ludzie po prostu dołączyli do kom panii i pozostali, bo spotkali w niej bratnie dusze. Mam wielką satysfakcję, że duch kooperacji rozprzestrzenił się również na inne społeczności. Czy wkład społeczności był zaskakujący?

Larry: Nie wiem, czy naprawdę taki był, czy nie. Myślę, że cała kultura obowiązkowej ścisłości i ostrzeżeń była dla mnie pewnym zaskoczeniem — ludzie proszący o więcej dyscypliny niż domyślnie. Stało się to akceptowalne kulturow o do tego stopnia, że zdecydowaliśmy się na wbudowanie tych mechanizmów do Perlą 6. To było pewne zaskoczenie. Największe zaskoczenie przyszło w chwili, kiedy przystąpiliśmy do przeprojektow yw ania Perlą dla Perlą 6. Poprosiliśmy o sugestie w postaci dokumentów RFC (ang. Request For Change). Spodziewałem się, że otrzymam może ze 20. Otrzymaliśmy 361. Częściowo zaskoczeniem było to, ile problem ów występowało w Perlu — około 15 razy więcej, niż się spodziewałem. Jeszcze bardziej zaskoczyło mnie to, ile różnych rzeczy, o których użytkownicy myśleli, że można je poprawić w odosobnieniu, w rzeczywistości było niemożliwych do poprawienia w ten sposób. A zatem największym zaskoczeniem ze strony społeczności w ciągu ostatnich 20 lat była dla mnie potrzeba systematycznej modyfikacji projektu języka. Byłem również zaskoczony (pozytywnie) sukcesem niektórych rozwiązań stworzonych przez społeczność. Pierwotne rozwiązanie podwójnego licencjonowania powstałe w Perlu 3 postawiło ten język w bardzo dobrym świetle zarówno wśród społeczności program istów obawiających się kultury korporacyjnej, jak i w środowisku korporacyjnym , które obawiało się społeczności programistów. Obie społeczności znalazły wsparcie w tym podejściu. Nie musiałem nawet zmuszać nikogo do podejm ow ania decyzji o tym , czy w ykorzystywana licencja to GPL, czy też Licencja Artystyczna. Raczej nie widziałem, żeby ktoś podejmował decyzję o tym, z której licencji będzie korzystał.

Larry: Zgadza się. Była to superpozycja licencji, której ludzie nawet nie zauważali. Zaskoczyło mnie negatywnie, że określono to terminem podwójnego licencjonowania i niektórzy interpretowali to jako konieczność wyboru jednej lub drugiej licencji. Nigdy nie m ożna wybrać określenia i spowodować, żeby oznaczało to, co ma się na myśli. Zawsze występuje taniec pomiędzy tym, co się myśli, że m ożna uzyskać, a tym, co się faktycznie uzyskuje.

Ewolucja i rewolucja Jakie podejście do projektowania i tworzenia oprogramowania pan preferuje: ewolucję czy rewolucję?

Larry: Pod pewnymi względami jestem misiem o bardzo małym rozumku, dlatego kiedy programuję, to przyjmuję podejście ewolucyjne. Podczas tworzenia programu w Perlu zwykle w prow adzam zmiany, urucham iam program , potem znów wprowadzam zmiany. Taki cykl trwa jakieś 30 sekund. Nie poświęcam zbyt wiele czasu na debugowanie, ponieważ zazwyczaj jest dość oczywiste, czy to, co zrobiłem ostatnio, jest prawidłowe, czy nieprawidłowe. Co jakiś czas refaktoryzuję kod, ale to również są zmiany ewolucyjne — naprzemiennie wprowadzam zmiany i sprawdzam, czy program działa dalej tak samo. Jeśli chodzi o projekt języka, to mój podstawowy sposób działania zawsze był podobny: podejście ewolucyjne, ale z szybkim tempem mutacji. Dzięki temu, jeśli ktoś pobierał migawki dość odległe od siebie, odnosił wrażenie, że zaszły rewolucyjne zmiany. W oczach miłośników Uniksa Perl 1 wyglądał jak radykalna modyfikacja narzędzi awk, sed i powłoki, ale dzięki temu większa część środowiska użytkowników Uniksa przeszła na stronę Perlą. Przez to stał się on możliwy do zaakceptow ania przez społeczność. Dla każdego nowego języka istotne znaczenie ma migracja, zatem nowe języki zwykle zapożyczają wiele elementów z istniejących języków (czasami żałowaliśmy faktu zapożyczenia niektórych elementów; w szczególności składnia wyrażeń regularnych pogorszyła się z biegiem lat; miejmy nadzieję, że to poprawi się w Perlu 6). Dla wielu osób Perl 5 wyglądał tak jak rewolucyjna zmiana w stosunku do Perlą 4, ale w rzeczywistości implementacja ewoluowała przez szereg form pośrednich, których świat zewnętrzny nigdy nie zobaczył. W pewnym momencie niektóre instrukcje były interpretow ane przez stary interpreter działający w trybie stackful, natom iast inne przez nowy interpreter działający w trybie stackless. Perl 5 w dalszym ciągu był bardzo zachowawczy pod względem zgodności wstecz. Perl 5 pozwala na praw idłowe uruchomienie większości skryptów napisanych w Perlu 1. W przypadku Perlą 6 ostatecznie zdecydowaliśmy się na przełom w zakresie zgodności — wyrzucenie prototypu, jakim były wcześniejsze wersje Perlą, i szybką modyfikację projektu składniowego i semantycznego przy jednoczesnej próbie zachowania odczucia, że Perl działa w taki sposób, w jaki zawsze działał. Tym razem jednak właściwie podszedłem do zadania zaangażow ania społeczności w przyrostow y proces projektow ania. Kiedy po raz pierwszy ogłosiliśmy rozpoczęcie prac nad projektem Perlą 6, otrzym aliśm y 361 dokum entów RFC. Większość z tych dokum entów zakładała przyrostow ą zmianę w stosunku do Perlą 5 bez w prow adzania żadnych innych modyfikacji. W pewnym sensie projekt Perlą 6 to po prostu efekt podsum owania,

478

ROZDZIAŁ

PIĘTNASTY

uproszczenia, ujednolicenia i racjonalizacji tych przyrostowych sugestii. Faktyczne przejście z Perlą 5 do Perlą 6 będzie jednak odczuwane jako rewolucyjna zm iana dla kogoś, kto nie uczestniczył w procesie projektow ania. Pomimo to większość programów w Perlu 6 będzie wyglądała dość podobnie do kodu Perlą 5, ze względu na podobieństw o procesu projektowego. Jednocześnie Perl 6 znacznie ułatw ia przejście na funkcyjny lub obiektowy sposób myślenia. Niektórzy uznają tę zmianę za rewolucyjną. W edług m nie zm iany rewolucyjne to z reguły sytuacje, w których użytkownicy nie chcą przechodzić przez wszystkie etapy pośrednie. Perl został zaprojektowany w taki sposób, aby pom óc użytkow nikom w przejściu przez pośrednie etapy w jak najsprawniejszy sposób. Dzięki tem u zm iany sprawiają wrażenie rewolucyjnych. To dosyć zabawne. Co to za rewolucja?

Larry: Mówię tu o prywatnych rewolucjach. Taka rewolucja zachodzi, kiedy ktoś, kto nie zna Perlą, zwraca się do osoby programującej w Perlu: „Próbuję to zrobić i nie wiem jak” . A programujący w Perlu odpowiada: „O, to proste. Popatrz” . I pisze niewielki program, który jest zarówno oczywisty, jak i odpowiednio szybki. Praca zostaje w ykonana, a pytający mówi: „Świetnie” . Zawsze, kiedy ktoś tak mówi, ma miejsce mała rewolucja. W pewnym sensie płynne przejście od ewolucji do rewolucji polega na tym, jak głośno powiemy „Świetnie” — lub jeśli jesteśmy niezadow oleni — „Fatalnie” . W dobrej rewolucji więcej osób krzyczy „Świetnie” niż „Fatalnie” . Wierzę, że naprawdę możliwe jest istnienie dobrych rewolucji. Być może w pewnym, osobistym stopniu ma to związek z teologią. Wierzę, że jeśli ludzie mają odpowiedni bodziec, potrafią drastycznie zmienić orientację w krótkim czasie. Niezależnie od tego, czy jest to zamierzone, czy nie?

Larry: Tak. To tak jak różnica pomiędzy współczesnymi naukow cam i a greckimi filozofami, którzy próbow ali wszystko wywnioskować z podstaw ow ych zasad. Współcześni naukowcy mają trochę więcej wiedzy, ale bez doświadczalnego testowania nie dojdzie do przypadkowego odkrycia — czegoś, co uderza w tył głowy i całkowicie odwraca sposób naszego myślenia. Na przykład Perl 5 w internecie?

Larry: Między innym i Perl 5 w internecie. Także wiele pośrednich form, które przedostały się na ląd lub wpadły w otwory po upadku asteroid, albo inne powstałe sytuacje, których istnienie przeczyło rodowodowi. Po prostu istnieją ogólne zasady, na które należy być otw artym — zarów no na niewielkie, stopniow e zmiany, jak i na olbrzymie przełomy.

PERL

479

Czy te wielkie przełomy można było przewidzieć? To przypomina mi rachunek lambda, kiedy wychodzimy od czterech bądź pięciu osobnych zasad, a następnie wnioskujemy i dedukujemy w kierunku przydatności zasady zupełności Turinga.

Larry: Tak, ale rzeczywistość zazwyczaj nas zaskakuje zjawiskiem zupełnie innej skali w porów naniu z tym, czego oczekiwaliśmy. Ewolucja może dotyczyć utrzym ania aktualnej tem peratury ciała organizm u w bardzo łagodnym klimacie, ale czasami spadają asteroidy o rozmiarze XXXL i wszystko odw racają do góry nogami. Indukcja pom aga w przypadku stopniow ych zmian. Niekoniecznie jest pom ocna w przewidywaniu zmian wtedy, gdy zapadnie się większa część gruntu, jaki mamy pod sobą. Wtedy warto mieć obszerną pulę genów. Geny są narzędziami w naszym przyborniku. Chcemy mieć ich dużo — zwłaszcza w obliczu zagrożenia ze strony asteroidów. Chce pan dać użytkownikom narzędzia, które pomogą przystosować się do nowych okoliczności, jeśli ludzie ci w nich się znajdą.

Larry: Znowu cały problem sprowadza się do porównania elementów niezmiennych ze zmiennymi. Indukcja bazuje na założeniu, że nasz teren jest niezmienny. Indukcja czy dedukcja?

Larry: Jedno i drugie. Myślę, że to dwie strony tej samej monety, a czasami moneta zatrzymuje się na krawędzi. W teorii praw dopodobieństw a zakłada się, że prawdopodobieństwo takiego zdarzenia wynosi zero. Ale tak nie jest. Nawet mnie udało się coś takiego. Była to jedna z najbardziej niezwykłych rzeczy, jakie mi się przytrafiły. Byłem dzieckiem i grałem w futbol z moim sąsiadem. Rzucaliśmy monetą. Zapytał: „Co wybierasz?” . Odpowiedziałem: „Krawędź” . I m oneta w ylądow ała na krawędzi, ponieważ spadła pomiędzy dwa sztywne źdźbła trawy. Czasami kontekst powoduje zmiany w teorii prawdopodobieństwa. Myślę, że historię mojego życia zdeterm inow ał wybór krawędzi i częściowo była to trafna decyzja. Jak pan uzasadnia sens istnienia wielu potencjalnie rywalizujących ze sobą lub współpracujących implementacji Perlą 6?

Larry: Uzasadnienie ich istnienia składa się z kilku części. W spom inaliśm y już, że chcemy dysponować obszerną pulą genów. Zdrowa pula genów wymaga wymiany wielu genów, a to może być zabawne. Inny pow ód jest taki: różne implementacje przyczyniają się do większej rzetelności. Różni ludzie w naturalny sposób inaczej postrzegają specyfikację. Jeśli istnieją jakieś rozbieżności w specyfikacji, to istnieje prawdopodobieństwo ich wykrycia. Następnie pozostaje kwestia negocjacji pomiędzy różnymi im plem entacjam i w zakresie tego, co specyfikacja napraw dę pow inna oznaczać.

480

ROZDZIAŁ

PIĘTNASTY

Brzmi to tak ja k strategia rodziców: „Najpierw przecinamy ciasto na połowę, a następnie twój brat wybiera ten kawałek, który chce".

Larry: Zgadza się. Jest to sposób narzucenia pewnego projektu osobom im plem entatorom . To konieczność w przypadku kiepskich projektantów , takich jak ja. Strategia w ykorzystania wielu im plem entatorów m a też inny aspekt: otóż zainteresowania różnych ludzi bywają odmienne. Ludzie najpierw tworzą prototypy różnych fragm entów implementacji. Może się zdarzyć, że po napisaniu połow y im plem entacji odkryjemy, że przyjęliśmy założenia, które bardzo utrudniają zaimplementowanie drugiej części. Z tego względu jest lepiej, gdy różni ludzie zajmują się prototypami różnych części projektu. Dzięki temu mogą wymieniać się informacjami na temat niebezpiecznych punktów. N iedaw no mieliśmy do czynienia z podobną sytuacją przy im plementacji SMOP. Programiści biorący udział w tym przedsięwzięciu nie zrealizowali zbyt wielu zadań implementacji, ale opracowali bardzo szczegółowe zasady niskopoziom ow ych współzależności pomiędzy listami, mechanizmami przechwytywania (ang. captures), sygnaturami oraz wszystkimi kluczowymi pojęciami. Pracują także nad semantyką iteratorów, tablic i innych konstrukcji. Jest to jedno z naukow ych uproszczeń: ignorujem y pozostałą część problem u i analizujem y tylko pewien jego aspekt. Myślę, że to bardzo cenne, ponieważ zmusza nas do przemyślenia projektu w miejscach, których do tej pory jeszcze nie analizowaliśmy. Czy jest tak dlatego, że mają one stać się pełnymi implementacjami, czy też dlatego, że ktoś tworzy prototypy specyficznych części?

Larry: W zasadzie prawie mnie to nie interesuje. Jeśli ktoś tworzy prototyp fragmentu implementacji, to napraw dę jest jego sprawa, jak szczegółowy to będzie prototyp. W szystko zależy od tego, ile ma energii, ilu innych ludzi może zaangażować do przedsięwzięcia. Jest to kolejny przykład powierzania zadania delegowania zadań innym osobom. Odkryto nowy kontynent i ludzie muszą go poznawać pod każdym względem. Nie daje pan błogosławieństwa je d n ej implementacji, a zachęca pan do eksperymentowania.

Larry: Tak. Zachęcamy do stosowania algorytmu drzewa rozpinającego (ang. flooding algorithm). Nie jest to algorytm, który chcą realizować korporacje. W korporacjach zwykle opracowuje się projekt, w którym określa się jeden główny cel. Narzuca się określone tem po wytw arzania, a projekt ma być zakończony do określonej daty. Jednak w naszym przypadku mamy do dyspozycji ten wspaniały współbieżny procesor, jakim jest społeczność open source. Algorytmy drzewa rozpinającego skalują się lepiej

PERL

481

na sprzęt równoległy niż na sprzęt szeregowy. Nie jest to jednak głupi algorytm drzewa rozpinającego, ale raczej działanie przypominające kolonię mrówek poszukujących pożywienia. A zatem w zasadzie optym alizujem y proces dla silnika, na którym będziemy działać. Nie jest to symetryczna architektura wieloprocesorowa, ale struktura, która bardziej przypomina klaster programistów. Nie wszyscy ci programiści mają tę samą architekturę. Problemem jest również współdzielona pamięć — zwłaszcza kiedy stan jest tak bardzo niezmienny.

Larry: Tu nie chodzi tylko o pamięć — społeczność open source nie jest architekturą do wszystkiego. Myślę jednak, że nasze podejście pow inno polegać bardziej na wykorzystaniu jej mocnych stron niż słabości. I z całą pewnością współdzielona pamięć nie wydaje się być zbyt poważnym problemem u mrówek. Czy jakaś konkretna implementacja Perlą 6 ogranicza to, co się dzieje na wyższych poziomach semantycznych?

Larry: Nawet jeśli spróbujemy stworzyć nowomowę, to żaden z języków nie pozwala na całkowitą kontrolę nad wszystkim, a implementacje to tylko jeden z elementów, nad którym i język nie może mieć całkowitej kontroli. Do tego stopnia, do jakiego próbujem y zachować kontrolę, definicja języka napraw dę sprowadza się do tego, co umieścimy w zestawie testów lub z niego wyłączymy. Myślę, że tego rodzaju decyzje powinny być głównie wynikiem negocjacji pomiędzy różnymi twórcami implementacji ze sporadyczną ingerencją projektanta języka. Jest to jeden z powodów, dla których uważam y za istotne posiadanie wielu implementacji. Zazwyczaj bowiem różne implementacje wygładzają sobie wzajemnie ostre krawędzie. Czy w jakiś konkretny sposób pana zespół zajmuje się dostępnością zasobów?

Larry: Myślę, że dzięki temu, że wysiłki są rozproszone w wielu miejscach, uzyskujemy efekt samoograniczania się. Kiedy zespół programistów rozrośnie się do liczby od sześciu do dwunastu osób, zwykle i tak dzieli się na kilka podprojektów. Naprawdę problem zasobów występuje tylko wtedy, gdy różni członkowie zespołu starają się pracować identycznie jak inni — nie patrząc na pracę innych. Jeśli robią to inaczej, to jest interesujący eksperym ent poszerzający pulę genów. Oczywiście każde podejście okazuje się w pewnym sensie niewydajne. Niezależnie od tego, czy zadanie jest wykonyw ane przez wielu program istów równocześnie, czy jest to coś, do czego powracamy później, ponieważ nie możemy zrobić wszystkiego od razu. Niektórzy uważają, że rozproszenie niewydajności na wielu programistów gwarantuje szybsze wykonanie zadania niż w przypadku sposobu szeregowego. Oczywiście przy założeniu, że m am y wystarczającą liczbę program istów, którzy m ogą się zająć problemem, że problem da się zrównoleglić oraz że uda nam się znaleźć ochotników, którzy będą ze sobą rozmawiali.

482

ROZDZIAŁ

PIĘTNASTY

Ochotnikom nie można powiedzieć, co mają robić. Co prawda można próbować, ale to nie działa.

Larry: To inna w ażna sprawa. O chotnicy i tak będą robić to, co chcą. A zatem, tak jak mówi przysłowie, jeśli nie możesz czegoś poprawić, zrób z tego funkcję. Bardzo podoba mi się cytat z pańskiej wypowiedzi: „Mówią, że im gorzej, tym lepiej, jednak w projekcie Perl 6 mamy nadzieję na jeden cykl »im lepiej, tym lepiej«".

Larry: To prawda. To jest moje obecne skrajne wyzwanie. Mam nadzieję, że czuje pan jego skrajność.

PERL

483

484

RO ZDZIAŁ

PIĘTNASTY

ROZDZIAŁ

SZESNASTY

PostScript

PostScript to konkatenacyjny język programowania najczęściej używany do opisu publikacji DTP (ang. Desktop Publishing) oraz publikacji elektronicznych. Język został opracowany przez Johna Warnocka i Charlesa Geschkego, którzy w 1982 roku założyli firmę Adobe Systems. Dzięki wyprodukowaniu w 1 985 roku przez firmę Apple drukarki LaserWriter z obsługą języka PostScript stosowanie techniki DTP stało się możliwe. Język PostScript szybko stał się standardem de facto wymiany dokumentów. Później język PostScript został w yparty przez swojego następcę — form at PDF.

485

Zaprojektowany po to, żeby istnieć W ja k i sposób zdefiniowałby pan język PostScript?

Charles Geschke: PostScript to język program ow ania, którego podstaw ow ym celem jest dostarczanie wysokopoziomowego opisu zawartości (drukowanych) stron w postaci niezależnej od urządzenia. John Warnock: PostScript jest interpretowanym językiem programowania, który emuluje prostą maszynę w irtualną wykorzystującą stos. Oprócz standardow ych operatorów dostępnych w większości języków program ow ania PostScript zawiera bardzo obszerny zbiór operatorów do renderow ania obrazów, grafiki i czcionek. Dzięki językowi PostScript aplikacja może emitować polecenia PostScript w sposób niezależny od rozdzielczości. Polecenia te definiują wygląd strony (na wydruku bądź na ekranie). Myślę, że PostScript odniósł sukces dlatego, że jest uniw ersalny i posiada dobrze zdefiniow any model przetw arzania obrazów. Istnieją inne protokoły drukarek, które tworzą statyczną definicję stron z wykorzystaniem struktur danych. Protokoły te okazały się nieskuteczne w opisie nawet pewnych intuicyjnie prostych stron. Co panów skłoniło do stworzenia języka PostScript, a nie formatu danych? Dokument miał być interpretowany przez drukarkę, zatem ja ka jest istotna różnica pomiędzy językiem a formatem danych?

John: Kiedy przystępowaliśmy do pracy nad tym projektem, w firmie Xerox PARC funkcjonow ał język o nazwie JaM. Prowadziliśmy badania w dziedzinie grafiki i chcieliśmy stworzyć interpretow any język, w którym m ożna by było uruchomić szybkie eksperymenty. Miały one na celu wykonywanie szybkich prób i tworzenie interfejsu ze sprzętem i oprogram ow aniem kom puterów Alto. Interesowało nas wykonywanie takich prób bez konieczności przechodzenia przez skomplikowany cykl kompilacji oraz długotrwałe operacje ładowania i urucham iania programów. Skorzystaliśmy z języka interpretow anego, który okazał się bardzo skuteczny w próbowaniu wielu nowych pomysłów. Charles: Stworzenie języka programowania w dłuższej perspektywie dawało większe możliwości. Jeśli istnieje zbiór kluczowych własności języka, które nie działają wydajnie w trybie interpretow anym , to m ożna zakodować je do postaci zbioru rozszerzeń języka poprzez wprowadzenie nowych operatorów oraz zaimplementowanie ich na znacznie niższym poziomie. Dzięki temu będą one działały wydajniej. Idea języka wynikała z tego, że nie wiedzieliśmy, jakiego rodzaju urządzenia, jakie środowiska oraz — do pewnego stopnia — jakiego rodzaju nowe sytuacje powstaną w przyszłości, jeśli chodzi o sposób zarządzania i opisywania wyglądu drukowanych stron. Język zagwarantował nam elastyczność, której nigdy nie udałoby się osiągnąć w przypadku zastosowania struktur danych.

486

ROZDZIAŁ

SZESNASTY

Są w nim takie same operacje i instrukcje sterujące, które występują w innych językach zupełnych w rozumieniu Turinga, ale zgodnie z hipotezą Churcha-Turinga nie można go matematycznie udowodnić. John: Istnieje wiele egzemplarzy protokołów poleceń w naszej branży, które nie są w pełni językami programowania. Gdy powstawał język, ocenialiśmy, że sposób używania protoko łu zawsze ma charakter otw arty i nieznany. K om pletny język programowania stwarzał okazję do zaprogramowania tego, o czym zapomnieliśmy lub czego nie przewidzieliśmy. Ta kom pletność dała językowi PostScript długowieczność, której nie przewidywaliśmy. Jedna z zalet języka konkatenacyjnego polega na tym, że jeśli chcemy opracować nową własność, możemy zrobić to sprzętowo na niektórych platformach oraz możemy emulować j ą programowo dla starszych platform . Jeśli zdefiniujemy nowe słowo, możemy je zdefiniować w kontekście prymitywów innych słów w języku. Dzięki temu może ono działać na starszych maszynach. Jeśli jednak chcemy mieć obsługę tego słowa na poziomie interpretera, możemy dodać ją w nowszych wersjach. Czy to prawda?

Charles: Tak. John: My stworzyliśmy język w taki sposób, że nawet podstawowe operatory mogły być redefiniowane. Instrukcję add m ożna zdefiniować w języku PostScript w taki sposób, aby wykonywała dowolną operację. Dzięki tej elastyczności stało się możliwe stworzenie formatu PDF, ponieważ zdefiniowaliśmy podstawowe operatory graficzne do pobierania operandów ze stosu oraz odkładania ich w postaci statycznych struktur danych zamiast utrzymywania programowego charakteru języka PostScript. Czy częściowo celem tego działania było obejście potencjalnych błędów w ROM?

John: Zdecydowanie. Charles: D rukarka LaserW riter — kiedy pow stała — m iała największą ilość oprogram ow ania, jaką kiedykolwiek zapisano w pamięci ROM. Pół megabajta?

John: Tak. Czy w tamtym czasie była to standardowa praktyka?

John: Było to bardzo m ocno rozbudow ane oprogram ow anie jak na możliwości umieszczenia go w pamięci ROM. Jeśli znalazły się w nim błędy, to lepiej było zostawić jakieś wyjścia ewakuacyjne. Forma języka pozwalającego na oprogramowanie błędów była niezwykle użyteczna. Charles: Gdy tworzy się dowolny projekt tej skali, trzeba brać pod uwagę to, że będą w nim błędy. Nie można tylko zaciskać kciuków i liczyć na to, że ich nie będzie.

POSTSCRIPT

487

My zastosowaliśmy mechanizm pozwalający na instalowanie łatek. Nie mogliśmy sobie pozwolić, aby co miesiąc wymieniać pamięci ROM w dziesiątkach tysięcy lub nawet setkach tysięcy drukarek. John: Jeśli wziąć pod uwagę koszty pamięci ROM w tamtych czasach, nie można było sobie pozwolić na ich wymianę. Czy sprzęt w tamtych czasach stanowił problem — poza koniecznością posiadania jakiegoś sprzętu?

John: Nie. Pierwsza wersja języka pow stała dla firmy Evans & Sutherland. Budowaliśmy duże symulatory graficzne. Kiedy pojawiła się specyfikacja projektu, sprzęt nie był jeszcze zbudow any i m usieliśmy stworzyć dla niego bazy danych. Musieliśmy pozostawić sobie sporo miejsca na wykonanie późnego wiązania. Własność późnego wiązania jest bardzo ważna, ponieważ nie ma możliwości przewidzenia sposobu, w jaki będzie ewoluował sprzęt i na jakiej maszynie będzie docelowo działał. Charles: John, może opowiedz trochę o bazie danych. To był cały port w Nowym Jorku. John: To były wszystkie budynki M anhattanu — właściwie nie wszystkie budynki M anhattanu, ale horyzont budynków M anhattanu, Statua Wolności itp. Było to też szkolenie ludzi dotyczące w prow adzania tankow ców do po rtu Nowy Jork. Był to niezwykły projekt realizowany prawie przez rok. To bardzo dużo danych.

John: To była ogromna ilość danych, jeśli wziąć pod uwagę, że zasadniczym sprzętem był kom puter PDP 15. Mieliśmy do dyspozycji bardzo niewielkie maszyny. Miały tylko 32 kB pamięci. Drukarka laserowa to musiał być luksus.

John: Mimo że była to drukarka laserowa, był to jednocześnie największy procesor, jaki kiedykolwiek zbudowała firma Apple. Z ich punktu widzenia była to duża maszyna do przetwarzania danych. Poza tym wtedy już były interfejsy Mac do grafiki. Użyliśmy języka programowania PostScript w celu zbudowania interfejsu w pewnym sensie sympatyzującego z ich interfejsem graficznym. Dzięki tem u mogliśmy przetwarzać ich struktury danych i wykonywać równoważne operacje w języku PostScript. To były programy PostScriptowe. Komputery Mac miały wtedy zaledwie 256 kB lub 512 kB. Dawało to niezbyt dużą elastyczność operacji z takim i aplikacjami, jak Mac Draw czy W rite. Drukarka LaserWriter w istocie pobierała cały zbiór programów i interpretowała ich polecenia.

488

ROZDZIAŁ

SZESNASTY

Czy komputery Mac generowały wtedy PostScript?

John: Tak, generowały PostScript, ale był to rodzaj konwersji z języka Quick Draw. Charles: Zgadza się, generowały PostScript, ale za pośrednictwem zbioru procedur, które pobierały dane w formacie Quick Draw i przetwarzały je na PostScript. Te makra, czy też procedury, w zasadzie działały na drukarce. Czy w miarę wprowadzania obsługi większej liczby urządzeń dokonywali panowie zmian w języku PostScript?

John: Konieczność korzystania z drukarek laserowych stwarzała nam trudną sytuację. Ostatecznie przenieśliśmy język PostScript na drukarki igłowe. Charles: Nie był to zbyt satysfakcjonujący projekt. John: Nie, nie był. Jak to je st — myśleć w dwóch wymiarach (mam na myśli grafikę) od momentu powstania języka?

Charles: Trzeba było zaprojektować m echanizm y do obsługi dw uw ym iarow ych transform acji pozwalające programiście na zaprogram ow anie własnego układu współrzędnych. Ostatecznie jednak ta przestrzeń musiała być przekształcona na układ współrzędnych urządzenia. John: Sądzę, że łatw o jest myśleć w dw óch wym iarach, jeśli wyobrazimy sobie, że każda konstrukcja dwuwymiarowa jest rysowana (lub zobrazowana) przez procedurę. Jeden z sukcesów języka PostScript polega na tym, że każdy z obiektów lub zbiór obiektów m ożna opakow ać we w łasny system współrzędnych. W efekcie m ożna tworzyć obiekty na stronie o różnych rozm iarach i orientacjach, bez względu na szczegóły, które w nich występują. Dzięki tem u prostem u pom ysłow i łatw o jest myśleć o tym, jak stworzyć stronę lub jej część. Z wykształcenia są panowie matem atykami. W ja kim stopniu wykształcenie matematyczne pomogło panom podczas tworzenia języka PostScript?

Charles: Jeśli chodzi o zrozumienie logiki transformacji modelu obrazowania, było oczywiste zarówno dla mnie, jak i dla Johna, że tak właśnie ma to działać. Interesowało nas bowiem stworzenie mechanizmu liniowej transformacji wbudowanego wewnętrznie w modelu zobrazowania. To jest mechanizm specyficzny dla języka. Nie wiem, jak duży to miało wpływ, ale tak się składa, że miałem wiedzę nie tylko z matematyki, ale także bardzo dobrze znałem cały proces drukowania. Mój dziadek i ojciec zajmowali się przygotowyw aniem św iatłodruku dla pras drukarskich. Przyglądałem się ich pracy i zrozum iałem wiele zasad związanych z drukiem, w szczególności w zakresie generow ania półcieni i tem u podobnych zagadnień. Wiedza z różnych dziedzin bardzo pomogła mi podczas tworzenia języka PostScript,

POSTSCRIPT

489

ale nie powiedziałbym, że to m atem atyka była najważniejsza. Posiadanie jej w przyborniku zawsze jest niezwykle pomocne. Zwłaszcza kiedy mamy do czynienia z abstrakcyjnymi definicjami. W ja k i sposób udawało się scalać pomysły? Czy zdarzyło się kiedykolwiek, że panowie się ze sobą nie zgadzali? W ja k i sposób znajdowali panowie wspólne rozwiązania?

Charles: Zatrudniłem Johna w 1978 roku, zatem pracujemy razem już od 30 lat. W ciągu tego czasu nigdy nie wystąpiły pomiędzy nami żadne poważne konflikty. Zawsze mieliśmy wystarczający szacunek do wzajemnych poglądów. Jeśli w jakiś sprawach mieliśmy różne zdania, natychm iast staraliśmy się znaleźć powody, dla których występow ały różnice, i albo rozstrzygnąć, czyj pom ysł jest lepszy, albo znaleźć sposób integracji tych dwóch pomysłów. O ile pam iętam , różnice między nam i prawie nigdy nie doprow adziły do jakiejś poważnej kłótni. Pod tym względem jest to dość unikatow e partnerstw o. Bardzo niewielu osobom udało się tego doświadczyć w swoim życiu zawodowym. W ielu zdarzyło się coś podobnego w relacjach przyjacielskich, małżeństwie czy też innych relacjach, ale w środowisku zawodowym rzadko zdarza się taki poziom wzajemnego szacunku oraz zdolność tak szybkiego ujednolicania poglądów. Wiele osób porównuje język PostScript do języka Forth, ponieważ oba bazują na wykorzystaniu stosu. Czy język Forth wyw arł jakiś wpływ na postać języka PostScript?

John: Nie. Dyskusje o podobieństwie języka Forth do języka PostScript dotarły do nas po zakończeniu prac nad językiem PostScript w firmie E&S. Język Forth jest trochę podobny, ale pod wieloma względami oba języki bardzo się różnią pomiędzy sobą. A zatem nie można mówić o poważnym wpływie języka Forth na język PostScript. Podobieństwo wynika z czystego przypadku. Charles: W pełni się z tym zgadzam. Zawsze sądziłem, że istnieje jakiś związek. Wygląda jed n ak na to, że podobne wymagania prowadzą do podobnych projektów.

John: Świetną cechą języka PostScript jest możliwość jego zaim plem entow ania za pom ocą bardzo niewielkiej ilości kodu. W ynika to z em ulow ania środowiska sprzętowego. Bardzo łatwo jest zbudować podstawowe oprzyrządowanie, a następnie dodać operatory — wtedy, gdy będą potrzebne. Charles: Rdzeń interpretera języka PostScript miał zaledwie kilka kilobajtów. John: Był dosyć mały. Charles: Tak jest. Był niewielki.

490

ROZDZIAŁ

SZESNASTY

Jakie problemy rozwiązywał projekt, który bazował na wykorzystaniu stosu? Czy język PostScript ucierpiałby, gdyby był językiem bardziej opisowym?

John: Projekt PostScriptu bazujący na w ykorzystaniu stosu jest bardzo prosty do zaim plem entow ania. Kod PostScript może być szybko zinterpretow any i uruchom iony. W kom puterach z czasów, w których pow stał język PostScript (bazujących na procesorze Motorola 68000), ta prostota i wydajność były bardzo ważne. Myślę, że dla stworzenia języka PostScript najważniejsze były następujące decyzje projektowe: •

Język PostScript był kom pletnym językiem program ow ania wyposażonym w zmienne, instrukcje warunkowe, instrukcje pętli itp.



Operatory PostScript można zdefiniować w samym języku. Ta zdolność umożliwiła nam przetworzenie istniejących plików PostScript na pliki w formacie Acrobat (PDF). To pozw oliło nam również na popraw ianie błędów w im plem entacji zaszytych w pamięci ROM.



Model generow ania obrazów pozwolił wydzielić i graficznie m anipulow ać strukturam i pomocniczymi, które potem m ożna było umieścić w większych elem entach. Na przykład PostScript um ożliw iał pobranie opisu strony, wyskalowanie jej i stworzenie z niej kom ponentu innej strony. Ta elastyczność i łatwość używania nie była dostępna w żadnym innym protokole drukarkowym.



PostScript um ożliw iał użytkow nikom przetw arzanie czcionek na zasadach identycznych z dowolnymi innymi elementami graficznymi: mogły być skalowane, obracane lub przekształcane w dowolny sposób. Tę własność wprowadziliśmy w języku PostScript jako pierwsi.



Chociaż pierwsze drukarki obsługujące PostScript były m onochrom atyczne, język przewidywał użycie kolorów.

Czy rozmiar stosu w trybie działania programu był jakoś ograniczony?

John: O ile pamiętam, stos używany do uruchamiania programów był ograniczony do 256 poziomów. Stosy były też ograniczone rozmiarem w bajtach. Charles: Procesor, na którym działał język wewnątrz drukarki LaserWriter, miał tylko — mówię tylko, ale wtedy to było dużo — półtora megabajta pamięci RAM, z czego jeden m egabajt stanow ił bufor ramek. Do działania wszystkich program ów było dostępne tylko pół megabajta pamięci RAM. Czy mieliście do dyspozycji dodatkowe pół megabajta pamięci ROM?

Charles: Tak. John: Tak.

POSTSCRIPT

491

Czy odwzorowaliście tę pamięć ROM w pamięci RAM, a następnie wykonywaliście uaktualnianie w trybie runtime, jeśli była taka potrzeba?

John: Nie. Jeśli było trzeba, dodawaliśmy operator do kodu pobranego do pamięci RAM. Ten operator przesłaniał operator wbudowany. Jak wspominałem wcześniej, istniała możliwość modyfikowania definicji operatorów. Za pomocą tego mechanizmu można było modyfikować funkcjonalności lub dodawać nowe. A co z semantyką formalną? Niektórzy projektanci tworzą semantykę języka, a następnie udowadniają niewielki podzbiór zasadniczych własności.

John: Mieliśmy mechanizm słowników do wyszukiwania nazw i symboli. Mieliśmy tablice. Mieliśmy mechanizmy do przetwarzania liczb. Mieliśmy do dyspozycji prosty mechanizm stosu pozwalający na pobranie do 256 operatorów. Była to jednak bardzo prosta maszyna. Kiedy udało się uruchomić podstawowy interpreter, debugowanie okazało się bardzo proste. Byliśmy przekonani co do rozbudowanych możliwości języka. Nie przeprowadzaliśmy żadnych formalnych dowodów. Czy przewidywali panowie możliwość ręcznego pisania kodu PostScript?

John: Nie, ale wiele osób pisało w języku PostScript. Po jakimś czasie można się było do tego przyzwyczaić. Teraz wolę do tego celu wykorzystywać JavaScript. Charles: Wszystkie broszury, za pomocą których prezentowaliśmy możliwości języka PostScript, były tworzone przez projektantów, którzy musieli się nauczyć programować. Myślę, że jedną z przyczyn, dzięki której język może mieć długie i zdrowe życie, jest to, co wcześniej powiedziałem na tem at rozm iaru interpretera. Powinien być odpow iednio mały. Zwykle wystarczył weekend, aby przenieść go na dowolną platformę sprzętową. John: Czasami mogło to zająć nieco więcej niż weekend. Charles: M ogło tak się zdarzyć w przypadku w ykonyw ania zaaw ansow anych graficznych przekształceń. Wtedy problem był nieco bardziej złożony. John: Jest i inna cecha języka PostScript, która wpłynęła na jego sukces. Co prawda był to język, ale również istniały określone problemy, które musieliśmy rozwiązać. Największym z nich było skalowanie czcionek na podstawie ich obrysu. Trudno było stworzyć estetyczną czcionkę na podstawie obrysu. Nikomu wcześniej się to nie udało. Niektórzy uważali to za niemożliwe.

John: Nie byliśmy pewni, czy to jest możliwe. Charles: Było to coś, co można porównać do położenia na szali naszej przyszłości. Zdecydowaliśmy, że musimy to zrobić, ponieważ alternatywnym rozwiązaniem było

492

ROZDZIAŁ

SZESNASTY

zatrudnienie armii ludzi w celu ręcznego m odyfikow ania m ap bitowych. Takie rozwiązanie nie pasowało do filozofii m echanizm ów liniowych transform acji występujących w języku PostScript. W ystarczyło obrócić znak o kilka stopni, a pow staw ała konieczność stworzenia nowej mapy bitowej. John: Było kilka kluczowych pomysłów, które umożliwiły zrealizowanie tego mechanizmu. Nam pierwszym się to udało. Charles: Uważam, że innym dużym sukcesem języka PostScript było uzyskanie doskonałej jakości półcieni dla kolorów. W tej dziedzinie w ykonano wiele pracy. Sądzę jednak, że im plem entacja opracow ana przez firmę Adobe do końca lat osiemdziesiątych była równie dobra lub naw et lepsza od systemów elektrom echanicznych używanych wcześniej. Czy pomogło panom w tym doświadczenie w badaniach nad grafiką?

Charles: To nie była tylko nasza zasługa. John: Oczywiście, że nie była to tylko nasza zasługa. Pierwszy zbiór mechanizmów obsługi półcieni wykonaliśmy poprzez emulację tego, w jaki sposób mechanicznie były tworzone półcienie. Później jednak zatrudniliśmy kilku matematyków. Steve Schiller w ykonał m nóstw o pracy w dziedzinie półcieni i zrozum iał ich działanie na poziomie o wiele bardziej podstawowym. Charles: Zarówno John, jak i ja byliśmy blisko związani z poligrafią. Mój dziadek i ojciec zajmowali się światłodrukiem. Kiedy pokazałem ojcu pierwsze efekty mojej pracy, spojrzał na nie i powiedział: „Cóż, niezbyt dobre” . Później zatrudniliśmy Schillera i innych. W końcu osiągnęliśmy taki poziom, że nawet mój ojciec go podziwiał. To było wspaniałe uczucie. Czy myśleliście tylko o obracaniu czcionek rastrowych?

Charles: Chcieliśmy uzyskać możliwość dowolnego skalowania i dowolnej rozdzielczości. Mniej ważne staje się to przy bardzo wysokiej rozdzielczości i stosunkowo niewielkich rozmiarach punktu. Ale w przypadku drukarek laserowych lub ekranu skalowanie dawało niezadowalające wyniki. John: Podstawowym pomysłem na zrealizowanie tej własności były próby tworzenia prototypu obrysu znaku i wyliczanie tych fragmentów, które należy uwzględnić. My nie robiliśmy tego w ten sposób. My obserwowaliśmy częstość występow ania obrazu rastrowego, a następnie dopasowywaliśmy obrysy do m ap bitow ych, po czym włączaliśmy odpowiednie mapy. Dzięki tem u wszystkie nóżki były takie same. Także wszystkie szeryfy były identyczne. Dzięki tem u też dobrze wyglądały pogrubienia. Był to bardzo prosty pomysł, ale nikt wcześniej na niego nie wpadł.

POSTSCRIPT

493

Charles: Można tolerować to, że jakiś element jest nieco grubszy lub nieco cieńszy od innego, ale jeśli element pow tarza się w obrębie znaku, ludzkie oko dostrzeże różnice. John: Nawet wtedy, gdy są bardzo małe. W ja k i sposób radzicie sobie z kerningiem i ligaturami?

John: Wystarczy ustawić początek rastra, a następnie wybrać najbliższy element. Jeśli chodzi o kerning dla par znaków lub zaawansowany kerning, w dalszym ciągu robimy tę samą rzecz. Wyrównujemy znak do granicy rastra, a następnie dobieramy odstęp pomiędzy tą granicą, a najbliższym pikselem następnego znaku. Działa doskonale. Ligatury to po prostu znaki o specjalnym projekcie. Charles: A potem trzeba zrobić to samo dla języków chińskiego i japońskiego oraz znaków kanji. John: Kiedyś pracował dla nas człowiek o nazwisku Bill Paxton. Kiedy zaczęliśmy pracować nad chińskimi znakami, nie tylko spacje pomiędzy literami były ważne, ale także oczka i m ałe prostokąty. Trzeba było zadbać o to, aby nie zniknęły oraz by miały odpowiednią wielkość. Bill Paxton stworzył bardzo złożony zestaw reguł dotyczący sposobu zniekształcania znaków do częstości rastra, tak aby zostały zachowane wszystkie najważniejsze części znaku. Czy trzeba było zidentyfikować wszystkie kluczowe fragmenty dla każdego znaku?

John: Właściwie tak. Charles: Ale trzeba to było zrobić tylko raz. Projektowanie opisu PostScript dla chińskiego alfabetu oczywiście zajęło znacznie więcej czasu niż dla alfabetu łacińskiego z tej prostej przyczyny, że w alfabecie chińskim jest znacznie więcej znaków. Ale trzeba było tylko raz zapłacić tę cenę. Później rodzaj alfabetu nie odgrywał już żadnego znaczenia. Czy można współdzielić te informacje pomiędzy czcionkami?

John: Buduje się strategię, po czym można zautomatyzować wiele rzeczy — wystarczy powiedzieć: „Oto sytuacja tego rodzaju. Należy z nią postępować w ten sposób” . Kiedy pracow ano nad czcionkami TrueType, stosow ano taką samą strategię, ale należało ją zastosować dla każdego znaku z osobna. N atom iast strategia dla języka PostScript była stosowana raz dla całego alfabetu. Inaczej mówiąc, dla małej litery h trzeba zidentyfikować lewy i prawy trzon (ang. stem). Identyfikacja lewego i prawego trzonu dotyczy również małej litery n. Wartość x-height dla znaku jest stała na przestrzeni całej czcionki. Należało zidentyfikować te właściwości, a następnie poddać je działaniu algorytmów. W przypadku zmiany projektu litery n w rzeczywistości nie trzeba było robić zbyt wiele. Stworzenie czcionki PostScript było znacznie łatwiejsze niż stworzenie czcionki TrueType.

494

ROZDZIAŁ

SZESNASTY

Nowoczesne drukarki potrafią interpretować PostScript (a także form at PDF) bez pośredniej translacji. Czy jest to korzyść wynikająca z elastyczności i elegancji projektu języka PostScript?

John: Należy pamiętać, że w czasach, kiedy PostScript został zaimplementowany po raz pierwszy, urządzenia miały bardzo mało pamięci. Drukarka LaserWriter miała 1,5 megabajta pamięci i 0,5 megabajta pamięci ROM. Jeden megabajt pamięci RAM był zarezerwowany na bufor strony. Z tego względu mieliśmy tylko 0,5 megabajta do wykorzystania w roli roboczej pamięci masowej. Pamięć ROM została wykorzystana do zapisania implementacji PostScript. Język PostScript był zaprojektowany w ten sposób, że zajmował bardzo mało miejsca. W większości przypadków nie wystarczało pamięci na przechowyw anie całego programu PostScript. Oznaczało to, że program przetwarzał i drukował strony, w miarę jak pojawiały się w programie PostScript — to znaczy były wczytywane przez drukarkę. Stosowanie tej strategii pozw alało na drukow anie bardzo złożonych zadań bez konieczności posiadania dużej ilości pamięci. Format Acrobat działa inaczej. Lokalizacja każdej strony jest zapisana na końcu pliku, co oznacza, że drukarki obsługujące form at Acrobat przed rozpoczęciem drukow ania m uszą załadow ać cały plik. W spółcześnie, gdy rozm iar pamięci jest rzędu kilku gigabajtów, nie stanowi to żadnego problemu. Tak więc PostScript i PDF mają identyczne modele tworzenia obrazów. Z tego powodu są blisko ze sobą powiązane. Czy trudność pisania czytelnych programów w języku PostScript wynika z własności tego języka?

John: Język PostScript doskonale się sprawdza w grafice. Wykorzystanie stosu, sposób zagnieżdżania transformacji oraz używanie rekurencyjnych wywołań — to wszystko świetnie się nadaje do obsługi grafiki. M ożna narysować obraz, a następnie ukryć go i w ykonać na nim transform acje. Język PostScript zajmuje się wszystkimi wew nętrznym i operacjami. Charles: Nie mogę się powstrzymać od przypomnienia pewnej anegdoty z czasów pracy w firmie Xerox PARC. Prowadzono tam zawsze dyskusje pomiędzy zwolennikami strukturalnego języka Mesa, ludźmi, którzy preferowali język Lisp, a tymi, których zadowalał dość prymitywny język BCPL. Jeden ze specjalistów od języków programowania zaproponował rywalizację polegającą na tym, że poszczególne grupy osób zaprogramują ten sam problem w preferowanym przez siebie języku. Chcieliśmy sprawdzić, która grupa zaproponuje najkrótsze, najszybsze i najbardziej eleganckie rozwiązanie. Okazało się, że najlepsze rozwiązanie opracowała grupa, w której pracował najlepszy program ista — Bob Sproul. Rozwiązał problem w języku BCPL i nie potrzebował

POSTSCRIPT

495

żadnych innych wymyślnych rzeczy. Okazuje się więc, że znacznie większy wpływ na jakość rozwiązania mają zdolności programisty niż własności konkretnego języka. W języku PostScript Level II dodano takie własności, ja k mechanizm odśmiecania. Czy przewidują panowie inne kierunki ewolucji języka?

John: W języku JavaScript w dalszym ciągu nie m a interfejsów graficznych pozwalających na w ydrukow anie strony. N atom iast kiedy chcę stworzyć matrycę strony, zawsze piszę odpowiedni kod w języku PostScript, a następnie przetwarzam go za pom ocą program u Acrobat Distiller w celu uzyskania pliku PDF. Bez tych własności nie mógłbym zrealizować połowy zadań w moim życiu. Charles: Jeśli ma pan na myśli konkretnie PostScript, to podejrzew am , że w tym momencie jego życia raczej nie należy spodziewać się jakiejś przyspieszonej ewolucji. Obecnie następuje przekształcanie modelu tworzenia obrazów graficznych z PostScriptu do innych środowisk, na przykład Flash. John: Tak. Aby Flash m ógł wykonywać wszystkie operacje tekstowe, musi być w yposażony w tradycyjne silniki tekstowe firmy Adobe. Wszystkie te program y ostatecznie zostaną przeniesione na telefony komórkowe. Firma Apple wykorzystuje form at PDF do opisania graficznych aspektów pulpitu systemu operacyjnego Mac OS X. Czytałem gdzieś, że był projekt wykorzystania języka PostScript do takich operacji.

Charles: Kiedy w 1983 roku podpisaliśm y um owę z firmą Apple, w jej ram ach przekazaliśmy firmie prawa licencji do technologii Display PostScript. Steve Jobs chciał mieć coś takiego w ramach kontraktu. Kiedy Steve opuścił Apple, firma zdecydowała się pójść własną drogą i porzucić ten projekt. Steve zrozumiał jednak, że chce mieć ten sam model zobrazow ania zarów no na ekranie, jak i na stronie drukow anej, tak aby nigdy nie doszło do niespójności pom iędzy tymi dwiema postaciami. Kiedy przeszedł do firmy NeXT, podpisaliśmy z nim umowę, a technologia Display PostScript stała się modelem zobrazowania dla środowiska komputerów NeXT. Mogę sobie wyobrazić, ja k to by działało z punktu widzenia technologii DTP, w której pomiędzy urządzeniami do wyświetlania potrzebna jest ja k największa dokładność.

John: Z p unk tu widzenia systemu ogólnego przeznaczenia w momencie, kiedy nauczyliśmy się skalować czcionkę do rozdzielczości ekranu — była w tym również zasługa Billa Paxtona — to był bardzo spójny model graficzny o dużych możliwościach. Charles: Proszę sobie wyobrazić, o ile bardziej interesujący byłby internet, gdyby tworzenie języka HTML rozpoczęto od modelu zobrazowania języka PostScript, zamiast przeprowadzać teraz te wszystkie operacje w celu jego symulacji. John: To napraw dę zabawne. Technologia Flash, dziś należąca do firmy Adobe, rozwija się w kierunku lepszej obsługi czcionek. To naprawdę interesujące. Flash nigdy

496

ROZDZIAŁ

SZESNASTY

zbyt dobrze nie radził sobie z czcionkami. Nigdy też nie zawierał zbyt dobrych silników graficznych. Flash ewoluuje w kierunku znacznego zwiększenia możliwości w tych obszarach. Charles: W rezultacie, jak m ożna się spodziewać, powstanie technologia Display PostScript for the Web. Czy sądzą panowie, że technologia ta może migrować do drukarek?

John: Nie. Charles: Nie. Czy drukarki będą miały znaczenie?

John: Coraz mniejsze. Charles: Obecnie można zauważyć, że wiele drukarek to drukarki obsługujące format PDF, a nie PostScript. Oznacza to, że interpretacja jest wykonywana w komputerze. John: Ale komputery mają dziś nieco inne możliwości obliczeniowe, niż miały kiedyś. Charles: Fakt, że można sterować drukarkę atramentową z komputera z szybkością 20 stron na minutę, mówi sam za siebie.

Badania i edukacja Czy w sposobie rozwoju sprzętu i oprogramowania od lat siedemdziesiątych pojawiło się coś, co naprawdę panów zaskoczyło? Wiele rozwiązań wykorzystywanych dziś znano w firmie PARC w latach siedemdziesiątych...

Charles: Myślę, że w arto przyjrzeć się, czym była firma PARC w momencie jej pow stania. H istoria rozpoczyna się w czasie, kiedy w Stanach Zjednoczonych Eisenhower przekazywał władzę Kennedy’emu. Eisenhower wziął Kennedy’ego na bok i powiedział mu, że doszły do niego pewne informacje od światłych ludzi z armii. Jeśli Stany Zjednoczone chcą kontynuow ać ekspansję swojej obecności militarnej w świecie, to muszą przejść z kom unikacji analogowej na cyfrową. Nie sądzę, aby prezydent Eisenhower i prezydent-elekt dokładnie rozumieli znaczenie tego zdania, jednak Kennedy podszedł do problemu bardzo poważnie. Zaprosił do swojego gabinetu ludzi najbardziej obeznanych z techniką — między innym i McNamarę — i przekazał im to, co usłyszał od Eisenhowera. Powiedział: „Chcę, żeby się pan tym zajął. Ma pan do dyspozycji największy budżet. Może pan wydać tyle pieniędzy, ile trzeba, aby projekt ruszył z miejsca, ale jednocześnie na tyle m ało, aby Kongres Stanów Zjednoczonych nie zadaw ał zbędnych pytań. Chcę, aby ten projekt ruszył z miejsca szybko i skutecznie” .

POSTSCRIPT

497

Z kolei McNamara wybrał do współpracy pracownika MIT. Był to Joseph C.R. Licklider — człowiek kierujący laboratoriami badawczymi w MIT, gdzie rozpoczęto badania nad wykorzystaniem technologii komputerowych nie do obliczeń, ale do komunikacji. Licklider zauważył, że badacze z MIT nawiązali relacje z innym i ośrodkam i akademickimi oraz kilkoma ośrodkam i przemysłowym i w kraju. Postanow ił je wszystkie odwiedzić. Przekonał się, że pracowali tam niezwykle inteligentni ludzie. Byli to ludzie z takich instytucji, jak Caltech, UCLA, Stanford, Berkley, Utah, Michigan i oczywiście z kilku instytucji na W schodnim Wybrzeżu, takich jak MIT, Carnegie Tech i kilku innych. M cN am ara postanow ił wziąć kilkadziesiąt m ilionów dolarów i rozprow adzić je w niewielkich pakietach pomiędzy tymi kilkunastoma uniwersytetami oraz kilkoma laboratoriam i badawczymi, między innym i Bolt Beranek, N ew m an oraz RAND Corporation. Powiedział do nich: „Nie chcę zajmować się drobiazgowym zarządzaniem wami. Chcę dać wam te pieniądze. Możecie je wydawać przez kilka lat na badania. Chciałbym jedynie — kiedy Kongres USA zapyta, na co zostały wydane te pieniądze — móc na to pytanie odpowiedzieć. Najważniejsze jest jednak, zwłaszcza jeśli chodzi o instytucje akademickie, abyście wyszkolili nową kadrę ludzi, którzy będą ekspertami w tej dziedzinie” . Jeśli przyjrzy się pan mojemu wykształceniu czy wykształceniu Johna, to zauważy pan, że wszyscy byliśmy studentam i ARPA (ang. Advanced Research Project Agency). Jeśli przejrzymy CV ludzi z Doliny Krzemowej, to okaże się, że prawie wszyscy założyciele firm i najważniejsi naukow cy w tej branży pobierali nauki w Agencji ARPA D epartam entu O brony w czasie, kiedy ludzie z kręgów rządowych nie zajmowali się drobiazgowym zarządzaniem projektami. Właśnie z tego przedsięwzięcia ośrodek PARC czerpał ludzi. Firma Xerox zatrudniła Roberta W. Taylora — człowieka, który przejął obowiązki od Licklidera. Wiedział on, do jakich chodziliśmy szkół. To on zatrudnił wszystkich tych ludzi w PARC. PARC był pierwszą branżową manifestacją jakości ludzi wykształconych przez ARPA w ciągu poprzedniego dziesięciolecia. Dzięki zebraniu tak wielu wykształconych ludzi w jednej instytucji mogliśmy wywierać niezwykły wpływ na branżę. Zwracam się do pana Charlesa Geschkego. Założył pan laboratorium Imaging Sciences Laboratory w Xerox PARC, gdzie kierował pan przedsięwzięciami badawczymi. Jaki jest pański sposób na zarządzanie grupą badawczą?

Charles: Najważniejszy aspekt to zatrudnienie jak najinteligentniejszych ludzi, jakich uda się znaleźć. Zatrudnienie Johna do pracy w tym laboratorium to najlepsza decyzja w m oim życiu zawodowym. Do tego w laboratorium było już kilku naukow ców , którzy również byli niezwykle utalentowani. Jeśli tacy ludzie zajmują się badaniami, to będą przyciągać innych, wysokiej jakości ludzi — w szczególności młodych — zwykle absolwentów uczelni. Udało nam się stworzyć bardzo silny zespół ludzi.

498

ROZDZIAŁ

SZESNASTY

Od samego początku nasze badania nie ograniczały się do prac grupy stworzonej w laboratorium . Dotarliśmy do innych części firmy Xerox i, do pewnego stopnia, także do społeczności akademickiej. Tego rodzaju integracja jest również bardzo cennym sposobem prow adzenia badań, ponieważ um ożliwia korzystanie z różnorodności punktów widzenia. W ja k i sposób rozpoznaje pan dobrego badacza?

Charles: Nie ma na to prostego testu. W większości przypadków dobrymi badaczami okazują się ludzie, którzy od jakiegoś czasu pracowali w branży. Trzeba się przyjrzeć ich dokonaniom. Znałem Johna z dobrej reputacji od czasu, kiedy skończył uczelnię, ale on i ja nigdy razem nie pracowaliśmy ani naw et nie spotkaliśmy się oficjalnie, zanim trafił do m nie na rozmowę kwalifikacyjną. Na podstaw ie jego osiągnięć w Evans & Sutherland wiedziałem, że jest to człowiek charakteryzujący się kreatywnym myśleniem oraz dążący do osiągnięcia celu. To nie jest osoba, która proponuje jakieś rozwiązanie, a następnie przekazuje je komuś innem u, ale osoba, która opracowuje implementację swoich pomysłów. Zawsze uważałem , że bardzo wartościow ą cechą osoby zajmującej się badaniam i jest to, że rozwija swój pom ysł i doprow adza do stw orzenia wysokiej jakości reprezentacji tego pomysłu. Być może na tym polega różnica pomiędzy klasycznymi badaniami a badaniami stosowanymi.

Charles: Nie sądzę, aby istniały pomiędzy nimi różnice. Myślę, że w każdym rodzaju badań obowiązują te same kryteria. Obserwowałem ludzi w czasie trw ania mojej kariery i zauważyłem — dotyczy to zarówno badaczy, jak i inżynierów — że oprócz umiejętności intelektualnych, które oni prezentują, ważne jest również, czy potrafią skończyć to, co sobie założyli. Ludzie mający takie cechy zwykle są znacznie bardziej produktywni i skuteczni. W ja k i sposób rozpoznaje pan obiecujące projekty?

Charles: Niektóre z nich przez czystą obserwację — czy przyciągają zainteresowanie ludzi, którzy chcą dołączyć do pracy. Pamiętam, kiedy zaczęliśmy pracę nad projektem InterPress dla firmy Xerox (pracowaliśmy wtedy nad prekursorem języka PostScript), wzięliśmy z wielu różnych miejsc spoza firmy ludzi, którzy byli chętni do zaangażow ania się w projekt. Pracował z nami profesor z Uniwersytetu w Stanford, profesor z Carnegie Mellon, naukowiec z firmy Xerox, który pracował samodzielnie na W schodnim Wybrzeżu. To bardzo interesujące. Sześciu z nas nigdy nie znalazło się w tej samej lokalizacji fizycznej, aż do czasu zakończenia projektu. Podczas trw ania całego projektu komunikowaliśmy się przez e-mail oraz używaliśmy sieci ARPAnet.

POSTSCRIPT

499

Moją strategią zawsze było wyznaczenie ogólnego kierunku. Pracownicy laboratorium wiedzieli, jaki m a być kierunek prac, a potem tylko od ich kreatywności zależało to, co będą robili. Moja rola jako menedżera polegała na nadaniu kształtu i reprezentacji tego, co chcieli zrobić, oraz zadbaniu o przydzielenie zasobów umożliwiających im realizację celu. W biznesie polegającym głównie na produkcji zazwyczaj więcej uwagi poświęca się produktom , które ostatecznie trafią na rynek. Zwykle staram y się tworzyć harmonogramy z uwzględnieniem szeregu kryteriów, które umożliwiają określonemu projektowi odniesienie sukcesu na rynku. Zadałem panu pytanie dotyczące laboratorium badawczego, ponieważ wiem, że m ia ł pan problemy ze stworzeniem produktu w projekcie InterPress.

Charles: Zarządzanie badaniam i było właściwe. Problem polegał na tym, że nie podjęto prób znalezienia sposobu, aby skorzystać z wyników badań i zastosować je w tej części korporacji, która zajm owała się produkcją. Problem występował na styku tych dwóch obszarów. Był to błąd na znacznie wyższym poziomie zarządzania. Aby jednak być sprawiedliwym, muszę przyznać, że my ludzie zajmujący się badaniami uważaliśmy, że jeśli tylko w padniem y na dobre pomysły, to ludzie z produkcji po prostu rzucą się na nie, aby je zrealizować. To sprowadza się do mojej wcześniejszej uwagi. Naprawdę dobry badacz — niestety nie postępowałem w ten sposób, kiedy byłem młody — aby osiągnąć sukces, powinien podążać za swoim pomysłem przez cały czas jego życia, aż do ostatecznej implementacji. Jaka jest różnica pomiędzy liderem a menedżerem?

Charles: Lider jest osobą, która zna cel oraz ma dość dobre w yobrażenie tego, jak można go osiągnąć. Musi też posiadać umiejętność zatrudniania i motywowania innych ludzi do pracy w celu osiągnięcia wyznaczonego celu. To właśnie robi lider. Z kolei menedżer głównie koncentruje się na wzajemnych zależnościach pomiędzy takimi elementami, jak budżet, sposoby komunikacji, oraz na innych relacjach między osobami pracującymi nad projektem. Osoba ta jednak może mieć tylko niewielkie pojęcie na tem at wyznaczonego celu. W każdej dużej organizacji powinien być zarówno lider, jak i menedżer. Wymieszanie tych ról często ma katastrofalne skutki. Tylko niektórzy liderzy mogą być menedżerami. Jeśli myślimy, że zatrudniam y kogoś, kto będzie spełniał rolę lidera, a osoba ta ma głównie umiejętności menedżerskie, prawdopodobnie nie będziemy zadowoleni z jego osiągnięć. Z drugiej strony, jeśli chcemy zarządzać dużą organizacją i wybierzemy lidera, który większość czasu poświęca na wymyślanie swoich doskonałych pomysłów na przyszłość, to prawdopodobnie osoba ta nie będzie dobrym menedżerem.

500

ROZDZIAŁ

SZESNASTY

Te dwie role wymagają innych umiejętności. Obie m ają kluczowe znaczenie. Nie można ich mylić, kiedy powierza się komuś jakieś zadanie. Osoba ma umiejętności albo lidera, albo menedżera. Rzadko zdarza się ktoś, kto potrafi dobrze pełnić obie funkcje jednocześnie. W ja k i sposób rozpoznaje pan dobrego programistę?

Charles: W większości przypadków poprzez pracę razem z nim , dla niego, obok niego — poprzez utrzymywanie aktywnych relacji zawodowych. Nie znam żadnej metody wyrażenia tego w sposób abstrakcyjny. Miałem szczęście pracować z bardzo różnymi ludźmi. Jedną z osób, którą oprócz Johna najlepiej zapamiętałem, jest ktoś, o kim być może pan nigdy nie słyszał. Ten człowiek to Ed Taft. Z atrudniłem go do pracy w Xerox PARC w 1973 roku. Uważam go za najlepszego program istę, z jakim kiedykolwiek miałem okazję pracować. Jest bardzo drobiazgowy w opisywaniu problemu, który próbuje implementować, oraz zdeterminowany, by zaimplementować go na poziomie gwarantującym największą elastyczność w zmianie zdania co do tego, jak powinien działać w praktyce. W pewnym sensie człowiek ten opóźnia wiązanie w sposób, w jaki robi to język PostScript, ale jest to osoba, która doprowadza projekt do celu. Kiedy opracuje fragment kodu i powie, że jest on skończony, jest to kod solidny jak skała. Ed jest programistą kom pletnym realizującym projekt od koncepcji do pełnej postaci. Gdybym mógł sklonować Eda, zrobiłbym to. W ten sposób można by zbudować doskonałą firmę. Czy jest jakiś konkretny tem at informatyczny, na którym powinni skoncentrować się studenci wyższych uczelni?

Charles: Jestem bardzo konserw atyw ną osobą, jeśli chodzi o pierwszy stopień wykształcenia akademickiego w zakresie informatyki. Uważam, że studenci powinni uczyć się jak najwięcej m atem atyki i fizyki, a dodatkow e zgłębianie inform atyki pow inno pozostawić się ludziom chcącym uzyskać poziom magistra lub doktora. To jednak jest tylko moje zdanie. Oczywiście uniwersytety nauczają tego, czego ich studenci żądają, rozumiem więc, dlaczego nadają tytuły licencjackie w dziedzinie informatyki. Konieczne jest posiadanie odpowiedniego przygotowania z matematyki i techniki. To potrzebne zwłaszcza wtedy, kiedy ktoś chce się zająć sprzętem. D odatkow o potrzebna jest pewna kombinacja wiedzy z chemii i fizyki. Takie jest moje prywatne zdanie na ten temat. Jeśli chodzi o ogólną edukację, to jestem gorącym zwolennikiem sztuk wyzwolonych (ang. liberał arts). Cóż z tego, że ktoś jest naukow cem i m a w głowie doskonałe pomysły, jeśli nie może skutecznie ich wyrazić i przekonać innych osób, by podążali

POSTSCRIPT

501

w jego kierunku. Umiejętności pisania i m ów ienia są absolutnie kluczowe dla osiągnięcia sukcesu. Jeśli ktoś nie posiada tych kom petencji, nie jest w pełni w ykształconą osobą. Uważam, że z tego powodu będzie mniej wydajny. W ramach podstawowego programu akademickiego powinno się znaleźć jak najwięcej sztuk wyzwolonych, a także nauki techniczne i matematyka. Czego powinni nauczyć się inni z pańskiego doświadczenia z językiem PostScript?

Charles: Jedną z największych zalet języka PostScript jest to, że pozwala on na opóźnienie wiązania z czymś jawnym tak długo, jak to możliwe. Innymi słowy, obliczenia, przetwarzanie danych i większość operacji wykonuje się na stosunkowo abstrakcyjnym poziomie. Dopiero w ostatnim momencie musimy zdecydować, które bity zamierzamy włączyć w określonym obrazie rastrowym oraz czy zwiążemy się ze specyficznym algorytmem, który to realizuje. Dzięki działaniu na wyższym poziomie abstrakcji m ożna budować sensowny, w ysokopoziom owy opis obrazu, który zamierza się stworzyć. Obraz ten staje się następnie możliwy do zaimportowania przez całą grupę urządzeń. Jest to filozofia w modelu zobrazowania, która daje okazję do zrobienia czegoś, o czym mówiliśmy znacznie wcześniej — stworzenia tego samego modelu zobrazowania, który będzie działał nie tylko na komputerze osobistym, ale także w internecie oraz na wszystkich cyfrowych urządzeniach — od telewizorów, poprzez telefony, aż po różne urządzenia internetowe. Na tym polega piękno języka PostScript. Możemy działać na wyższym poziomie w zakresie sposobu opisania wyniku, który chcemy uzyskać, i dopiero w ostatnim momencie związać ten opis z konkretnym urządzeniem.

Interfejsy do długowieczności W ja k i sposób projektant powinien myśleć o długowieczności języka programowania ogólnego przeznaczenia? Czy trzeba przedsięwziąć określone kroki?

John: Wiele języków próbuje rozwiązywać specyficzne problemy. Pamięta pan język HyperCard opracowany przez Atkinsona? Popełnił on błąd, który według mnie należy do najbardziej pospolitych błędów popełnianych przez programistów. Z tego powodu HyperCard nie stał się kompletnym językiem programowania. W kompletnym języku programowania muszą być instrukcje sterujące, instrukcje pętli, muszą być podstawy m atem atyczne i wszystko to, co czyni język kom pletnym . Jeśli czegoś zabraknie, to w pewnym momencie w przyszłości uderzymy głową w mur. Ludzie spojrzą na nas i zapytają: „Po co te wszystkie funkcje trygonom etryczne? Do czego ich używać?” — a jak się okaże, wszystkie zostaną użyte. Bardzo ważne podczas projektowania języka jest to, aby już na początku zdać sobie sprawę, że język

502

ROZDZIAŁ

SZESNASTY

musi być kom pletny. Potrzebny jest dostęp do systemu plików. Aby język był kom pletny, musi zawierać wszystkie potrzebne elementy. Uważam to za naprawdę bardzo ważne. Dziś, w 25 lat po pow staniu języka PostScript, w dalszym ciągu działają maszyny obsługujące PostScript — są ciągle takie same i nadal obsługują ten sam język PostScript. Jest dość mocno ulepszony, ale podstawowy program wciąż działa. Charles: Używam drukarki LaserWriter drugiej generacji tylko dlatego, że posiada najlepszy m echanizm ręcznego wysuwu papieru, jaki kiedykolwiek spotkałem w jakimkolwiek produkcie. W dalszym ciągu działa. Firma Canon zakładała jednak, że drukarka będzie w stanie wydrukować tylko 100 000 wydruków, a potem będzie można ją wyrzucić. Z całą pewnością projekt okazał się zbyt solidny. Jaka jest różnica w projektowaniu języka przeznaczonego do wykorzystania przez ludzi i języka używanego przez maszyny? Podobnie — ja k a jest różnica między językiem tworzonym przez ludzi a tym, który tworzą maszyny? Czy są jakieś czynniki, które przychodzą do głowy podczas projektowania, kiedy się myśli, że można coś zrobić w określony sposób, ponieważ żadnemu człowiekowi nie przyjdzie do głowy, by usiąść i odpowiednio odwinąć pętlę?

John: Jedną z największych w ad języka PostScript jest trudność debugowania. A to dlatego, że kod pisze się raz. Podczas pisania jesteśmy na nim skupieni. Po w ykonaniu zadania, kiedy próbujemy do niego powrócić sześć miesięcy później, jest znacznie trudniej. W przypadku standardowych języków z notacją wrostkową (ang. infix notation), gdy nie ma zbyt wielu stanów do zapamiętania, czytanie kodu i jego debugowanie jest znacznie łatwiejsze. Nie trzeba mieć w głowie całego obrazu stosu?

John: Oczywiście, że nie. Czy brali panowie pod uwagę tę dychotomię podczas projektowania języka PostScript?

John: Ze względu na łatwość dodawania nowych operatorów pisaliśmy bardzo krótkie procedury i próbowaliśmy wydzielać funkcjonalność w tak czytelny sposób, jak się tylko da. Składnia jednak nie jest przystosowana do tego, by programy były pisane przez ludzi. Wspominali panowie, że korzystali z usług projektantów , którzy pisali programy w języku PostScript w celu stworzenia broszur. W latach osiemdziesiątych mieli panowie asystentów, którzy ręcznie pisali w języku LaTeX. W ja k i sposób uczyli panowie projektantów pisania programów do generowania broszur?

John: To interesujące pytanie. Mało znanym faktem, o którym firma Adobe nigdy nie wspominała, jest to, że każda z naszych aplikacji została wyposażona w podstawowe

POSTSCRIPT

5 03

interfejsy do języka JavaScript. Można tworzyć skrypty dla InDesign. Można tworzyć skrypty dla Photoshopa. Za pom ocą JavaScript m ożna też opracować skrypty dla Illustratora. Sam przez cały czas piszę program y JavaScript sterujące Photoshopem . Jak powiedziałem , jest to bardzo słabo znany fakt, ale interfejsy skryptowe są kompletne. Dają one pełny dostęp do aplikacji. W przypadku aplikacji InDesign możliwy jest dostęp do m odelu obiektowego, na wypadek gdyby ktoś chciał się do niego dostać. Charles: Możliwości te nie są przeznaczone dla ludzi o słabych nerwach. N aw et języka JavaScript do generowania modelu dokumentów HTML czasami nie powinni używać ludzie o słabych nerwach.

John: Oczywiście, że nie powinni! Robię to bardzo często i muszę przyznać, że nie jest to łatwe, przede wszystkim dlatego, że zestawy znaków są inne. Te dwa środowiska różnią się pomiędzy sobą prawie wszystkim. Pomimo trudności ludzie wykorzystują te możliwości. Najlepszym sposobem zautomatyzowania tworzenia dokumentów jest wykorzystanie JavaScript i napisanie skryptu sterującego m echanizmem składu program u InDesign lub niesamowitych silników generow ania obrazów w ew nątrz Photoshopa. Istnieje możliwość autom atycznego budowania wielu różnych rzeczy w dość prosty sposób. Ponownie brzmi to ja k cecha przemawiająca za długowiecznością. Należy stworzyć język ogólnego przeznaczenia i pozwolić ludziom na wykorzystywanie wszystkich operacji i możliwości, a jednocześnie dać im sposobność pisania pętli i konstrukcji sterujących.

John: To naprawdę działa. Istnieją takie projekty. Znam kilka takich. Jedna z moich witryn WWW ma 90 000 stron. Gdybym nie zautomatyzował tworzenia tej witryny, nie mógłbym jej zrealizować. To po prostu zbyt wiele stron HTML. Czy projektanci o to prosili, czy też ktoś im pokazał, ja k się to robi?

John: Niewiele osób zajmujących się utrzym yw aniem i rozszerzaniem program u Photoshop, InDesign oraz innych wie, że program Bridge obsługujący zadania przetw arzania struktury plików oraz przeglądania obrazów pomiędzy Illustratoren!, Photoshopem i programem InDesign jest napisany w JavaScript. Po prostu tłumaczycie kod w różne strony pomiędzy modelami obiektowymi.

John: Zgadza się. To dużo kodu JavaScript, ale fakt, że pozwala on wszystko scalić, jest doskonały. Kod jest całkowicie przenośny.

504

ROZDZIAŁ

SZESNASTY

Jak panów zdaniem programiści powinni postrzegać sprzęt? Czy oprogramowanie jest przyczyną innowacji?

Charles: Myślę, że jest zarówno jin, jak i jang. W czasach, kiedy te innowacje pojawiły się w firmie PARC, były one realizowane na bardzo małych maszynach ze stosunkowo niewielką ilością pamięci, um iarkow aną w ydajnością sieci itp. W ram ach tych ograniczonych środowisk projektanci wykazywali się kreatywnością, starając się wykonywać operacje, które w pew nym sensie zaskakiwały użytkowników. Ludzie ci nigdy wcześniej nie widzieli czegoś takiego. Dziś jesteśmy w takiej sytuacji: środowiska dramatycznie się zmieniły, a sprzęt rozwija się niezwykle szybko. Mamy do dyspozycji gigabajty pamięci za bardzo umiarkowaną cenę, a także procesory o niezwykłej szybkości. Myślę, że interesującym zjawiskiem jest to, że kiedy zostały usunięte ograniczenia stosunkowo niskiej wydajności i stosunkowo niewielkiego rozmiaru pamięci, ludzie stali się trochę leniwi. Zakładają, że m ają do dyspozycji nieograniczone zasoby sprzętowe. Nagle stają przed bardzo złożonym problemem, kiedy sprzęt znów staje się ograniczeniem i kiedy rozwiązanie określonego problemu wymaga kreatywności i znacznego wysiłku. Twierdzę, że pom iędzy środowiskiem sprzętowym a oprogram ow aniem zawsze pow inna występować równowaga. Jeśli chodzi o aplikacje, to myślę, że Vista jest dobrym przykładem sytuacji, w której proces w ytw arzania stał się nieco leniwy, a programiści założyli, że kom putery będą w stanie poradzić sobie ze wszystkimi wewnętrznymi niedostatkami wydajności oraz sposobami wykonywania określonych operacji. Jak się okazało, takie podejście nie było właściwe. Teraz programiści muszą cofnąć się o krok i w pewnym sensie wprowadzić ograniczenia dla możliwości sprzętu. W efekcie prawdopodobnie powstanie lepsza wersja systemu Windows, zwłaszcza jeśli wziąć pod uwagę doświadczenia z systemu Vista. Czy stworzenie języka, który zyska popularność, jest dziś łatwiejsze?

Charles: Nie sądzę. Myślę, że kiedy ludzie po raz pierwszy uczą się sposobów projektowania i programowania, tworzą sobie w głowach rodzaj środowiska, które bardzo m ocno kształtuje sposób, w jaki myślą o wytwarzaniu oprogramowania. Jest im bardzo trudn o przeciąć węzeł pomiędzy tym doświadczeniem a nowym sprzętem, który się pojawia. Aby now y język program ow ania stał się napraw dę popularny, trzeba znaleźć środowisko, które będzie używane przez wielu użytkowników odpowiednio wcześnie, a następnie doprowadzić do tego, by się w nie zaangażowali. Pod pewnymi względami popularność języka wynika raczej ze środowiska edukacyjnego niż z wysiłków niezależnej organizacji, która próbuje wypromować nowy język na rynku.

POSTSCRIPT

505

Taką ewolucję można dziś zaobserwować na przykład w zakresie technologii cloud computing, w której obliczenia i dostęp do informacji są rozproszone pomiędzy kom puterem desktop użytkownika, internetem , serwerami oraz wieloma innymi elementami. Wyobrażam sobie — choć w tym obszarze nigdy nie działałem — że być może otwiera to okazję do stworzenia języka, który pozwoli w bardziej bezpośredni sposób rozwiązać problemy różnorodności środowisk, niż jest to możliwe za pomocą jakiegokolwiek z obecnie dostępnych języków. Mówię „być może”, ponieważ nie wiem, czy jest to kwestia języka, czy nie. Jeśli spojrzymy na takie firmy, jak Google, M icrosoft i do pewnego stopnia Adobe, zauważymy, że koncentrują się one na dostarczeniu klientom bezproblemowego dostępu do swoich środowisk. Z czasem może się jednak okazać, że istniejące narzędzia stwarzają ograniczenia w tego rodzaju dostępie. Niezależnie od tego, czy chodzi o sam język programowania, czy — co ważniejsze — język wraz ze środowiskiem programowania (które zawsze towarzyszy językowi), trzeba będzie usprawnić zestaw narzędzi w stosunku do tego, co jest dostępne dziś. Problemem jest to, że nie istnieje zbyt wiele naturalnych środowisk. Nie istnieje już, tak jak kiedyś, Bell Labs, IBM Research czy też Xerox PARC. Nie znajdujem y się w żadnym z laboratoriów branżowych, w których mogłyby być prow adzone tego rodzaju badania i projekty. Oczywiście istnieje cały szereg wysokiej jakości środowisk akademickich, ale większość z nich dopiero powstaje w ramach ściśle ukierunkowanych projektów badawczych wspieranych głównie przez rząd Stanów Zjednoczonych za pośrednictwem agencji NSF lub DARPA. Brakuje środowisk podobnych do tych, w których opracowano system Berkeley Unix, czy też środowisk, w których razem z Williamem Wulfem, przy okazji mojej pracy doktorskiej, opracowaliśmy język BLISS — wysokopoziomowy język do programowania systemowego. Mogliśmy uzyskać fundusze ze względu na sposób, w jaki w tamtych czasach agencja ARPA zarządzała funduszami. Dziś funduszy na badania nie zdobywa się już tak łatwo. Trudno mi powiedzieć, gdzie mają być wykonywane takiego rodzaju badania. W korporacji bardzo trud no jest znaleźć środki na tego rodzaju inwestycje. Musi ona bowiem generować przychody i zyski. Chyba że korporacja posiada oddzielną jednostkę badawczą, którą finansuje. Większość współczesnych korporacji, zwłaszcza w branży oprogramowania i środowisku internetu, nie posiada tego rodzaju ośrodków w ramach swojej struktury. A może projekty open source?

Charles: Być może. Jednak z mojego punktu widzenia projekt open source ma szansę skutecznego działania wówczas, gdy koncepcja jest już dobrze rozwinięta, kiedy ustanow iono pew ną strukturalną integralność i kiedy problem jest już dobrze rozumiany. Myślę, że kiedy weźmie się pusty arkusz papieru i nazwie się go projektem open source, będzie bardzo trudno ruszyć z miejsca.

506

ROZDZIAŁ

SZESNASTY

Czy proponowałby pan stworzyć z tego otwarty standard?

Charles: W spółcześnie trzeba by było to zrobić. Ludzie potrzebują otwartego standardu i szczerze mówiąc, pow inien istnieć sposób, aby m ożna było dodawać do języka własne narzędzia. Niewielu wie o tym — co praw da mogę mówić tylko 0 produktach Adobe, ponieważ najlepiej je znam — że do wszystkich produktów Adobe istnieje dostęp poprzez otw arty interfejs w stylu JavaScript. Dzięki tem u firmy zew nętrzne m ogą tworzyć bardzo zaawansowane dodatki do każdego z naszych produktów — z systemu InDesign do Photoshoba, do formatu Acrobat itd. Można to robić za pom ocą neutralnych dla platformy, niezależnych skryptów. Wiele firm 1 indywidualnych grup robi coś takiego przez cały czas. Nie jest to system open source w tym sensie, że udostępniam y kod w C program u Photoshop. Chodzi o sposób utrzymania integralności zasadniczych kom ponentów oraz zapew nienia swobody eksperym entow ania i dodaw ania elem entów przez podm ioty zewnętrzne.

Standardowe życzenia Jaki jest największy problem do rozwiązania w dziedzinie programowania komputerów lub informatyki?

John: Cóż, dziś mam na półce 30 - 40 podręczników dotyczących internetu. Wszystkie są bardzo grube i wzajemnie ze sobą sprzeczne. Bardzo bym chciał, aby zrobiono porządek z modelami zobrazowania, aby uporządkowano środowiska programowania, by uporządkowano wszystko to, co tworzy dzisiejszy internet, ponieważ naprawdę nie ma powodu, aby istniały wszystkie te różnice. Problem różnych przeglądarek i konieczności obsługi różnych implementacji standardu HTML powinien zniknąć. Jeśli chodzi o technologię Flash, to próbujemy zmodyfikować ją w taki sposób, abyśmy co najmniej mogli uzyskać jeden język niezależny od platformy, który będzie można przenosić z platform y na platform ę bez konieczności stykania się za każdy razem z inną semantyką. Charles: Całkowicie się zgadzam. Bardzo frustrujące jest to, że po tylu latach od powstania internetu w dalszym ciągu używamy środowiska, w którym często słyszymy: „Jeśli napraw dę chcesz, aby to działało, musisz skorzystać z Firefoksa” . Obecnie pow inniśm y już daw no być za tym punktem ! Cały sens uniwersalności internetu polega na braku takich różnic. Pomimo to w dalszym ciągu musim y z nimi żyć. Zawsze fascynuje mnie to, jak dużo czasu musi upłynąć, aby tego rodzaju historyczne zaszłości mogły odejść w zapom nienie. Im częściej będą one im plem entow ane w przeglądarkach, tym bardziej będzie się um acniała ich długowieczność, a to jest głupie.

POSTSCRIPT

5 07

Czy uważają panowie ten stan rzeczy za porażkę trwającego dziś procesu standaryzacyjnego?

John: Proces standaryzacyjny w mniejszym stopniu dotyczy rozwiązywania problemów, a w większym kodyfikowania standardów. Charles: Osobiście uważam, że wiele prowadzonych dziś działań standaryzacyjnych to kropienie wodą święconą rzeczy, które są już dziś dość kompletne. Jak powiedział John, nie mają one na celu stworzenia niczego nowego. Mają tylko usankcjonować prawnie określoną technologię i jej historyczne pokolenia. Dopóki nie będzie aktywnych, żywych organizacji, które przejmą odpowiedzialność za standardy, dopóki im plem entacje danego standardu nie będą łatw o dostępne, tak by nikt nie musiał próbować tworzenia implementacji samodzielnie, nie będzie mowy o prawdziwych standardach. W początkowych latach języka PostScript zawsze mieliśmy pewien dylemat. Jeśli klonom języka uda się przejąć kontrolę nad językiem PostScript, to nigdy nie dojdzie do stworzenia PostScript 3. Do tego momentu byłoby w nim tyle niezgodności, że tworzenie nowej wersji nie miałoby sensu. John: To samo można powiedzieć o formacie PDF. W końcu przekonaliśmy Narodowe Archiwum do zaadaptowania tego formatu. Co prawda stanowi to podzbiór bieżącej wersji, ale przynajmniej jest jakaś specyfikacja. Kiedy tworzyliśmy format Acrobat, powiedzieliśmy sobie, że musi on mieć taki kształt, aby te pliki były naprawdę żywe. Postanowiliśmy, że będzie on kompletnie zgodny wstecz, tak aby przeglądarki mogły czytać naprawdę stare pliki. To poważne wyzwanie. Acrobat to olbrzymi kod, ale spełnia w internecie tak ważną rolę, że nie mogę sobie nawet wyobrazić, by go tam nie było. Czy standardy powinny być wyznaczane przez je d n ą główną implementację, czy też mogą wynikać z pewnego porozumienia?

John: W przypadku języka PostScript to nasza implementacja naprawdę zdefiniowała, czym był standard. M ożna powiedzieć, że jest to praw da także w odniesieniu do formatu Acrobat. Problem występował wtedy, kiedy ktoś używał Netscape, a następnie przeglądarki Microsoft. Firma M icrosoft nie m iała motywacji, by utrzymywać zgodność tych przeglądarek. Myślę, że to jest tragiczne. Charles: To samo zrobiono z Javą. Czy internet stałby się lepszy, gdybyśmy używali języka PostScript zamiast HTML i JavaScript?

Charles: Pracujemy teraz nad now ą platform ą dla internetu. Jej bieżąca nazwa to Adobe AIR. Platforma Adobe Internet Runtime to sposób na zapewnienie stopnia zaaw ansow ania grafiki w internecie na takim samym poziomie, jaki m ają nasze

508

ROZDZIAŁ

SZESNASTY

aplikacje oraz jaki występuje w jądrze modelu zobrazowania języka PostScript. Chcemy przenieść ten model do internetu w taki sposób, aby można było tworzyć aplikacje, które bezproblemowo rozmywają różnice pomiędzy tym, co jest możliwe w aplikacji desktop, a aplikacją internetow ą. Wierzymy, że jest to sposób na przeniesienie zobrazow ania i grafiki w stylu PostScript do internetu. Takiego sposobu HTML nie zapewnia. W języku HTML są dwa problemy. Po pierwsze, zapewnia on bitmapową reprezentację informacji. Po drugie, nie jest standardowy. Mówiąc, że nie jest standardowy, mam na myśli to, że jeśli weźmiemy dowolną z popularnych przeglądarek internetowych i wskażemy na określoną stronę HTML, okaże się, że każda z przeglądarek generuje inne wyniki. Dla m nie jest to niedopuszczalne. Oznacza to bowiem, że jeśli ktoś chce stworzyć zaaw ansow aną w itrynę WWW, to aby uzyskać ten sam wygląd stron w różnych przeglądarkach, musi program ow ać ją w sposób specyficzny dla przeglądarek. To jest cofanie się w czasie. W taki sposób program ow ało się w starych, złych czasach. Zdarzyło się to dlatego, że HTML pozostawiono jako rodzaj otwartego standardu. Uważam, że jest w tym jakaś w ewnętrzna sprzeczność. Implementacja standardu może być otwarta, ale sam standard powinien być bardzo dobrze zaprojektowany i przemyślany, tak aby tego rodzaju rozdźwięki nie były widoczne. Przypominam sobie, kiedy firma Sun po raz pierwszy opublikowała Javę. Ostatecznie podpisano umowę z firmą Microsoft. Jedną z pierwszych rzeczy, które zrobiła firma Microsoft, było zmodyfikowanie Javy. Nie było jednolitości pom iędzy różnymi implementacjami. To łyżka dziegciu w beczce miodu, jeśli chodzi o całą koncepcję standardów. Jeżeli chcemy mieć standard, to m a to być standard i wszyscy muszą się do niego zastosować. Zazwyczaj oznacza to, że istnieje jedna autokratyczna organizacja, która utrzymuje standard, a nie implementację. Uważamy, że istnieją realne szanse, aby jakość działań możliwych do w ykonania w internecie przenieść na całkowicie nowy poziom. Cały czas nad tym pracujemy. Istnieje wiele interesujących aplikacji, które firmy zewnętrzne już zrealizowały za pom ocą platform y AIR. Chcemy w dalszym ciągu się na tym koncentrow ać. Platform a AIR powoduje, że system operacyjny, jaki działa na naszym lokalnym laptopie lub komputerze PC, i jego platforma sprzętowa są właściwie bez znaczenia. Łatwo się zorientować, dlaczego firmy Apple i Microsoft uważają to za trudne. Otóż chciałyby one, aby użytkownicy kupili ich implementację integracji z internetem. My mówimy, że platforma nie pow inna mieć znaczenia. Ta chmurka pow inna być dostępna z dowolnego komputera oraz poprzez dowolny rodzaj informacji zapisanych w internecie.

POSTSCRIPT

509

510

RO ZDZIAŁ

SZESNASTY

ROZDZIAŁ

SIEDEMNASTY

EIFFEL

Eiffel jest obiektowym językiem programowania opracowanym w 1 9 85 roku przede wszystkim przez Bertranda Meyera. Obecnie język jest zarządzany przez kom itet standaryzacyjny Ecma International, który opracował standard ISO opublikowany w 2006 roku. W języku jest dostępny obszerny zbiór własności, które dziś są uznawane za nowoczesne i rozpowszechnione: mechanizm odśmiecania, programowanie generyczne oraz bezpieczeństwo typów. Największą z nowatorskich własności języka jest koncepcja projektow ania kontraktowego (ang. design by contract), co oznacza, że język wymusza warunki wstępne dla interfejsu, warunki końcowe i niezmienniki. Własności te poprawiają niezawodność i dają możliwość wielokrotnego wykorzystania komponentów. Wpływy Eiffela są widoczne w takich językach, jak Java, Ruby i C#.

511

Owocne popołudnie Dlaczego zdecydował się pan na stworzenie języka programowania?

B ertrand Meyer: Bardzo niewiele osób tw orzy język program ow ania wyłącznie dla samego jego tworzenia. Eiffel powstał, bo była taka potrzeba. Zaprojektowałem język program ow ania, ponieważ musiałem pisać programy, a wszystko, co było dostępne, mnie nie zadowalało. Czy potrzebował pan narzędzia, na przykład do zaimplementowania koncepcji projektowania kontraktowego?

Bertrand: Ta potrzeba była oczywista, ale ogólnie rzecz biorąc, potrzebowałem języka obiektowego. Spróbuję szerzej wyjaśnić kontekst. W 1985 roku założyliśmy firmę Interactive Software Engineering. Jej obecna nazwa to Eiffel Software. Naszym celem miało być budowanie narzędzi inżynierii oprogramowania. Otrzymywaliśmy fundusze od japońskiej firmy na stworzenie edytora do pisania program ów — edytora ukierunkowanego na składnię. Stworzyliśmy ten edytor. Odniósł umiarkowany sukces. To była bardzo mała firma. Ja w dalszym ciągu wykładałem na Uniwersytecie Stanu Kalifornia w Santa Barbara, a zatem firma ta była rodzajem biznesu na boku. Mieliśmy uniksowe stacje robocze, które otrzymaliśmy od japońskich klientów — były one jednym z ich produktów. Był rok 1985. Zajmowałem się programowaniem obiektowym prawie od 10 lat. Miałem szczęście, że w latach siedemdziesiątych natrafiłem na język Simula 67. Od razu zwrócił moją uwagę. Wiedziałem, że to jest odpowiedni sposób, w jaki należy programować. Na komputerach, które nas interesowały, nie było kompilatora Simuli, a mnie ten język bardzo się spodobał. Jak Tony Hoare powiedział o Algolu, była to innow acja przewyższająca wielu jego następców. Pomimo tych zalet język Simula nie miał ani w ielokrotnego dziedziczenia, ani typów generycznych. Ja natom iast uważałem, że potrzebuję i jednego, i drugiego. Swoje powody wyjaśniłem na pierwszej konferencji OOPSLA w referacie „Typy generyczne a dziedziczenie” . W związku z tym zaczęliśmy się przyglądać dostępnym językom. Dostępny był język C++. Otworzyłem tę książkę, ale bardzo szybko ją zamknąłem — to z całą pewnością nie był taki język, jaki miałem na myśli. Idea w prow adzenia możliwości program ow ania obiektowego dla programujących w języku C była interesująca, ale z całą pewnością mógł to być tylko tymczasowy krok w kierunku czegoś bardziej spójnego. Funkcjonował również język Objective-C, ale za bardzo przypom inał Smalltalka i m iał niewiele wspólnego z zasadami inżynierii programowania, jakimi byliśmy zainteresowani. To samo można było powiedzieć o samym Smalltalku, który był fascynującym językiem, ale nie spełniał naszych priorytetowych oczekiwań. Język Eiffel narodził się jako kombinacja technik obiektowych oraz zasad inżynierii programowania i praktyk, które zostały opracowane w ciągu poprzedniej dekady. Smalltalk m iał charakter program ow ania eksperym entalnego — naszym zdaniem był nieodpow iedni dla rodzaju zadań,

512

ROZDZIAŁ

SIEDEMNASTY

które mieliśmy zamiar wykonywać. Na przykład bardzo uciążliwy okazał się brak typów statycznych. A zatem istniało mnóstwo ekscytujących pomysłów, ale nie było niczego takiego, czego naprawdę chcielibyśmy używać. W związku z tym napisałem raport. Był to raport napisany na Uniwersytecie Santa Barbara, opisujący nie język, ale bibliotekę struktur danych i algorytmów. Bardzo interesowały mnie możliwości wielokrotnego wykorzystania kodu, dlatego chciałem mieć standardową bibliotekę, która będzie opisywała podstawowe struktury danych występujące w informatyce. Dla tych struktur danych czasami używam określenia „K nuthw are” . To, co później nazwaliśmy Eiffel Base, w tam tym czasie nazwałem biblioteką DSL (ang. Data Structures Library). A zatem napisałem artykuł opisujący tablice, listy jednokierunkowe, stosy, kolejki itp. Użyłem do ich opisu określonej notacji i powiedziałem, że m am y zamiar je zaim plem entow ać. Sądziłem, że implementacja zajmie trzy tygodnie. Ciągle nad nią pracujemy. Ale to był już Eiffel. Język sam w sobie nie był w centrum naszej uwagi. K oncentrow aliśm y się raczej na komponentach wielokrotnego użytku. Doszedłem do wniosku, że aby można było tworzyć dobre kom ponenty wielokrotnego użytku, potrzebne są klasy. Potrzebne są również typy generyczne, które były tam od samego początku. Potrzebne są w ielokrotne dziedziczenie oraz uważna kom binacja typów generycznych i w ielokrotnego dziedziczenia, tak jak to pokazałem w referacie wygłoszonym na konferencji OOPSLA. Potrzebne były klasy abstrakcyjne. Oczywiście konieczne były kontrakty, które dla mnie wydawały się rzeczą najbardziej trywialną. Wszyscy inni robią w okół nich wielkie zamieszanie. Ja jednak w dalszym ciągu nie rozum iem , jak m ożna program ować bez kontraktów . W iedziałem również, że jest potrzebny dobry m echanizm obsługi strum ieni czy też serializacji. Z tego względu była to jedna z pierwszych rzeczy, które stworzyliśmy. Osobiście nauczyłem się tego z języka o nazwie SAIL (ang. Stanford Artificial Intelligence Language), który był bardzo dobrze zaprojektowany — nie był obiektowy, ale bardzo interesujący. Języka SAIL używałem na Uniwersytecie w Stanford 10 lat wcześniej. A zatem stworzyliśmy taki mechanizm. Miał on absolutnie podstawowe znaczenie od samego początku. Oczywiście był też potrzebny mechanizm odśmiecania. Jak udało się panu dojść do takiej filozofii? Czy pańskie doświadczenie jako programisty-praktyka wystarczyło do zidentyfikowania sposobu, w ja k i tworzy się oprogramowanie?

Bertrand: Częściowo doświadczenie, a częściowo również czytanie literatury. Kiedy byłem studentem w Stanford w 1973 roku, czytałem książkę Structured Programming autorstwa Dahla, Dijkstry i Hoare’a [Academic Press]. Są to w zasadzie trzy monografie pod wspólnym tytułem. Pierwsza, autorstwa Dijkstry, jest znaną publikacją dotyczącą programowania strukturalnego. Druga, której autorem jest Hoare, dotyczy struktur danych. Także jest doskonała. Jest też trzecia monografia. Jedną z interesujących lekcji, jakie wyciągnąłem z życia, jest to, że ludzie czytają początki książek. To jest

EIFFEL

5 13

wskazówka dla osób piszących książki — trzeba bardzo uważnie wybierać to, co ma znaleźć się na pierwszych stronach, ponieważ 90% ludzi czyta 50 stron, a następnie się zatrzymuje. Nawet jeśli książka jest bardzo dobra. Większość osób przeczytała pierwszą część książki Structured Programming — tę, którą napisał Dijkstra. Niektórzy przeczytali część napisaną przez Hoare’a. Myślę, że niewiele osób dotrwało do końca i przeczytało trzecią część — „Hierarchical Program m ing Structuring” — którą napisał Ole-Johan Dahl we współpracy z Tonym Hoare’em. Treść tej książki to w zasadzie wprowadzenie do języka Simula oraz program ow ania obiektowego. Ja byłem pilnym studentem . Powiedziano mi, żebym przeczytał tę książkę, i przeczytałem ją od początku do końca. Bardzo podobała mi się pierwsza i druga część. Trzecia wydała mi się równie interesująca. To wyjaśnia również, dlaczego kilka lat później, kiedy na scenie pojaw iło się program ow anie obiektowe i większość osób stwierdziła, że jest to technika będąca następcą m etod strukturalnych, dla mnie nie m iało to sensu. Uważam, że programowanie obiektowe było częścią metod strukturalnych od samego początku. Techniki strukturalne dotyczyły programowania w małych fragmentach, natomiast programowanie obiektowe dotyczyło większej skali, ale ogólnie rzecz biorąc, pomiędzy tymi dwom a technikam i nie było luki. Po przeczytaniu tekstu Dahla i H oare’a w iedziałem, że jest to właściwy sposób program ow ania. Kiedy zacząłem pracę w branży w połowie lat siedemdziesiątych, miałem to szczęście, że mój szef pozwolił mi kupić kompilator Simuli. Był dość drogi, ale bardzo dobry. Używałem go bardzo często w firmie, dla której pracow ałem , do tw orzenia bardzo interesującego oprogramowania. Dla mnie było absolutnie oczywiste, że nie istnieje inny sensowny sposób program ow ania, ale większość innych osób uważała m nie za kom pletnie stukniętego. Programowanie obiektowe było wówczas ciągle dość bezpostaciowe. W połowie lat siedemdziesiątych, zaraz po ukończeniu studiów, napisałem razem z moim przyjacielem, Claude’em Baudoinem, książkę w języku francuskim pod tytułem Méthodes de Programmation [Eyrolles], czyli „Metody programowania” . Był to rodzaj kom pendium wiedzy, którą posiadaliśm y — wszystkiego, czego nauczyliśmy się na Uniwersytecie Stanford i w innych miejscach. Książka odniosła olbrzymi sukces. W dalszym ciągu jest wznawiana, co ze względu na rok pierwszego wydania — 1978 — jest dość niezwykłe. Książka służyła do szkolenia — mogę to powiedzieć bez przesady — kilku pokoleń francuskich studentów inżynierii oprogramowania, a także studentów rosyjskich, ponieważ została przetłumaczona w Związku Radzieckim na język rosyjski. W Rosji również odniosła spory sukces. Kiedy jestem w Rosji, wciąż spotykam ludzi, którzy mówią mi, że uczyli się programowania z tej książki. Do wyjaśnienia technik program ow ania, a także algorytmów i struktur danych używaliśmy pseudokodu. Tony Hoare, gdy pokazałem mu tę książkę, powiedział, że interesuje go przetłumaczenie jej na język angielski i wydanie w Prentice Hall w ramach znanej międzynarodowej informatycznej serii książek. Odpowiedziałem: „Dlaczego nie” , na co on zapytał: „Mówisz trochę po angielsku, dlaczego więc sam nie zajmiesz się tłumaczeniem?” .

514

ROZDZIAŁ

SIEDEMNASTY

Zamiast upierać się, że należy znaleźć jakiegoś tłumacza, byłem na tyle głupi, że się zgodziłem. To naprawdę najgłupsza rzecz, jaką zrobiłem w moim życiu: oczywiście w miarę tłum aczenia książki aktualizow ałem ją, ponieważ m inęły już trzy lata od pierwszej publikacji. Byłem już dojrzałym programistą i miałem więcej pomysłów. Nadałem książce tytuł Applied Programming Methodology. Książka niestety nigdy nie została opublikowana, ponieważ nigdy jej nie skończyłem. Przepisywałem od nowa każde zdanie. Było to bardzo niewydajne, ale podczas pisania ulepszałem pseudokod, który wykorzystałem w pierwszej publikacji. W szczególności czułem, że nie jestem w stanie prawidłowo wyrazić programów lub algorytmów bez używania kontraktów. Notacja kontraktów używana w języku Eiffel pow stała podczas pracy nad tym tłumaczeniem. Istotne znaczenie m iał także inny fakt. Pracowałem w branży, ale wziąłem urlop naukowy w celu pracy na Uniwersytecie Stanu Kalifornia w Santa Barbara. Jako osoba z zewnątrz otrzym ywałem więc kursy, których nikt nie chciał wykładać. To była sekwencja kursów 130A i 13OB, Struktury danych i Algorytmy, które spełniały bardzo interesującą rolę, ponieważ miały w rzeczywistości trzy cele. Głównym celem było nauczenie studentów struktur danych i algorytmów. Ale przyświecały tem u także dwa nieudokumentowane cele, naprawdę ważne. Pierwszy cel miał być wystarczająco trudny, aby nie udało się go osiągnąć odpowiedniej liczbie studentów. Dzięki temu ci, którzy przetrwali, mogli zostać pełnow artościow ym i studentam i informatyki. Drugim, niejaw nym celem było nauczenie studentów języka C, poniew aż jego znajom ość była im potrzebna do nauki na innych kursach. To był kom pletny absurd, ponieważ o ile język C jest dobry w pew nych zastosow aniach, o tyle na pew no nie nadaje się do w yrażania algorytmów, nie wspominając już o ich nauczaniu. Było to fatalne doświadczenie, ponieważ zamiast mówić studentom o tym, co miało być przedmiotem kursu, pomagałem im debugować programy, walczyć ze wskaźnikami i wykonywać tym podobne operacje. To nauczyło mnie dwóch rzeczy. Po pierwsze, że nigdy więcej nie chcę mieć do czynienia z językiem C jako językiem dla ludzi. Język C stosunkowo dobrze nadaje się do generowania przez kompilatory, ale idea, że ludzie mają w nim programować, jest kompletnym absurdem. Po drugie, nauczyłem się, że jedynym sposobem prezentacji podstawowych struktur danych i algorytmów jest wstawienie w wielu miejscach niezm ienników pętli, zmiennych pętli oraz w arunków wstępnych i końcowych. Zatem do pewnego stopnia, kiedy na koniec tego roku musiałem zaprojektować notację na potrzeby mojej pracy w firmie, którą właśnie założyliśmy, użyłem języka, którego chciałbym używać do prowadzenia kursu na Uniwersytecie Stanu Kalifornia. Mówiąc bardziej ogólnie, był to efekt lektury wielu materiałów, zagłębienia się we współczesne prace z dziedziny inżynierii oprogramowania takich teoretyków, jak Dahl, Dijkstra, Hoare, W irth, Harlan Mills, David Gries, Barbara Liskov, John Guttag, Jim Horning, oraz postępowania zgodnie z duchem ewolucji języków programowania. Dla mnie było

EIFFEL

515

to oczywisty sposób postępow ania. Właściwie mogę powiedzieć, że Eiffel został zaprojektowany — chciałem powiedzieć, że w ciągu popołudnia, ale to nie było nawet tyle. Eiffel został zaprojektowany w 15 minut. To było coś zupełnie oczywistego. Czy język Eiffel naprowadził pana na koncepcję projektowania kontraktowego?

Bertrand: Nie. To było trochę inaczej. Koncepcje istniały wcześniej. Celem języka jest ich odzwierciedlenie. Nie jestem odpow iednią osobą, do której należałoby adresować to pytanie. Myślę, że to pytanie należy skierować do osób, które nie stosują projektowania kontraktowego. To je należy zapytać dlaczego. Ja po prostu nie mogę zrozumieć, dlaczego ludzie piszą elementy oprogramowania bez zadania sobie trudu wyrażenia, do czego te elementy mają służyć. To jest pytanie, które należałoby zadać Goslingowi, Stroustrupow i, Alanowi Kayowi czy też Hejlsbergowi. Jak m ogą pisać oprogram ow anie czy też projektow ać język umożliwiający innym pisanie oprogram owania bez dostarczenia tego rodzaju mechanizmu? Ja nie potrafię sobie wyobrazić, jak można napisać dwie linijki kodu bez tego mechanizmu. Pytanie o to, dlaczego używamy projektow ania kontraktow ego, jest jak pytanie o to, dlaczego używamy liczb arabskich. To ci, którzy używają liczb rzymskich do m nożenia, pow inni się tłumaczyć. Słyszałem, że projektowanie kontraktowe w języku obiektowym wymusza stosowanie zasady podstawień Liskov. Czy zgadza się pan z tym?

Bertrand: Nigdy do końca nie potrafiłem zrozumieć, czym jest zasada podstawień Liskov. Dla mnie to po prostu polimorfizm. Myślę, że to prawda, z tym zastrzeżeniem, że musi być zapewniona możliwość kompletnych podstawień. Na przykład nie można ograniczać typów dziedziczonych w taki sposób, by wykonywały mniej, niż wykonuje typ macierzysty. Ogólnie rzecz biorąc, trzeba wymusić ten sam kontrakt, ja k i wykorzystuje klasa macierzysta.

Bertrand: Myślę, że to właśnie zostało wprowadzone w języku Eiffel w 1985 roku. Idea osłabienia w arunku wstępnego oraz wzmocnienia w arunku końcowego przy okazji przedefiniowania procedury. Jeśli zatem zasada podstawień Liskov mówi coś takiego, to uważam, że odpowiedź jest twierdząca. Jednak język Eiffel nie czekał na Barbarę Liskov. Podoba mi się koncepcja zbieżnych odkryć.

Bertrand: Z prac Barbary Liskov pochodzi pojęcie, na którym bezpośrednio polegamy — to pojęcie abstrakcyjnego typu danych, opublikowane w pracy z 1974 roku. Miało ono bardzo istotne znaczenie. Oczywiście duży wpływ miały też prace nad językiem CLU w MIT. Jednak zasada podstawień Liskov nigdy nie wydawała mi się niczym specjalnie nowym.

516

ROZDZIAŁ

SIEDEMNASTY

W ja k i sposób projektowanie kontraktowe pomaga zespołom programistów?

Bertrand: Umożliwia różnym grupom wchodzącym w skład zespołu dowiedzieć się, co robią ich partnerzy, bez konieczności wiedzy na temat tego, jak to robią. To pozwala na pobieranie migawek produktów dla wszystkich zespołów jedynie na podstawie specyfikacji i bez potrzeby przywiązywania się do określonej reprezentacji. Jest to również bardzo dobre dla menedżerów. Czy nie występuje zagrożenie nadmiernej specyfikacji rozwiązania?

Bertrand: Nie, właściwie nie. Zagrożeniem zawsze jest niedostateczna specyfikacja. Przy projektow aniu kontraktow ym rzadko dochodzi do nadmiernej specyfikacji. Zagrożenie nadmierną specyfikacją zachodzi wtedy, gdy następuje zbyt szybkie przejście do implementacji zamiast pozostania na poziomie specyfikacji. To jednak nie może się zdarzyć w przypadku kontraktów, ponieważ opisują one zamiar, a nie jego realizację. Problem ze specyfikacją bazującą na kontraktach jest odwrotny: ludzie nie mówią wystarczająco dużo, ponieważ wyspecyfikowanie wszystkiego jest trudne. Czytałem, że w przypadku użycia kontraktów kod nie może nigdy próbować weryfikować warunków kontraktu? Cała idea polega na tym, aby wystąpienie awarii w kodzie było trudne. Czy może pan wyjaśnić tę decyzję?

Bertrand: Jest wiele osób, które twierdzą, że stosują zasady projektowania kontraktowego, a które nie są wystarczająco odważne, aby stosować się do tej reguły. Idea jest bardzo prosta. Dotyczy w arunków w stępnych. Jeśli istnieje w arunek w stępny procedury, który brzmi: „O to w arunki, które pow inny być spełnione”, to w samym kodzie procedury nigdy nie należy sprawdzać kontraktu. Oznacza to, że odpowiedzialność za sprawdzanie kontraktów w czasie wykonywania programu — przy założeniu, że niektóre klienty mogą zawierać błędy i nie spełniać warunków wstępnych — spada na inny mechanizm. Jest to pewien automatyczny mechanizm, który będzie używany podczas testow ania i debugow ania. Jeśli jednak zarów no definiujemy warunek wstępny, jak i sprawdzamy prawdziwość tego samego warunku w kodzie i coś nie działa prawidłowo, wykonujemy tę samą czynność dwukrotnie. Oznacza to, że nie jesteśmy w stanie stwierdzić, czy warunek lub ograniczenie znajduje się w kręgu odpowiedzialności klienta, czy dostawcy. Gotowość do usunięcia w ew nętrznego spraw dzania w arunków w stępnych jest dobrym testem tego, czy jest stosowane projektowanie kontraktowe, a nie pewnego rodzaju programowanie defensywne. Niewielu ludzi ma ochotę to robić. W projektow aniu kontraktow ym jest bardzo czytelna zasada — w arunek wstępny to ograniczenie nałożone na klienta, czyli wywołującego. Jeśli zatem wystąpi naruszenie w arunku wstępnego, jest to wina klienta. Odpowiedzialność za to naruszenie nie leży po stronie procedury. Odpowiedzialność za warunek końcowy leży po stronie dostawcy — czyli procedury. Jeśli m am y do czynienia z w arunkiem wstępnym, za który odpow iada wywołujący, ale sprawdza go sama procedura, to znaczy,

EIFFEL

5 17

że nie przystosowaliśmy odpowiednio swojego sposobu myślenia i duża ilość kodu będzie bezużyteczna. Jest to oczywiście bardzo niebezpieczne, zwłaszcza że bardzo często ten kod nie zostanie uruchomiony podczas testowania, debugowania i procesu wytwarzania oprogramowania. W gruncie rzeczy jest to kwestia poważnego podejścia do specyfikacji. Jak ważne jest rozgraniczenie pomiędzy specyfikacją a implementacją?

Bertrand: To napraw dę dobre pytanie. Owo rozgraniczenie jest bardzo ważne, ale jest to względna różnica. Chcę przez to powiedzieć, że całkowicie niemożliwe okazuje się stwierdzenie, że coś jest absolutną specyfikacją lub że coś jest absolutną im plementacją. To jedna z charakterystycznych cech oprogram owania, że każdy jego element stanowi specyfikację czegoś bardziej konkretnego oraz implementację czegoś, co jest bardziej abstrakcyjne. Weźmy choćby taką konstrukcję, która wygląda bez wątpienia jak implementacja — na przykład instrukcję przypisania := A+l lub A := B. Większość osób powiedziałaby, że to czysta implementacja. W rzeczywistości jednak, z punktu widzenia autorów kom pilatorów , jest to specyfikacja czegoś, co będzie rozwinięte na kilkanaście instrukcji maszynowych albo instrukcji w języku C. Rozgraniczenie jest ważne, ale prawdziwa trudność w oprogramowaniu polega na tym, że przy odpowiednich rozm iarach i skali techniki używane do pisania im plementacji są bardzo podobne do technik wykorzystywanych do pisania specyfikacji. Istnieje na przykład oczywiste zjawisko dla każdego, kto pisze specyfikacje formalne: kiedy piszemy odpow iednio duże specyfikacje formalne, to w gruncie rzeczy wykonujem y czynności i zadajemy pytania, które są bardzo bliskie czynnościom wykonywanym i pytaniom zadawanym podczas pisania programów. A zatem różnice zawsze są względne. Powodem jest to, że w przypadku oprogramowania nie mamy do czynienia z obiektami fizycznymi. Nigdy nie mamy do czynienia z konkretnymi, namacalnymi i materialnymi obiektami. Mamy do czynienia wyłącznie z abstrakcjami, a zatem różnica pom iędzy im plem entacją i specyfikacją jest w zasadzie jednym z poziomów abstrakcji. Tak więc zazwyczaj stwierdzenie, że coś jest implementacją lub specyfikacją, okazuje się bez znaczenia. Można powiedzieć, że X jest specyfikacją Y. Inaczej mówiąc, Y jest implementacją X. To zdanie ma sens, ponieważ jest sprawdzalne. N atom iast stwierdzenia „X jest specyfikacją” lub „X jest im plem entacją” są niesprawdzalne. Nie można na nie udzielić precyzyjnej odpowiedzi: tak lub nie. Jakie jest powiązanie pomiędzy językiem programowania a projektem oprogramowania napisanego w tym języku?

Bertrand: Jedna z unikatow ych własności języka Eiffel — jedna z oczywistych właściwości oprogramowania, których nikt inny nie uznaje za oczywiste czy nawet prawdziwe — polega na tym, że łańcuch pomiędzy koncepcją a realizacją jest w całości ciągły. To właśnie nazywamy „bezszwowym wytw arzaniem oprogram ow ania” .

518

ROZDZIAŁ

SIEDEMNASTY

Jest to prawdopodobnie najważniejszy aspekt języka Eiffel. Wszystko inne ma wspierać tę własność. Jest to na przykład koncepcja, zgodnie z którą nie istnieje prawdziwa różnica pomiędzy projektem a implementacją. Implementacja, sparafrazuję tu znane powiedzenie, jest po prostu projektem realizowanym innymi środkami. Różnica polega tylko na poziomie szczegółowości oraz poziomie abstrakcji. W szczególności język Eiffel został zaprojektowany bardziej jako język analizy i projektowania niż jako język implementacji. Ogólnie rzecz biorąc, jest on właściwie bardziej metodą niż językiem, ale w tej części, w jakiej jest językiem, służy zarów no analizie, projektow aniu, jak i im plementacji. Ludzie używający Eiffela zwykle nie używają języka UML lub podobnych narzędzi. Dla projektanta Eiffela jest to w dużym stopniu szum, który ma niewielki związek ze wszystkimi cechami, które są użyteczne dla oprogramowania. Eiffel jest narzędziem, które ma być pomocne. Pan mówi o projekcie, ale ja powiedziałbym, że zaczyna się od poziom u specyfikacji i analizy, dalej jest projekt, a następnie im plem entacja — Eiffel ma pomagać na wszystkich tych etapach. Nie istnieje jednak jakaś zasadnicza luka pomiędzy tymi zadaniami zarówno w mojej opinii, jak i w opinii programistów Eiffela w ogóle. Należałoby także wspomnieć o tym, że język powinien być jak najbardziej skromny. Wiele języków będących dziś w użyciu można zaliczyć do języków wysokich kapłanów. Zawierają one wiele dziwnych symboli i konwencji, które trzeba zrozumieć, aby zostać przyjętym do kręgu w tajem niczonych. Na przykład wiele spośród dom inujących współczesnych języków sięga do języka C. Powstają one w wyniku ciągłego dodawania i usuw ania własności z języka C. Aby je poznać, trzeba zrozumieć wiele różnych zagadnień. Idea języka Eiffel — nie mogę powiedzieć, że Eiffel został całkowicie pozbaw iony jakiegokolwiek bagażu, choć jest on bardzo niewielki — jest taka, że jeśli myślimy o projekcie, to koncentrujem y się na projekcie, a nie na języku. Największym kom plem entem , jaki kiedykolwiek słyszałem od ludzi używających Eiffela, było to, że kiedy używają tego języka, koncentrują się wyłącznie na problemie. Jest to najlepszy z wpływów, jaki język może wywierać na projekt. Czy kiedykolwiek rozważał pan inne rozwiązanie niż obsługa obiektów na poziomie języka? Co by pan powiedział o użyciu komponentów podobnych do niewielkich narzędzi wbudowanych w system Unix, które można połączyć ze sobą za pomocą potoków w celu tworzenia złożonych własności? W końcu kiedy piszemy do kogoś wiadomość albo list w języku angielskim, włoskim czy francuskim i chcemy coś opisać, nie używamy koncepcji obiektów.

Bertrand: Oczywiście mechanizmy uniksowe są bardzo eleganckie. Są one jednak zbyt szczegółowe jak na narzędzia potrzebne do tw orzenia rozbudow anego oprogram ow ania. Zgodnie z moim doświadczeniem obiekty to jedyny mechanizm, który udow odnił swoje możliwości skalow ania do dużych systemów. Innym

EIFFEL

519

podejściem, które ewentualnie m ógłbym brać pod uwagę, jest program ow anie funkcyjne, choć nie jestem pewien, czy spełniłoby ono swoją rolę. To atrakcyjna koncepcja — bardzo elegancka i można się z niej dużo nauczyć, ale na najwyższym poziomie obiekty są lepsze. Obiekty — właściwie pow inienem powiedzieć klasy — są znacznie skuteczniejsze w przechwytywaniu struktury systemów na dużą skalę. Nie tyle język naturalny jest analogią, ile opis m atem atyczny. Jak w spom niałem wcześniej, chodzi bardziej o klasy niż o obiekty. Klasy to nic innego, jak przeniesienie na grunt programowania pojęcia struktur, które dobrze się sprawdzają w matematyce: grup, pól, pierścieni itp. M atem atyka posługuje się obiektami, które m ają bardzo naturalny charakter — na przykład liczbami i funkcjami — i pokazuje, że w obu przypadkach mamy tę samą strukturę zdefiniowaną przez operacje o tych samych właściwościach. N astępnie tworzy się abstrakcję w postaci pojedynczego pojęcia — na przykład grupy, m onoidu bądź pola. Te pojęcia sprawdzały się bardzo dobrze w matematyce w ciągu ubiegłych 2000 lat. Pod tym względem klasy, czy też obiekty, nie są takim now ym pojęciem. Jest to po prostu bezpośrednia transpozycja standardow ego pojęcia struktury matematycznej. Wiele współczesnych systemów jest podzielonych na komponenty i rozproszonych w sieci. Czy w języku powinny się znaleźć mechanizmy obsługujące te aspekty pracy sieciowej?

Bertrand: Jest to pożądana właściwość i Eiffel um ożliwia jej wykorzystywanie, ale ja nie powiedziałbym, że to jest część, w której Eiffel najbardziej błyszczy. Obecnie prowadzonych jest wiele prac dotyczących dynamicznej aktualizacji i współbieżności. Będą one widoczne za kilka kolejnych miesięcy, ale na razie jeszcze nie są dostępne. Tak. Myślę, że to jest ważne! Można by się spierać, czy mechanizmy te powinny się znaleźć w języku, czy w implementacji, ale jakieś wsparcie ze strony języka będzie potrzebne. Jakie związki dostrzega pan pomiędzy paradygmatem obiektowym a współbieżnością?

Bertrand: Uważam, że współbieżność ma olbrzymie znaczenie. Bardzo dużo o tym pisałem. Istnieje wiele pozycji literatury na temat opracowanego przez nas modelu SCOOP, który dotyczy współbieżnego programowania obiektowego. Podstawowa zasada jest taka, że naiwne rozwiązania nie działają. Istnieje pewna tendencja do tego, by mówić: „Tak, obiekty współbieżne to rodzaj tego samego pomysłu, który musi dobrze działać. Obiekty w naturalny sposób są w spółbieżne” . Niestety, realia nie są tak różowe. Koncepcja współbieżnych obiektów nie będzie działać, jeśli pomieszamy pojęcie programowania obiektowego ze współbieżnością na poziomie podstawowym. Spróbuję powiedzieć kilka słów na tem at metody SCOOP. Przede wszystkim należy zdać sobie sprawę, że standardow e pojęcie kon traktu nie może być tak samo interpretow ane w kontekście program ow ania współbieżnego, jak w odniesieniu do programowania sekwencyjnego. Metodologia SCOOP ma prowadzić do rozszerzenia

520

ROZDZIAŁ

SIEDEMNASTY

modelu programowania obiektowego w najprostszy możliwy sposób umożliwiający obsługę współbieżności. Jest to idea, która znacznie różni się od innych. Na przykład w rachunku procesów przyjmuje się dokładnie odwrotne podejście: pytamy, jaki jest najlepszy mechanizm dla współbieżności, a następnie dodajemy pozostałe elementy na wierzchu. W ten sposób uzyskujemy coś, co bardzo się różni od standardowych sposobów program ow ania. Idea m etody SCOOP polega na tym, że ludzie mają problemy z rozumowaniem w sposób współbieżny, ale potrafią rozumować znacznie skuteczniej, jeśli robią to sekwencyjnie. W związku z tym metoda SCOOP pozwala programistom programować w sposób współbieżny, ale taki, który jest bardzo zbliżony do programowania sekwencyjnego. Taki sposób pozwala na zachowanie zwykłego sposobu myślenia. Na jakim poziomie powinniśmy obsługiwać współbieżność? Na przykład maszyna JVM zarządza niektórymi elementami w sposób niemal przezroczysty.

Bertrand: Wątki Javy okazały się bardzo przydatne w wielu zastosowaniach, jednak ich koncepcja nie jest blisko związana z podejściem obiektowym. Ogólnie rzecz biorąc, są to — w ujęciu Dijkstry — semafory. W języku Eiffel jest dostępna biblioteka o nazwie EiffelThreads, która oferuje mniej więcej to samo. Dla każdego zapewne jest oczywiste, że takie rozwiązania są dobre na krótką metę, ale się nie skalują. W dalszym ciągu jest zbyt wiele okazji do wystąpienia sytuacji wyścigu i zakleszczeń. Nadrzędnym celem pow inna być próba autom atycznej ochrony program istów przed tymi problemami. To wymaga pracy na wyższym poziomie ekspresji. Jak pan zasugerował, oznacza to, że coraz więcej pracy trzeba wykonać w implementacji.

Wielokrotne wykorzystywanie kodu i generyczność W ja k i sposób język Eiffel postępuje z modyfikacjami oraz ewolucją programów?

Bertrand: Rozszerzalność, razem z możliwościami wielokrotnego wykorzystywania kodu, od samego początku była głów nym celem projektu. Do pewnego stopnia jest to pow ód, dla którego projekt języka Eiffel był kontynuow any, ponieważ — jak powiedziałem — początkow o opracowaliśm y ten język jako narzędzie w ew nętrznego użytku, a nie jako coś, co będziemy sprzedawać reszcie świata. Pomyśleliśmy o rozszerzeniu dostępności Eiffela dopiero wtedy, kiedy programiści zaczęli mówić, że Eiffel różni się od języków, które wykorzystywaliśmy wcześniej, głównie tym, że pozwala na znacznie łatwiejsze zmienianie zdania. M ożna było to robić bez ponoszenia konsekwencji niezdecydowania. Myślę, że kluczowe znaczenie ma kilka aspektów. Po pierwsze, w języku Eiffel dość uważnie jest przeprowadzane ukrywanie informacji. Ma to na celu wyizolowanie sytuacji, w której niektóre m oduły wzajemnie pokazują występujące w nich zmiany. Nie istnieje ukrywanie informacji dla potomków, ponieważ to nie ma sensu, ale istnieje ukrywanie informacji dla klientów. Na przykład naprawdę uderzające i nieco szokujące

EIFFEL

521

jest to, że we współczesnych językach obiektow ych w dalszym ciągu m ożna przypisywać wartości bezpośrednio do atrybutu oraz do pola obiektu. Nie m ożna tego zrobić w języku Eiffel, ponieważ pow oduje to naruszenie zasady ukryw ania informacji. Dla oprogramowania to jest katastrofa. Poza tym m echanizm dziedziczenia jest bardzo elastyczny i pozwala na pisanie oprogramowania poprzez różnicowanie istniejących wzorców. Dodatkowy poziom elastyczności dają typy generyczne. Absencja mechanizmu języka powyżej poziomu klas umożliwia łączenie klas w bardzo elastyczny sposób. K ontrakty również są przydatne, poniew aż w przypadku m odyfikow ania oprogram ow ania ważne jest to, abyśmy wiedzieli, co zm ieniam y — w szczególności czy zm ieniam y aspekty specyfikacji, czy tylko fragmenty implementacji. Kiedy modyfikujemy oprogram ow anie, musimy zdecydować, czy jest to czysto wewnętrzna zmiana, która nie będzie miała wpływu na kontrakty. W takim przypadku w iadom o, że zm iana nie będzie m iała żadnego wpływu na klienty. Jeśli natom iast modyfikacja pow oduje zmianę kontraktów , to należy dokładnie się przyjrzeć temu, w jaki sposób się to dzieje. Dzięki tem u uzyskamy właściwy poziom szczegółowości w zarządzaniu zakresem zmian. Uważam, że między innym i te mechanizmy w najbardziej podstawowy sposób obsługują rozszerzalność. W ja k i sposób programiści powinni myśleć o wielokrotnym wykorzystywaniu kodu? Pytam dlatego, że pewne osoby, z którymi rozmawiałem, powiedziały mniej więcej tak: nawet jeśli budujemy klasy, to nie powinniśmy koncentrować się na możliwości ich wielokrotnego wykorzystywania. Projektowanie klas, które będzie można wykorzystać wielokrotnie, jest bowiem bardzo trudne. Na tym aspekcie powinniśmy skupić się tylko wtedy, gdy odkryjemy, że określonej klasy używamy w różnych kontekstach wiele razy. W tym momencie warto poświęcić dodatkowy czas na to, by umożliwić je j wielokrotne wykorzystywanie.

Bertrand: Myślę, że jest to prawda tylko wtedy, gdy programista nie czuje się zbyt dobrze w programowaniu kodu do wielokrotnego użytku lub gdy jest nowicjuszem. Jeśli ktoś nie ma doświadczenia w tworzeniu kodu wielokrotnego użytku, to może się zdarzyć, że poświęci mnóstwo czasu na próbę stworzenia oprogramowania, które ma być bardziej ogólne, niż to wynika z wymagań w danym momencie, i nie osiągnie sukcesu. Uważam za to, że jeżeli program ista jest dobry w tw orzeniu kodu wielokrotnego użytku, posiada długotrwałe doświadczenie w pracy z komponentami w ielokrotnego użytku tw orzonym i przez innych, a także ma doświadczenie w przystosowywaniu własnego oprogramowania do wymagań wielokrotnego użytku, zrobi to dobrze. Błąd popełniany przez wiele osób polega na tym, że nie rozumieją one istnienia dwóch aspektów wielokrotnego wykorzystywania kodu oraz tego, że jeden musi poprzedzać drugi. Istnieje aspekt konsum enta oraz aspekt producenta. W konsumenckiej formie

522

ROZDZIAŁ

SIEDEMNASTY

w ielokrotnego wykorzystywania kodu używamy istniejącego oprogram ow ania na potrzeby własnych aplikacji. Wiele osób robi to głównie w celu zaoszczędzenia czasu. W producenckiej formie wielokrotnego wykorzystywania kodu przystosowujemy własne oprogramowanie do możliwości wielokrotnego używania. Jeśli ktoś próbuje być producentem komponentów wielokrotnego użytku od samego początku, poniesie porażkę, ponieważ tworzenie tego rodzaju kom ponentów wymaga stosow ania specyficznych technik. W takiej sytuacji program ista poświęca m nóstw o czasu na poprawienie uniwersalności swojego kodu. Tymczasem trzeba odgadnąć, w jakim kierunku oprogramowanie może być uogólniane później. Z reguły nowicjusze nie odgadują tego kierunku poprawnie, ponieważ nie jest to łatwe. Jeśli jednak przyjąć nieco skromniejszą postawę i zacząć jako konsument — studiować kod wielokrotnego użytku wysokiej jakości, analizować sposób jego projektowania, przyglądać się interfejsom API — m ożna łatw o nauczyć się odpowiedniego stylu i spróbować zastosować go do własnych programów. To jest właśnie sposób, w jaki to działa w społeczności użytkowników języka Eiffel. Ludzie uczą się programować w Eiffelu, przyglądając się standardow ym bibliotekom , takim jak EiffelBase czy EiffelVision w przypadku grafiki. Są to biblioteki wysokiej jakości, które mogą służyć za model dobrego oprogramowania. Kiedy się je przestudiuje, wtedy można zastosować te same zasady do własnych programów. W ten sposób mogą one stać się znacznie lepsze, a w szczególności bardziej przystosowane do w ielokrotnego użytku. To jest odpow iedni sposób działania: należy rozpocząć jako konsum ent i uczyć się na podstawie własnego doświadczenia, jak stać się producentem. Z moich obserwacji wynika, że takie postępowanie jest skuteczne. To właśnie była myśl przewodnia mojej książki Reusable Software [Prentice-Hall]. Wykorzystanie takiego podejścia pozwala przystosować oprogramowanie do wymagań kodu wielokrotnego użytku. Według zwolenników technik programowania zwinnego (czy też program ow ania ekstremalnego) nie należy przejmować się w ielokrotnym wykorzystywaniem kodu, ponieważ to strata czasu. Uważam, że jest to prawdziwe tylko w stosunku do osób, które nie są zbyt dobre w programowaniu komponentów wielokrotnego użytku. Osoby te nie podjęły bowiem wysiłku, aby nauczyć się sposobów tworzenia dobrego oprogramowania wielokrotnego użytku poprzez analizę dobrych modeli. Być może jest to również kwestia użytego języka programowania.

Bertrand: Z całą pewnością tak. Nie musi pan mnie zbyt m ocno naciskać, abym to przyznał. Eiffel został zaprojektowany w celu osiągnięcia trzech celów. Pierwszym z nich była popraw ność lub — bardziej ogólnie — niezawodność. Drugim była rozszerzalność, łatwość m odyfikow ania oprogram ow ania, a trzecim możliwości wielokrotnego wykorzystania. A zatem możliwości wielokrotnego używania kodu są wbudowane w język. Warto na przykład zauważyć, że od samego początku w języku Eiffel istniały klasy generyczne. M iało to absolutnie podstaw ow e znaczenie dla

EIFFEL

5 23

możliwości wielokrotnego wykorzystania kodu, ale przez wiele lat ludzie się z tego śmiali. Na pierwszej konferencji OOPSLA w 1986 roku nasza firma przygotowała stoisko z ulotkami na ten temat. Ludzie przychodzili do naszego stoiska i śmiali się ze słowa generyczność (ang. genericity), które — jak m ów iono — nie było naw et poprawnym angielskim słowem. Nikt nie miał wtedy pojęcia, co to znaczy. Kilka lat później w języku C++ wprowadzono pojęcie szablonów. Kiedy w 1995 roku pojaw iła się Java, nie było w niej typów generycznych. M ów iono, że nie są one konieczne, że to jedna z komplikacji programowania obiektowego, która wprowadza bałagan w języku. Dziesięć lat później w Javie pojawiły się typy generyczne, ale w moim odczuciu były zbyt skomplikowane i nie spełniały swojej roli. Największym problemem był może nie tyle zły projekt, ile ograniczenia zgodności. Kiedy pojawił się język C#, nie mogłem uwierzyć, że pom im o doświadczeń z Javą także nie było w nim typów generycznych. Wreszcie, około siedem lat po pierwszej publikacji języka, dodano obsługę typów generycznych. Konieczność istnienia typów generycznych w języku Eiffel została dostrzeżona na samym początku, a motywacją były możliwości wielokrotnego wykorzystywania kodu. Szczegóły mechanizmu dziedziczenia, kombinacje zmiany nazwy, redefiniowania, anulowania definicji obecne w języku Eiffel, mechanizm powtarzanego dziedziczenia — wszystko to jest uzasadnione i m otyw ow ane możliwościami wielokrotnego wykorzystywania kodu. Oczywiście dla zapewnienia możliwości wielokrotnego wykorzystywania kodu kluczowe znaczenie mają kontrakty. Powiedziałem wcześniej, że nie rozumiem, jak można programować bez kontraktów, ale jeszcze trudniejsze do zrozumienia jest to, jak m ożna tworzyć kom ponenty pozornie wielokrotnego użytku bez czytelnej specyfikacji tego, co te komponenty miałyby robić. Coraz częściej ludzie to rozumieją. Być może słyszało o tym niewiele osób, ale w wydaniu platform y .NET 4.0 ma się pojawić biblioteka obsługi kontraktów Code Contracts, a wszystkie biblioteki bazowe Mscorelib mają być zaprojektowane od początku z wykorzystaniem kontraktów. To aż 23 lata po języku Eiffel. Wreszcie zrozumiano, że nie można zapewnić możliwości wielokrotnego wykorzystywania kodu bez kontraktów. Do tego był potrzebny czas. Oczywiście przez cały ten czas w języku Eiffel w prow adzano nowe pojęcia. Dzięki tem u Eiffel w dalszym ciągu pozostaje na przedzie, krok przed innymi językami. Kiedy i w ja k i sposób doszedł pan do wniosku, że typy generyczne są tak samo ważne ja k klasy?

Bertrand: Ten konkretny wniosek wynikał bardziej z kontekstu akademickiego niż z bezpośredniej potrzeby praktycznego zastosowania. W czasie trwania mojej kariery przez większość czasu pracowałem w branży, choć miałem epizody akademickie. Kilkakrotnie w 1984 i 1985 roku prowadziłem kurs na Uniwersytecie Stanu Kalifornia w Santa Barbara o nazwie Zaawansowane koncepcje w językach program owania. Był to przedmiot, który mogłem wykładać dość swobodnie.

524

ROZDZIAŁ

SIEDEMNASTY

Postanowiłem się przyjrzeć, jakie koncepcje były wiodącymi pojęciami języków program ow ania. Uwzględniłem zarów no język Ada, który generował m nóstw o energii, jak i Simula, który w ogóle nie generował energii. Wiedziałem jednak, że język Simula to powiew przyszłości ze względu na występujące w nim koncepcje program ow ania obiektowego. Podczas prowadzenia tego kursu musiały rodzić się pewne pytania, ponieważ w jednym tygodniu mówiłem o typach generycznych, natomiast w następnym o dziedziczeniu lub odwrotnie. Naturalnym pytaniem było to, jak można porównać te dwie rzeczy. Nie pamiętam, czy pytanie to zadał jakiś konkretny student, ale być może tak było. Pamiętam, że zadałem sobie pytanie: „Czy m am zamiar być jednego tygodnia doktorem Generyczność, a następnego magistrem Dziedziczenie?” . To doprowadziło mnie do postawienia kolejnego pytania: „Co mogę zrobić z jednym, czego nie mogę zrobić z drugim?” i na odwrót. Oczywiście był to efekt wielu dyskusji i wcześniejszych refleksji, ale społeczność języka programowania została podzielona pomiędzy tych, którzy uważają, że Ada jest wszystkim, jeśli chodzi o elastyczność języka programowania, oraz niezbyt liczny klub tych, którzy odkryli programowanie obiektowe i dziedziczenie. Na grupach roboczych często m ożna spotkać takie oto dyskusje — „Mój język jest lepszy”, „Nie, nie, mój język jest lepszy” . Z mojego doświadczenia wynika, że nikt nie zadaje sobie trudu, by pójść trochę dalej i spróbować zrozumieć relacje pomiędzy poszczególnymi mechanizmami oraz porównać siłę ekspresji każdego z nich. Na potrzeby mojego kursu przedstaw iłem analizę porów naw czą tych dwóch mechanizmów. Wtedy nadszedł termin składania artykułów na pierwszą konferencję OOPSLA. Było oczywiste, że na konferencji wygłoszę właśnie ten referat. Usiadłem i napisałem to, co myślę, i to, co zrozumiałem. Swoje powody wyjaśniłem na pierwszej konferencji OOPSLA w referacie „Typy generyczne a dziedziczenie” . Opublikował pan ten artykuł, zanim w najbardziej popularnych językach obiektowych zorientowano się, że istnieje problem. Nawet w Smalltalku nie funkcjonuje rozwiązanie tego problemu?

Bertrand: W Smalltalku nie ma to znaczenia. Jest to język o dynamicznej typizacji, zatem nie ma potrzeby, by występowały w nim tego rodzaju mechanizmy. Książka odniosła olbrzymi sukces. To chyba cytat z Schopenhauera. Brzmi mniej więcej tak: „Najpierw się z ciebie śmieją, a potem ...” . ...najpierw cię ignorują, a potem się z ciebie śmieją. Tak.

Bertrand: Właśnie tak było w rzeczywistości. Dosłownie. Na pierwszej konferencji OOPSLA miałem referat z pieczęcią Uniwersytetu Stanu Kalifornia w Santa Barbara.

EIFFEL

525

Był to zatem artykuł w pełni akademicki. Moja firma też miała stoisko poświęcone temu tematowi. Była to zupełna prowizorka, ponieważ w ogóle nie mieliśmy pieniędzy. Materiały drukowaliśmy sami. Były na nich nawet ręczne dopiski. Ludzie przychodzili do naszego stoiska i śmiali się z nas, a potem przyprowadzali swoich znajomych, żeby pośmiać się razem. Jak wspomniałem wcześniej, jedną rzeczą, z której się śmiano, było słowo generyczność (ang. genericity). Byli tacy wielcy faceci z HP. Kiedy mówię „wielcy”, to naprawdę mam na myśli, że byli wielcy. Niektórzy z HP przychodzili do naszego stoiska po kilka razy i za każdym razem przyprowadzali innego kumpla. Pokazywali sobie słowo „generyczność” i szydzili:,Jak się to wymawia? To musi być jakieś francuskie słow o” . Próbowali głośno różnych sposobów w ymawiania: „To chyba wymawia się generisissyty!” itd. Taki był duch tam tych czasów. A jakieś 20 lat później natknąłem się na artykuł, z którego się dowiedziałem, że typy generyczne zostały wprowadzone w języku Java. Na tym po części polega paradoks życia.

Szlifowanie języków Słyszałem, że włada pan co najmniej trzema językam i naturalnymi: angielskim, oczywiście francuskim i niemieckim. Czy poliglotyzm pomógł panu w pracy projektanta oprogramowania?

Bertrand: Krótka odpowiedź: tak. Mój niemiecki właściwie nie jest najlepszy. Francuski to mój język ojczysty. Angielski... staram się, jak mogę. Mówię dosyć płynnie po rosyjsku. W łaściwie m am tytuł m agistra z języka rosyjskiego. Mój poziom rosyjskiego nawet się jednak nie zbliża do poziomu osób posiadających tytuł magistra. Dość dobrze w ładam też włoskim. Potrafię w ykładać po rosyjsku bez większych problemów. Potrafię wykładać po włosku przez jakiś kwadrans, a potem mój mózg się przegrzewa. Tak czy owak, odpowiedź na pana pytanie brzmi z całą pewnością twierdząco. Do informatyki częściowo przyciągnęło mnie moje zainteresowanie językami. Wiedza o tym, że istnieje kilka sposobów na wyrażenie tego samego, że określone wyrażenia niekoniecznie pasują do siebie jeden do jednego, że m ożna położyć różny akcent na różne elementy, że czasami rzeczownik jest właściwym rozwiązaniem, a innym razem czasownik wyraża niuanse, które staramy się przekazać — to z całą pewnością miało istotny wpływ na mnie i bardzo mi pomogło. Uważam również, że osoby, które znają co najmniej jeden język obcy, lepiej posługują się swoim językiem ojczystym. Co więcej, duża część działań, jakie wykonuje się w technice, a w program owaniu w szczególności, to nie samo pisanie programów, ale także pisanie po angielsku bądź w innym języku naturalnym. Warto więc poświęcić czas na poprawienie umiejętności pisania oraz nauczenie się jednego lub kilku języków — ich znajomość jest niezwykle pomocna.

526

ROZDZIAŁ

SIEDEMNASTY

Czy do programowania podchodzi pan z perspektywy matem atyka, lingwisty, czy też jest to kombinacja tych dwóch podejść?

Bertrand: Chciałbym podchodzić do program ow ania w bardziej m atem atyczny sposób, niż to robię. Jestem przekonany, że za 50 lat programowanie będzie po prostu gałęzią matematyki. Są osoby, które od dłuższego czasu prom ują m atem atyczne podejście do program ow ania. To podejście jeszcze się całkiem nie przyjęło poza kilkoma wybranymi obszarami, gdy ludzie nie m ają wyboru, ponieważ budują dość małe, ale bardzo ważne systemy. W końcu programowanie jest zastosowaniem matematyki — opisu matematycznego, który może być zinterpretowany przez maszynę. Uważam, że programowanie będzie miało jeszcze bardziej matematyczny charakter w przyszłości. Jeśli chodzi o moje w łasne podejście, uważam, że jest to kom binacja tego, co pan nazw ał podejściem lingwistycznym, bardziej spontanicznym , twórczym i dyskursywnym, oraz tego, co określiłbym próbą stosow ania ścisłego podejścia matematycznego. Z całą pewnością na Eiffela matematyka wywarła większy wpływ niż na większość innych języków program ow ania. No może poza językami funkcyjnymi, takimi jak Haskell. Pytałem wielu projektantów języków, co sądzą o tym, by wyjść od niewielkiego, ścisłego rdzenia języka i tworzyć resztę mechanizmów na tej bazie. Weźmy na przykład rachunek lambda. Kiedy stworzymy funkcję, możemy obliczyć wszystko. Co pan sądzi o takim podejściu?

Bertrand: Nie sądzę, aby było to tak bardzo pomocne. Programowanie jest kombinacją nauki i inżynierii. Ma z całą pewnością aspekt naukowy. Jak powiedziałem, program ow anie to właściwie matematyka. Jednak druga strona to aspekt inżynierii. Jeśli weźmiemy za przykład niektóre istniejące dziś program y, to są one bardziej złożone od jakichkolwiek innych produktów działalności człowieka. Duże systemy operacyjne — na przykład dystrybucja Linuksa, Visty, Solarisa — składają się z dziesiątek milionów wierszy kodu. Często jest to ponad 50 milionów. To niezwykle złożone konstrukcje inżynierskie. Wiele problemów, które muszą one rozwiązać, to w gruncie rzeczy problemy inżynierskie. Różnica pomiędzy nauką a inżynierią polega na stosowaniu pewnych uproszczeń. W nauce potrzebujemy kilku bardzo inteligentnych pomysłów, natomiast w inżynierii trzeba uwzględnić bardzo dużą liczbę detali. Większość z nich nie jest zbyt skomplikowana, ale są one bardzo liczne. Różnica jest taka, że z jednej strony mamy kilka bardzo inteligentnych elementów, a z drugiej wiele elementów niespecjalnie trudnych. W przypadku programowania interesujące jest to, że potrzebujemy i nauki, i inżynierii. Z pozoru może się wydawać, że to przeczy temu, co powiedziałem przed chwilą — że za kilka dziesięcioleci programowanie będzie w gruncie rzeczy gałęzią matematyki — ale uważam, że nie ma tu sprzeczności. Spróbuję to wyjaśnić.

EIFFEL

5 27

W zasadzie programowanie nie jest niczym innym, jak matematyką, ale akcent należy położyć na zwrot „w zasadzie” . W praktyce programowanie obejmuje również wiele aspektów inżynierskich, o które musimy zadbać. Jeśli piszemy system operacyjny, musimy rozwiązać problem tysięcy sterowników urządzeń napisanych przez naiwnych programistów i zadbać o to, by nie spowodowały awarii naszego systemu operacyjnego. Musimy zadbać o wszystkie języki naturalne i dialogi prowadzone przez ludzi. Musimy zastosować bardzo złożony zbiór mechanizmów dla interfejsów użytkownika. Mimo że podstawowe idee są proste, detali jest bardzo dużo. Trudność programowania ma dwa aspekty: naukowy oraz inżynierski. Bardzo solidne podstawy matematyczne — na przykład rachunek lambda — pomagają w pierwszej części, ale nie w drugiej. Pomagają też w części, która jest dziś najbardziej zrozumiała. Rachunek lambda doskonale nadaje się do modelowania zasadniczej części języka programowania, takiego jak języki Pascal lub Lisp. Jednak nowocześniejsze języki programowania są o wiele bardziej ambitne. Ostatecznie musimy wszystko sprowadzić do bardzo prostych matematycznych zasad. Obecnie jednak same zasady matematyczne nie wystarczają do obsługi współczesnych wyzwań programowania na dużą skalę. Czy taka jest właśnie różnica pomiędzy akademickim a branżowym językiem programowania?

Bertrand: Zdecydowanie tak. Kiedy powstawał projekt języka Ada, ludzie krytykowali go, uznawszy za zbyt rozbudow any i za bardzo złożony. W tedy Jean Ichbiach, projektant Ady, pow iedział w jakimś wywiadzie: „M ałe języki rozwiązują małe problemy” . W tym stwierdzeniu jest sporo prawdy. To była odpowiedź na krytykę takich osób, jak Wirth, który wyraża pogląd, że małe jest piękne. W gruncie rzeczy W irth miał sporo racji. Czy istnieje coś pomiędzy programowaniem strukturalnym a obiektowym? Wspomniał pan, że pańskim zdaniem programowanie strukturalne było techniką dobrą do tworzenia niewielkich programów, natomiast do projektowania dużych programów bardziej nadaje się programowanie obiektowe. Czy istnieje zbiór programów znajdujących się pomiędzy tymi dwiema granicami?

Bertrand: Nie. Uważam, że nie ma potrzeby używania niczego innego niż programowanie obiektowe. Osobiście nauczyłem się obu technik w tym samym czasie. Nie widzę pow odów , żeby używać czegokolwiek innego niż techniki obiektowe do wszystkich zadań, z wyjątkiem niewielkich skryptów. Programowanie obiektowe po prostu oznacza stosowanie matematycznego pojęcia struktury programów. Nie istnieją dobre argumenty przeciwko stosowaniu tej techniki.

528

ROZDZIAŁ

SIEDEMNASTY

Istnieją albo małe programy, albo duże?

Bertrand: Nie istnieją klarowne argum enty przeciwko używ aniu klas. Nie wiem, co sądził o tym Dijkstra. Nigdy nie należał do gorących zwolenników programowania obiektowego, ale nie słyszałem również, aby krytykował tę technikę, a Dijkstra bardzo głośno wyrażał swoje poglądy, jeśli coś m u się nie podobało. Powiedział pan, że jednym z mechanizmów potrzebnych do języka Eiffel była serializacja strumieni. Czy mogę zapytać, do czego pan je j potrzebował?

Bertrand: Pierwszą aplikacją, którą tworzyliśmy w Eiffelu, był inteligentny edytor, który sprzedawano pod nazwą ArchiText. W przypadku edytora przez cały czas pracuje się na małych strukturach danych przechowywanych w pamięci, które później trzeba zapisać na dysku. Jednym ze sposobów, by to zrobić, jest parsowanie tekstu za każdym razem, ale to m etoda absurdalna. Przypuśćmy, że poddajem y tekst edycji i zmodyfikowaliśmy strukturę małego fragmentu tekstu. Tekst jest reprezentowany za pom ocą abstrakcyjnego drzewa składni lub innej reprezentacji wewnętrznej. Nie chcemy zamieniać tej struktury na tekst, a następnie zamieniać go w drugą stronę, za każdym razem. Chcemy używać abstrakcyjnej struktury danych przez cały czas. Aby zapisać tekst, po prostu wciskamy przycisk, a resztą zajmuje się m echanizm obsługi strumieni. To była nasza pierwsza aplikacja, ale wszystkie kolejne były bardzo podobne. Takie same zasady stosujemy, kiedy piszemy kompilator. Załóżmy, że kompilator ma kilka przebiegów. Każdy przebieg potrzebuje struktury danych z poprzedniego przebiegu. Jest ona następnie przetwarzana, a wynik zostaje zapisany na dysku. Nie chcemy pisać tego jawnie za każdym razem. Chcemy tylko wcisnąć przycisk. Dziesiątki aplikacji potrzebują tego rodzaju mechanizmów. Pomiędzy skrajnymi fazami można wprowadzić dodatkowe.

Bertrand: Zgadza się. Nie jesteśmy ograniczeni do jednej konkretnej struktury przetwarzania. Wspomniał pan o czymś, co nazwał pan „bezszwowym wytwarzaniem oprogramowania". Powiedział pan, że jes t to podstawowy aspekt języka Eiffel. Co to jest „bezszwowe wytwarzanie oprogramowania"?

Bertrand: To pomysł, którego zasadniczą treścią jest zapewnienie jedności procesu konstrukcji oprogram ow ania: pojedynczy zbiór problem ów , pojedynczy zbiór rozwiązań tych problemów oraz pojedyncza notacja do wyrażenia wyników. Właśnie taki próbuje być język Eiffel. Pozostaje to w całkowitej sprzeczności z kierunkiem ewolucji branży, z jakim mamy do czynienia w ciągu ostatnich 20 lat. Uważam ten kierunek za całkowicie błędny. Występuje tendencja do separacji poszczególnych aspektów konstrukcji programów.

EIFFEL

529

Jest tak dlatego, że to kierunek korzystny z punktu widzenia biznesu — trzeba kupować narzędzia analityczne, narzędzia projektowe, środowiska IDE oraz na każdym poziomie korzystać z usług konsultantów. Jest i inny powód tej sytuacji — ludzie nie zdają sobie sprawy z tego, że specyfikacja stanowi oprogramowanie w takim samym stopniu, w jakim jest nim implementacja. Dawniej języki program owania były bardzo niskopoziomowe, zatem pomysł, aby myśleć w języku program ow ania, okazał się absurdalny. Jednak w odniesieniu do współczesnych języków programowania nie ma powodów, by w dalszym ciągu utrzymywać tę lukę. Ludzie mają problemy z odejściem od sposobu myślenia, ja k i obowiązywał w erze kart perforowanych, kiedy dostarczało się program w postaci zadania wsadowego i wracało się następnego dnia. Jeśli był gdzieś błąd kompilacji, mieliśmy kłopoty. Trzeba było usiąść i wcześniej wszystko bardzo dokładnie przemyśleć.

Bertrand: Zgadza się. Nie ma niczego złego w tym, żeby wcześniej dokładnie wszystko przemyśleć. Nie oznacza to jednak, że należy używać całkowicie różnych schematów myślenia, a w konsekwencji różnych narzędzi i języków na różnych etapach projektowania. Problem ten jest postrzegany niemal w kategoriach moralnych. Istnieje niejawny pogląd, że analiza jest szlachetna i w spaniała, a im plem entacja to robota brudna i godna pogardy. Do pewnego stopnia była to prawda, kiedy trzeba było programować w języku asemblera albo w takich językach, jak FORTRAN. Ten ostatni był wspaniałym osiągnięciem w jego czasach, ale nie był językiem na tyle w ysokopoziom owym , aby ludzie potrafili w nim myśleć. Stąd idea, że szlachetną częścią pracy jest myślenie na wczesnym etapie projektowania. Następnie ktoś, niekoniecznie ta sama osoba, musiała zaimplementować całość — zakasać rękawy i wykonać brudną robotę — tak jak po podniesieniu maski mechanik zaczyna brudzić sobie ręce. To była prawda do pewnego stopnia, jednak w przypadku nowoczesnych języków programowania, a zwłaszcza w przypadku języka Eiffel, nie musi to tak wyglądać. Zamiast podejmować próby, by metody analizy i projektowania były zorientowane bardziej w kierunku implementacji, zaczynamy od drugiej strony. Zaczynamy od program ow ania i tworzymy język program ow ania w sposób tak ekspresyjny i elegancki oraz tak zbliżony do produktyw nych sposobów myślenia, że możemy w tym języku wykonywać wszystko — od początku do końca. Pierwsze wersje programu są abstrakcyjne i opisowe. Kolejne wersje są w większym stopniu operacyjne i na koniec powstaje program wykonywalny. Nie ma potrzeby, aby istniały luki pomiędzy różnymi fazami tego procesu. Jest to pogląd całkowicie odm ienny od m etodologii wytw arzania sterowanego modelem, zgodnie z którą mamy model, a następnie coś całkowicie innego — program.

530

ROZDZIAŁ

SIEDEMNASTY

W tym przypadku pański model, niezależnie od tego, czy wizualny, czy nie, jest podobny do kodu źródłowego, ponieważ to kod źródłowy pan generuje. Kod źródłowy je st rodzajem efektu końcowego.

Bertrand: To jest właściwe podejście, pod warunkiem że mamy całkowitą gwarancję, że nikt nigdy nie dotknie kodu źródłowego — ani po to, by go debugować, ani po to, by go zmodyfikować. Taki sposób myślenia jest bardzo częsty. Myślimy: „To przecież tylko model — nikt nigdy nie dotknie kodu źródłowego”. Rzeczywistość często jednak jest całkiem inna. Kod źródłowy jest artefaktem projektu.

Bertrand: Pytanie, co debugujemy. Jeśli w rzeczywistości debugujemy model, to nie ma czego krytykować. Oczywiście to oznacza, że to, co nazywamy modelem, jest w rzeczywistości programem. Być może to program bardzo wysokopoziomowy, ale to jest program. Opracowaliśmy bardzo wysokopoziomowy język programowania, następnie musimy zbudow ać w okół niego kom pletne środowisko w ytw arzania oprogramowania. Z drugiej strony, jeśli debugujemy program, który został wygenerowany, to musimy liczyć się ze wszystkimi trudnościami rozdzielonego wytwarzania. Jest to kluczowe pytanie, które należy zadać ludziom stosującym metodologię wytwarzania sterowanego modelem: którą wersję debugujecie? Którą wersję zmodyfikujecie, jeśli klient poprosi o nową funkcję na wczoraj? Czy można udowadniać programy, czy też kontrakty służą tylko do testowania?

Idea mechanizmu kontraktów obecna w języku Eiffel od samego początku była taka, że kontrakty będą służyły długoterm inow em u dow odzeniu program ów , jednak na krótką metę będą one wykorzystywane do testow ania program ów. Jedną z konsekwencji tego założenia jest możliwość włączenia monitorowania kontraktów w fazie w ykonyw ania program u. W przypadku naruszenia kontraktu powstaje wyjątek. N astępnym krokiem — jego przeprow adzenie zajęło dużo czasu — było użycie mechanizmu kontraktów do przeprowadzenia całkowicie zautomatyzowanych testów. Takie badania przeprowadzaliśmy w ciągu kilku ostatnich lat z moją grupą w ETH. Obecnie mechanizm automatycznego testowania jest zintegrowany z narzędziami. Zasadnicze pytanie brzmi: co sprawia trudność w testowaniu? Po pierwsze, trzeba zautomatyzować proces testowania. Problem ten został rozwiązany dzięki bibliotece JUnit oraz wszystkim tym doskonałym narzędziom, które są dziś używane do zautomatyzowania tej części procesu. Po drugie, są rzeczy do zautomatyzowania, których właściwie nie zautomatyzował nikt inny. Ich automatyzacja jest jednak najtrudniejsza. Jedną z nich jest generowanie

EIFFEL

531

przypadków testowych. To trudne, poniew aż wymaga stworzenia wszystkich przypadków testowych — potencjalnie mogą być ich tysiące lub nawet setki tysięcy. Po trzecie i ostatnie, naw et jeśli pokonaliśmy dwie pierwsze trudności, w dalszym ciągu mamy problem, ponieważ musimy uruchom ić te tysiące testów i ktoś musi zdecydować, czy test zakończył się pomyślnie, czy nie. To również należałoby zautom atyzow ać. Dzięki ko ntraktom możemy przekazać zadanie rozstrzygania o skuteczności testów wyroczniom testowym. Wystarczy, że powiemy: jeśli warunek końcowy lub niezmiennik jest spełniony, test zakończył się sukcesem. Jeśli zaś warunek końcowy lub niezmiennik został naruszony, test się nie powiódł. Wszystko to dzieje się automatycznie. Pozostaje generowanie przypadków testowych. Do tego stosujemy podejście, które na pierwszy rzut oka może się wydawać głupie, a w rzeczywistości sprawdza się całkiem dobrze: generowanie losowe lub pseudolosowe. Narzędzie tworzy obiekty prawie losowo, a następnie wywołuje wszystkie procedury — wszystkie metody odpowiednich klas, w większości przekazując do nich losowe argumenty. Następnie wystarczy tylko czekać. My nazywamy to testowaniem w czasie lunchu. Naciskamy przycisk „uruchom test” i wracamy godzinę później, po lunchu. Następnie możemy zaobserwować, czy jakieś warunki końcowe zostały naruszone. Technika ta bardzo dobrze się sprawdza, ponieważ właściwie nie musimy nic robić. Czekamy tylko, aż automatyczny mechanizm generujący przetestuje oprogramowanie. Ale taką technikę można zastosować tylko dla języka, który ma wbudowaną obsługę kontraktów. W przeciwnym wypadku trzeba by dodać kontrakty oraz mechanizmy sprawdzania kontraktów. Istnienie obsługi kontraktów w języku to doskonały sposób testowania oprogramowania. Co pan sądzi o możliwości dowodzenia programów? Czy jest to przydatne? Czy zawsze będzie to marzenie nie do spełnienia?

Bertrand: Staje się to coraz bardziej realne. Prace nad takimi mechanizmami zajęły znaczną część akademickiej części mojej pracy. To bardzo frustrujące, ponieważ podstaw ow e pom ysły istnieją już od prawie 40 lat. W łaściwie od opublikow ania w 1969 roku przez Tony’ego H oare’a artykułu na tem at semantyki aksjomatów. Praktyczna realizacja trwała bardzo długo, ale nie jest to marzenie nie do spełnienia. Zwłaszcza w ciągu ostatnich 5 - 1 0 lat odnotow ano znaczący postęp. Bardzo interesujące są prace nad narzędziem Spec# prowadzone w ośrodku Microsoft Research. W ośrodku ETH prowadzimy badania związane z językiem Eiffel. Co prawda jeszcze niewiele się o nich mówi, ale to dlatego, że jesteśmy bardzo ambitni. Zanim naprawdę zadziwimy świat, musimy rozwiązać mnóstwo problemów. Myślę jednak, że nasze wyniki są obiecujące.

532

ROZDZIAŁ

SIEDEMNASTY

Prowadzi się także prace w ramach projektu SPARK. To bardzo interesujący projekt. Jego uczestnicy potrafią tworzyć programy, które dają się udowadniać. Problem polega na tym, że to język, w którym nikt nie chce programować. Mówią, że jest to podzbiór języka Ada. W gruncie rzeczy to podzbiór Pascala. Ceną, jaką należy zapłacić za możliwość dowodzenia, są wszystkie przyjemności życia programisty. Nie m a klas, nie ma dynamicznego tworzenia obiektów, nie ma typów generycznych, dziedziczenia czy wskaźników. Dzięki przyjęciu tak ograniczonej formuły języka możliwe stało się zbudow anie skutecznych narzędzi do dowodzenia. Jest to prawdziwe osiągnięcie, poniew aż dzięki tem u m ożna budow ać istotne systemy — zwykle stosowane w wojskowości lub lotnictw ie — i udow odnić ich popraw ność. Ale w tej postaci, w jakiej ten język występuje teraz, prawie nikt nie chce program ow ać. Ja nie zdecydowałbym się na to i 99% programistów na świecie także się nie zdecyduje. Pomimo wszystko to ważne osiągnięcie. Celem prac, które prowadzimy w ośrodku ETH, jest stworzenie podobnych narzędzi dowodzenia poprawności, ale dla języka, w którym programiści chcą programować. Oczywiście natychm iast, kiedy w języku pojawiają się wskaźniki, są też aliasy, a to bardzo kom plikuje problem . Problemem dziś już nie jest sama możliwość dow odzenia popraw ności program ów. Prawdziwym problem em jest dowodzenie popraw ności program ów napisanych w nowoczesnych i realistycznych językach programowania. Jestem pewien, że ten cel w końcu uda się osiągnąć. Trzeba zdać sobie sprawę, że w gruncie rzeczy m am y do czynienia z problem am i nierozstrzygalnymi. Ostatecznie zawsze będą istniały fragmenty programów, których nie da się udowodnić, dlatego właśnie pracujemy także nad testami. W ytwarzanie oprogramowania przebiega dziś znacznie szybciej niż kiedyś. Jest to z całą pewnością najbardziej ekscytujące osiągnięcie w ciągu ostatnich dwóch, trzech lat. Obecnie testowanie jest całkowicie zintegrowane ze środowiskiem projektowania. A zatem jedną z głównych korzyści programowania kontraktowego jest możliwość całkowicie automatycznego testowania. Mamy mechanizm Eiffel Testing Framework, obecnie całkowicie zintegrowany ze środowiskiem Eiffel Studio, pozwalający testować programy poprzez kliknięcie przycisku. Nie trzeba pisać przypadków testowych. Nie trzeba pisać testowych sędziów. Wystarczy wywołać narzędzie Testing Framework, które tworzy egzemplarze klas i wywołuje metody tych klas, a następnie czekać na niepowodzenie lub powodzenie kontraktu. Inną doskonałą własnością mechanizmu Testing Framework są narzędzia syntezy testów: kiedy uruchomienie testów się nie powiedzie, narzędzie autom atycznie tworzy z niego test, który m ożna uruchom ić w celu ułatw ienia popraw ienia błędu i dołączyć do zestawu testów regresji. Aby odnieść się do pańskiego pytania: są dowody i są testy. Te dwa aspekty zawsze będą niezbędne. Ale z całą pewnością testy stają się możliwe.

EIFFEL

5 33

Dowody i kontrakty sprawiają wrażenie, jakby były kolejnymi stopniami na przesuwanej skali.

Bertrand: Tak jak w spom niałem , rozpoczęliśmy od prac nad dynam iczną oceną kontraktów, ale ostatecznym celem zawsze było dowiedzenie tego, że klasy spełniają warunki swoich kontraktów. Do niedawna dowodzenie było niemożliwe. Po prostu branża nie była gotowa na dowody. Czy powinniśmy się spodziewać mechanizmu dowodzenia w Eiffelu w ciągu kilku następnych lat?

Bertrand: Z całą pewnością. W dalszym ciągu jest to przedmiot badań, zatem trudno wyznaczyć dokładny termin. Prace badawcze nad testowaniem rozpoczęły się około czterech lat temu. Pierwsze rezultaty można zaobserwować w środowisku. Jeśli chodzi o dowody, to myślę, że pierwsze wyniki zobaczymy już niedługo. Przewiduję, że będzie to za około trzy lata. A zatem jest to na pewno pański cel produkcyjny?

Bertrand: Z całą pewnością. Wspomniał pan o takich obszarach w kodzie, których poprawności nie można dowieść. W Haskellu jest wykorzystywane pojęcie monad w celu oddzielenia czystego kodu od nieczystego — czyli takiego, w którym występują efekty uboczne. Czy można sobie wyobrazić podobny mechanizm do wyizolowania kodu, którego nie da się udowodnić?

Bertrand: Musimy oddzielić części, które da się udow odnić, od tych, których udow odnić nie można, ale myślę, że nie będziemy używać do tego monad. Monady to bardzo interesujące pojęcie. Można ich używać w kontekście dowodzenia do innego celu: pozwalają one na zastosowanie podejścia przyrostowego. Można zdefiniować język bazowy zapewniający możliwość dow odzenia, a następnie dodać bardziej zaawansowane konstrukcje, na przykład wyjątki lub inne m echanizm y w sposób bardziej przyrostowy.

Zarządzanie wzrostem i ewolucją Powiedział pan, że zaprojektow ał język Eiffel w ciągu popołudnia, ale zaimplementowanie tej wizji zajęło prawie 2 0 lat.

Bertrand: Kluczowe pojęcia są napraw dę bardzo proste, a reszta — jak mówią niektórzy — to komentarz. Do pewnego stopnia właśnie tak postępowaliśmy. Przez ostatnie 20 lat rozwijaliśmy podstawowe koncepcje. Weźmy klasy albo dziedziczenie, zwłaszcza dziedziczenie wielokrotne. Weźmy typy generyczne, kontrakty oraz szereg innych własności języka. Na przykład własność, która jest bardzo ważna w języku Eiffel — zapewnienie jednego dobrego sposobu

534

ROZDZIAŁ

SIEDEMNASTY

wykonywania każdej operacji. Albo idea wysokiego współczynnika sygnału do szumów: celem języka nie pow inno być dążenie do jak najmniejszego rozmiaru za wszelką cenę. W języku pow inny znaleźć się własności, które w noszą jak największe możliwości, a jednocześnie jak najmniej komplikacji. Należy wybrać kilkadziesiąt takich pomysłów. Niektóre z nich dotyczą języka, inne są metakoncepcjami na temat projektu języka. Mniej więcej na tym to polega. Jednak aby przekształcić te koncepcje w narzędzie umożliwiające pisanie aplikacji symulujących system obrony antyrakietowej Stanów Zjednoczonych czy też zarządzających miliardami dolarów, potrzeba pracy inżynierskiej, która zajmuje mnóstwo czasu. Każdy dysponuje ograniczonymi zasobami. Problem nie dotyczy tego, czy firma jest mała, czy duża, ponieważ każdy nowatorski projekt stanowi efekt pracy małej grupy. Jedyny wyjątek to projekty, które w gruncie rzeczy są projektam i inżynierskimi. W ysyłamy człowieka na Księżyc. To jest praca dla kilku tysięcy osób na kilka lat. Albo badanie ludzkiego genomu. Wiemy, jak się to robi, potrzeba tylko pracy inżynierów. To jednak są wyjątki. Jeśli przyjrzymy się naprawdę nowatorskim koncepcjom, to zauważymy, że tworzą je małe grupy ludzi. Nigdy nie widziałem przełomowego produktu w oprogramowaniu, który stworzyłaby grupa licząca więcej niż 10 osób. Zwykle jest jeszcze mniejsza i zawiera od 2 do 5 członków. Każdy ma ograniczone zasoby, zatem kluczowe decyzje, jakie podejmujemy, dotyczą tego, co należy robić, a czego nie należy robić. Oczywiście po drodze popełniliśm y kilka błędów. Na przykład zainwestowaliśmy w wersję dla systemu OS/2, co okazało się całkowitym marnotrawstwem wysiłków. Mogliśmy raczej włożyć więcej tru du w stworzenie dobrej wersji podstawowej. Takie decyzje są podejmowane na co dzień. Niektóre z nich okazały się pomyłkami. Czy 2 0 lat zajęło pana zespołowi dojście do momentu, w którym poczuliście zadowolenie z języka? Czy dopiero po takim czasie implementacja, kształt oraz dostępność języka spełniły postawione pierwotnie cele projektowe?

Bertrand: Nie powiedziałbym tego, ponieważ aby powiedzieć coś takiego, trzeba być trochę bezczelnym. W pewnym sensie pierwsza implementacja była czymś, do czego cały świat pow inien się już przyzwyczaić. M ożna by również zachować większą powściągliwość i stwierdzić, że jeszcze nie osiągnęliśmy stanu zadowolenia, o którym pan mówi. Pracujemy nad tym codziennie. Poprawiamy implementację i wykonujemy rzeczy, które naszym zdaniem są całkowicie nieodzowne. Implementacja nigdy nie będzie doskonała. Na pew no taka nie będzie za mojego życia. Powstaje jednak pytanie o to, nad czym należy pracować w danym momencie. Co jest istotne, a co stanowi tylko dodatek. Zawsze można poddać analizie wybory dokonane wcześniej i zastanowić się, czy akcenty zostały rozłożone właściwie, czy nie.

EIFFEL

535

Z całą pewnością istnieją pewne elementy implementacji, które będą krytykowane. Często będzie to słuszna krytyka. Z drugiej strony są ludzie, którzy używają Eiffela od 10 - 15 lat. Niektórzy prawie od chwili pojawienia się pierwszej implementacji. Osoby te sprawiają wrażenie zadowolonych. A zatem z jednej strony nigdy nie będę zadow olony z implementacji, natom iast z drugiej strony sądzę, że Eiffel w czasie swojego istnienia dostarczył doskonałych rozwiązań — znacznie wyprzedzających swoją epokę — ludziom, którzy chcieli z niego korzystać. Trzeba być zuchwałym, aby wierzyć, że projekt będzie trwał 2 0 lat, ale także trzeba uwierzyć w to, że przez te 2 0 lat praca nad projektem będzie sprawiała przyjemność.

Bertrand: Zgadza się. Decyzja o tym, co robić, a czego nie robić, jest bardzo trudna. Byliśmy na przykład jedną z pierwszych firm komercyjnych, które wydały wersję dla Linuksa. W tamtym czasie wydawało się to całkowicie szaloną decyzją. Porównajmy wersję dla Linuksa z wersją dla OS/2. Wysiłek włożony w opracowanie wersji dla OS/2 poszedł całkowicie na marne. Z drugiej strony chyba w 1993 roku ktoś powiedział do mnie: „Używamy czegoś, co nazywa się Linux. Czy możecie opracować wersję dla Linuksa?” . Nikt w firmie nie chciał się tym zająć. Powiedziałem, że jest to odmiana Uniksa. Opracowaliśmy kilkanaście odmian dla Uniksa, ponieważ w tamtych czasach istniało wiele komercyjnych odmian tego systemu. Bardzo ciężko pracowaliśmy nad technologią, która miała być przenośna, a zatem poprosiłem o próbę kompilacji kodu pod Linuksa. Powiedziałem, że jeśli ma to zająć miesiąc, nie w arto podejm ow ać wysiłków. Jeśli jednak ma to być jeden dzień, być może warto. Okazało się, że nie wiązało się to z żadną pracą. Kod pod Linuksa skompilował się po kliknięciu przycisku. Człowiek, który poprosił m nie o wersję dla Linuksa, był bardzo zadowolony. Nagle zaczęły napływać kolejne prośby. Z punktu widzenia zdrowego rozsądku opracowanie wersji dla Linuksa było niemądre, natom iast dla systemu OS/2 jak najbardziej uzasadnione. Jak się później okazało, było całkowicie na odwrót. Opracowanie wersji dla Linuksa odpowiednio wcześnie bardzo nam pomogło. Bardzo trudno podejmuje się takie decyzje. Zazwyczaj pracujemy z niepełnymi informacjami. Często rad udzielają nam ludzie, którzy często nie do końca wiedzą, o czym mówią. W niosek, jaki płynie z tego doświadczenia, jest taki, że należy wykorzystywać informacje ze wszystkich źródeł. Ostatecznie jednak trzeba wiedzieć, co się robi, i podejmować decyzje samodzielnie, według własnej oceny. Jakie kryteria stosuje pan w celu przeanalizowania tych decyzji?

Bertrand: Spójność. Staram się odpowiedzieć na pytanie, czy zapew niają one spójność z ideą organizacji. Czy modyfikacja będzie spójna? Czy raczej spowoduje obranie kierunku, który odbiega od naszych zasadniczych pomysłów, kompetencji

536

ROZDZIAŁ

SIEDEMNASTY

i przyjemności? W ażne jest bowiem, aby to, co robimy, sprawiało przyjemność. Trzeba odpowiedzieć sobie na pytanie, czy będzie to nowe doświadczenie, które nas czegoś nowego nauczy i doda coś nowego do tego, co już wiemy. W ja k i sposób podejmuje pan decyzję o dodaniu nowych własności do języka Eiffel? W ja k i sposób rozszerza pan język?

Bertrand: Do roku 1998, a właściwie jeszcze na przełomie lat 2000 - 2001, byłem odpowiedzialny za ewolucję języka. Potem sprawy przybrały inny obrót wraz z utworzeniem komitetu standaryzacyjnego Eiffela w organizacji ECMA. Doprowadziło to do pow stania standardu ECMA w 2005 roku i standardu ISO w roku 2006. Odpowiedź na pańskie pytanie z p u nk tu widzenia adm inistracyjnego jest taka, że w języku są w prow adzane tylko te zmiany, które zostaną zatw ierdzone przez kom itet standaryzacyjny. N atom iast jeśli chodzi o techniczną stronę procesu, to jesteśmy bardzo ostrożni w kwestii rozwoju języka Eiffel. Niektóre nasze dokonania jako komitetu oraz jako społeczności są dość oryginalne. Po pierwsze, stosujemy w języku zasadę, że pow inien istnieć jeden dobry sposób w ykonyw ania każdej operacji. Jest to najlepsza m etoda przeciwdziałania przedostaw aniu się do języka zbędnych własności. Nie oznacza to, że w ogóle nie chcemy now ych własności. Nie chcemy takich now ych własności, które są redundantne z istniejącymi mechanizmami. Zasada jest taka, że jeśli programista chce coś zrobić, to powinien zastosować jeden dobry sposób realizacji tego zamiaru. Jako kontrprzykład mogę podać, że w niektórych językach istnieje zarówno mechanizm dynamicznego wiązania, jak i tablice wskaźników funkcji. Programista, zwłaszcza początkujący program ista obiektowy, nie wie, którą z tych technik wybrać, w przypadku gdy m a problem w yw oływania różnych funkcji w zależności od określonego typu obiektu. W Eiffelu takie dylem aty prawie nie istnieją. Nie da się spełnić tej zasady w 100%, ale jesteśmy blisko. Inną wiodącą zasadą jest maksymalizacja czegoś, co można nazwać współczynnikiem sygnału do szumów. Interesująco wypada porównanie tej zasady do podejścia, jakie stosow ał w odniesieniu do języków Niklaus W irth. W irth zawsze dążył do tego, by języki były jak najmniejsze. Miał fobię na punkcie rozrastania się języków. Sądzę, że w jego opinii wiele m echanizm ów Eiffela m ogłoby pow odow ać nadm ierne rozrastanie się języka. Bardzo szanuję ten pogląd, ale w języku Eiffel stosujemy nieco inne podejście. Język niekoniecznie musi być mały co do rozmiaru. Uważam raczej, że współczynnik sygnału do szumów powinien być bardzo wysoki — szumów powinno być jak najmniej. Szumami nazywam takie własności języka, które nie są specjalnie przydatne, a jedynie komplikują język bez wnoszenia nowej ekspresyjnej siły. Z kolei sygnałem nazywam tę ekspresyjną siłę. Dla przykładu jakieś 12 lat temu dodaliśmy do języka pojęcie „agenta” , które udow odniło swoją niezwykłą przydatność. Jest to rodzaj domknięcia, kom pletnego wyrażenia lambda. Istniała obawa, że będzie to mechanizm redundantny z istniejącymi, ale tak się nie stało. Jest to przykład ważnego

EIFFEL

5 37

dodatku do języka. Ten dodatek okazał się niezwykle popularny wśród użytkowników, umożliwił wykonanie rzeczy, których wcześniej nie dało się zrobić w elegancki sposób. Prawie czysty sygnał, bardzo mało szumów. Trzecią zasadą, jaką stosujemy, jest dbanie o to, aby wszystko, co robimy, było zgodne z celami Eiffela oraz duchem Eiffela. W szczególności chodzi o to, by dbać o poprawienie niezawodności oraz zmniejszenie szans na popełnianie błędów przez programistów. W tym zakresie nastąpił bardzo istotny rozwój w ciągu ostatnich dwóch, trzech lat. Myślę, że Eiffel jest pierwszym kom ercyjnym językiem, który m ożna określić jako bezpieczny pod względem w skaźników (ang. void-safe). Oznacza to, że w Eiffelu nie ma już odwoływania się do wskaźnika nuli. Własność ta została w pełni zaim plem entow ana wraz z ostatnim wydaniem 6.4. W tej wersji zostały przekonwertowane wszystkie biblioteki. Bezpieczeństwo na poziomie wskaźników jest standardow ym problem em języków obiektowych, a właściwie naw et takich języków, jak C lub Pascal. Istnieje zagrożenie, że wyrażenie x . f spow oduje błąd ze względu na to, że x m a w artość nuli lub — w edług term inologii języka Eiffel — void. Programujący w Eiffelu zupełnie nie ponoszą takiego ryzyka. Myślę, że jest to istotne osiągnięcie, ponieważ usuwa potencjalnie ważny problem działania w trybie runtime, który w dalszym ciągu istnieje w programowaniu obiektowym. Tego rodzaju zmiany chcemy wprowadzać po to, by zwiększyć niezawodność pisanych programów. Istnieje więcej zasad, ale wspomnę jeszcze tylko o jednej. Otóż komitet standaryzacyjny jest bardzo odważny. Nie wahamy się przed modyfikowaniem języka. W szczególności nie zawahamy się przed usunięciem pewnych mechanizmów języka, jeśli poczujemy, że istnieje lepszy sposób, by zrealizować określoną czynność. Oczywiście robimy to bardzo ostrożnie, ponieważ istnieje baza użytkowników korzystających z języka. Jeśli kod klienta składa się z kilku m ilionów wierszy, nie możemy sobie pozwolić na to, by przestał działać. Z tego powodu stare mechanizmy są zwykle obsługiwane przez kilka lat. Oferujemy narzędzia wspomagające migrację i różne narzędzia pomocnicze. Jeśli jednak w pewnym m omencie dojdziemy do wniosku, że istniał pewien sposób A wykonywania określonej operacji, a znaleźliśmy sposób B osiągnięcia tego samego rezultatu, który okazuje się lepszy (ponieważ jest prostszy, bezpieczniejszy, bardziej rozszerzalny itp.), to po prostu usuwamy stary m echanizm i zastępujemy mechanizmem, który według nas jest lepszy. W ja k i sposób pana zespół radzi sobie z problemami zgodności wstecz i w przód?

Bertrand: Problemy te znajdują się w centrum naszej uwagi. Powiedziałbym, że jeśli ktoś jest w branży i utrzymuje produkt od jakiegoś czasu, to owe zagadnienia szybko staną się jednymi z ważniejszych, którym trzeba będzie poświęcić sporo czasu. Jest to niezwykle tru dne pytanie dla firm, które nie są najważniejszymi graczami na rynku. Jeśli ktoś dominuje na rynku, może zrobić to, co uważa za słuszne. Wszystkie największe firmy z branży postępują w ten sposób co jakiś czas. Po prostu modyfikują produkt z dnia na dzień, a klienci nie mają innego wyboru, jak się dostosować.

538

ROZDZIAŁ

SIEDEMNASTY

Społeczność języka Eiffel okazuje się dość szczególna pod tym względem, ponieważ jest bardziej otwarta na innowacje w porównaniu z większością innych społeczności — w szczególności społeczności języków program ow ania. Ludzie akceptują to, że rzeczywistość musi się zmieniać, a poniew aż użytkownicy Eiffela patrzą w przyszłość i są zainteresowani naprawdę dobrymi rozwiązaniami — takimi, które są eleganckie i twórcze — akceptują zmiany nawet wtedy, gdy mają wiele milionów wierszy kodu do zarządzania. Nie lubią tylko takich sytuacji — myślę, że nikt ich nie lubi — gdy ktoś przykłada im pistolet do głowy i mówi: „Jeśli nie wprowadzisz zmian, to umrzesz” . Jeżeli ktoś wprowadza zmiany właśnie w taki sposób, to raczej nie może liczyć na popularność wśród swoich użytkowników. W komitecie standaryzacyjnym ECMA stosujemy następującą strategię: próbujemy wyliczyć wszystkie argumenty przemawiające za określoną sytuacją i przeciw niej, a następnie poddajem y je gruntow nej analizie. Jeśli zdecydujemy, że coś musi się zmienić, zm ieniamy to. Nie odrzucam y zmian, wymawiając się tym, że określona czynność była wykonywana przez wiele lat w taki, a nie inny sposób. Jeśli musimy coś zmienić, zmieniamy to, ale zmiana musi być bardzo uważnie zaplanowana. Trochę to upraszczam, poniew aż są zm iany w języku, zm iany w bibliotekach, zm iany w narzędziach. Strategia niekoniecznie musi być taka sama w każdym przypadku. Istnieją jednak pewne zasady podstawowe: •

Należy odrobić zadanie domowe i całkowicie przekonać się co do tego, że zmiana jest odpowiednia.



Należy przygotować plan.



Trzeba wyjaśnić powody wprowadzania zmiany. Jest to bardzo ważne. Powinniśmy założyć, że zwracamy się do inteligentnych ludzi. Jeśli rzeczywiście odrobiliśmy zadanie dom owe i dokładnie przemyśleliśmy pow ody zm ian, jeśli potrafim y je wyjaśnić oraz przekonać do nich naszych najbliższych kolegów, z pewnością uda nam się przekonać także innych inteligentnych ludzi.



Ludziom trzeba dać czas. Prawie zawsze znacząca zmiana w języku jest procesem dwuetapowym, a czasami nawet trzyetapowym. Istnieje nowe wydanie, w którym nowy mechanizm jest opcjonalny, a stary w dalszym ciągu pozostaje domyślny. Można jednak próbować wykorzystać nowy mechanizm jako opcję, zwykle klasa po klasie. Dzięki temu można wypróbować poszczególne części systemu. Następnie powstaje wersja, w której zamieniamy miejscami domyślne mechanizmy. Bardzo rzadko usuwamy coś całkowicie. Nawet gdy jakaś własność jest przestarzała, w dalszym ciągu pozostaje dostępna jako opcja. Niestety, nie zawsze jest to możliwe, ponieważ czasami pomiędzy starym a now ym m echanizm em występują niezgodności.



Jeśli jest taka możliwość, należy zapewnić pomoc w migracji: dostarczyć narzędzia, biblioteki oraz wszystko to, co może pom óc ludziom w przejściu od starego do nowego mechanizmu.

EIFFEL

539

Przez ten proces przechodziliśmy wiele razy. Zwłaszcza w ostatnich pięciu lub sześciu latach. W historii języka Eiffel były dwa główne przełomy. Pierwsza wersja została opublikow ana w latach 1985 - 1986. Drugą wersję opublikow ano w roku 1988, ale były to głównie dodatki do pierwszej wersji, dlatego nie spowodowały one żadnych problem ów niezgodności. Następnie, pomiędzy 1990 a 1993 rokiem, przeszliśmy na Eiffela 3. Była to z całą pewnością bardzo istotna zmiana. Korzyści były jednak tak duże, że zmiana ta nie spowodowała zbyt wielu problemów. Język nie zmienił się zbytnio aż do początku procesu standaryzacyjnego, co nastąpiło w 2001 roku. Standard opublikowano w roku 2005, a w 2006 został on standardem ISO. W standardzie wprowadzono kilka istotnych zmian w języku. Ich implementacja zajęła kilka lat. Dziś jest prawie kompletna. Obecnie jesteśmy w trakcie wprowadzania jednej z najtrudniejszych zmian, ale napraw dę opłaca się ją wprowadzać. Chodzi o m echanizm typów dołączonych (ang. attached type), który rozwiązuje problem bezpieczeństwa wskaźników, o którym w spom inałem wcześniej. Jest to gwarancja tego, że wywołanie x. f nigdy nie zostanie uruchomione, jeśli wskaźnik x ma wartość nuli. K ompilator przechwytuje takie przypadki i odrzuca program , gdy występuje odwołanie do pustego wskaźnika (dereferencja do wskaźnika nuli). Mechanizm ten pow oduje jednak niezgodności z istniejącym kodem, choćby z tego pow odu, że w istniejącym kodzie występują przypadki odw ołań do pustych wskaźników. Implementację tej modyfikacji wprowadzono w wersji 6.2, a ostatnie udoskonalenia znalazły się w wersji 6.3. W pełni skonw ertow aną bibliotekę zapewniającą bezpieczeństwo wskaźników dołączono do wersji 6.4 — konwersja biblioteki była najbardziej pracochłonnym przedsięwzięciem (pracujemy zgodnie ze schematem cyklu zegara — publikujemy rocznie dwa wydania: jedno na wiosnę i jedno jesienią). Konwersja istniejącego kodu okazała się bardzo delikatnym problemem. Nie możemy sobie pozwolić na to, by kiedykolwiek uznać istniejący kod za przestarzały. Możemy jedynie powiedzieć użytkownikom: jeśli chcecie skorzystać z nowego mechanizmu, który będzie dostępny już od dziś, powinniście wykonać odpowiednie czynności. Sami zrobiliśmy to samo, dlatego wiemy, że warto. W iemy też, ile to wymaga wysiłku. Dostarczymy wam całej wiedzy wynikającej z naszych doświadczeń oraz wszelkich narzędzi, które mogą wam w tym pomóc. Czego powinni nauczyć się inni ludzie z pańskiego doświadczenia?

Bertrand: Nie należy kierować się m odą, tylko wybierać rozwiązania, które są uzasadnione intelektualnie.

540

ROZDZIAŁ

SIEDEMNASTY

Posłowie

M

o ją p r z y je m n o ś ć z pr a c y n a d t y m pr o je k t e m m o ż n a o p is a ć je d n y m s ł o w e m



e n t u z ja z m

.

Wszystkie osoby, z którymi przeprowadzaliśmy wywiady, charakteryzowały się tym, czego można było oczekiwać — głęboką wiedzą fachową i historyczną oraz olbrzymim doświadczeniem. Co ważniejsze — to entuzjazm tych ludzi w dziedzinie projektowania języków, ich implementacji i rozwoju okazał się najbardziej zaraźliwy. Na przykład Anders Hejlsberg i James Gosling sprawili, że ponow nie zacząłem fascynować się językami C# i Java. Chuck Moore i Adin Falkoff przekonali mnie do eksploracji Forth i APL — dwóch języków, które powstały, zanim się urodziłem. Al Aho przyciągnął m oją uwagę opisem klasy Compi ler. Wszyscy moi rozmówcy podsunęli mi pomysły, które bardzo chciałbym zrealizować, gdy tylko znajdę czas. Mam olbrzymi dług wdzięczności nie tylko za czas, który te osoby poświęciły Federico i mnie, ale także za to, że wskazały drogę do bogatego i żyznego pola pomysłowości. Oto najcenniejsze lekcje, jakie wyniosłem z tego doświadczenia: •

Nigdy nie przeceniaj wartości prostoty projektu lub implementacji. Złożoność m ożna dodać zawsze. Mistrzowie się jej pozbywają.

541



Podążaj za ciekawością i pasją. Wiele z największych odkryć i wynalazków stało się faktem, kiedy odpowiednia osoba znalazła się w odpowiednim miejscu i czasie i była gotowa na poszukiwanie poprawnej odpowiedzi.



Poznawaj swoją dziedzinę, zarówno jej przeszłość, jak i teraźniejszość. Każda z osób, z którym i rozm aw iałem , w spółpracow ała z innym i inteligentnym i i ciężko pracującymi ludźmi. W naszej dziedzinie współdzielenie informacji ma kluczowe znaczenie.

Moda na języki może się zmieniać, ale problemy, z którymi stykali się moi rozmówcy, w dalszym ciągu nas dotykają, a odpowiedzi wciąż są aktualne. W jaki sposób należy utrzymywać oprogramowanie? W jaki sposób znaleźć najlepsze rozwiązanie problemu? Jak zaskakiwać i zadziwiać użytkowników? W jaki sposób radzić sobie z nieuniknioną potrzebą zmian, aby nie zniszczyć rozwiązań, które muszą nadal działać? Dziś znam lepsze odpowiedzi na te pytania. Mam nadzieję, że niniejsza książka pomogła Czytelnikom w poszukiwaniu mądrości. — Shane Warden

542

P OS ŁOWI E

Współtwórcy

Alfred V. Aho jest profesorem wydziału informatyki na Uniwersytecie Columbia. W latach 1995 - 1997 oraz jesienią 2003 roku pełnił rolę szefa tego wydziału. Profesor Aho m a tytuł licencjata fizyki inżynieryjnej n adany przez Uniwersytet w Toronto oraz doktora w dziedzinie inżynierii i informatyki nadany przez Uniwersytet w Princeton. W 2003 roku profesor Aho zdobył nagrodę Great Teacher Award przyznaną przez Stowarzyszenie Absolwentów Uniwersytetu Colum bia (ang. Society o f Columbia Graduates). Posiada medal Johna von N eum anna organizacji IEEE. Jest członkiem Amerykańskiej Akademii Inżynierii oraz Amerykańskiej Akademii Sztuk i Nauk. Otrzymał honorowe doktoraty Uniwersytetów w Helsinkach i Waterloo, jest członkiem Amerykańskiego Stowarzyszenia Postępu Naukowego, organizacji ACM, Bell Labs i IEEE. Aho jest dobrze znany ze swoich licznych artykułów i książek dotyczących algorytmów, struktur danych, języków programowania, kompilatorów oraz podstaw informatyki. Wśród współautorów jego książek są takie osoby, jak John Hopcroft, Brian Kernighan, Monica Lam, Ravi Sethi, Jeff Ullman i Peter Weinberger.

543

Od nazwiska Aho pochodzi także litera „A” w AWK — szeroko rozpowszechnionym języku dopasow yw ania wzorców. (Litera „W ” pochodzi od nazwiska Petera W einbergera, natom iast „K” od nazwiska Briana Kernighana). Algorytm dopasow yw ania wzorców Aho-Corasick jest używany w wielu program ach do przeszukiwania bibliografii oraz analizy genomów. Aho napisał pierwsze wersje programów egrep i fgrep do dopasowywania łańcuchów znaków. Programy te po raz pierwszy pojawiły się w systemie Unix. W kręgu bieżących zainteresow ań profesora Aho są takie zagadnienia, jak języki programowania, kompilatory, algorytmy, inżynieria programowania oraz komputery kwantowe. Profesor Aho piastował stanowisko przewodniczącego Specjalnej Grupy Zainteresowań Algorytmami i Teorią Obliczeniową w stowarzyszeniu ACM (Special Interest Group on Algorithms and Computability Theory). Był też przewodniczącym Kom itetu Doradczego D yrektoriatu Inform atyki i Inżynierii organizacji NSF (ang. N ational Science Foundation). Obecnie jest zastępcą redaktora naczelnego sekcji artykułów fachowych magazynu Communications o fth e ACM. Zanim objął swoje obecne stanowisko na Uniwersytecie Columbia, Aho pełnił funkcję wiceszefa Com puting Sciences Research Center w Bell Labs — ośrodka, w którym powstał UNIX oraz języki C i C++. Był między innymi pracownikiem technicznym, kierownikiem działu oraz dyrektorem tego ośrodka. Profesor Aho był także generalnym menedżerem ośrodka Information Sciences and Technologies Research Laboratory w Bellcore (obecnie Telcordia). Grady Booch jest uznanym na arenie m iędzynarodowej ekspertem w dziedzinie architektury oprogramowania, inżynierii programowania oraz kolektywnych środowisk w ytw arzania oprogram ow ania. Swoją karierę zawodow ą w pełni poświęcił uspraw nianiu sztuki i nauki wytwarzania oprogramowania. Grady Booch pracował jako szef do spraw naukowych firmy Rational Software Corporation od m om entu jej założenia w 1981 roku aż do przejęcia przez firmę IBM w roku 2003. Obecnie pracuje w ośrodku Thomas J. Watson Research Center firmy IBM jako główny ekspert inżynierii oprogramowania. W tym ośrodku kontynuuje pracę nad książką Handbook o f Software Architecture. Prowadzi też kilka projektów w dziedzinie inżynierii program ow ania, wykraczających poza ramy obecnie produkow anych produktów . Grady Booch angażuje się w pracę nad rzeczywistymi problemami klientów. Pracuje nad tw orzeniem silnych związków z wyższymi uczelniam i i innym i ośrodkam i badawczymi na całym świecie. Booch jest jednym z twórców języka UML (ang. Unified Modeling Language). Jest także jednym z tw órców kilku innych produktów firmy Rational. Pracował jako architekt oraz doradca do spraw architektury wielu złożonych systemów oprogram ow ania na świecie, niem al w każdej dziedzinie, jaką m ożna sobie wyobrazić. Jest autorem sześciu bestsellerów, między innymi UML Users Guide oraz słynnej książki Object-Oriented Analysis and Design with Applications (obie opublikowało wydawnictwo

544

WSPÓŁTWÓRCY

Addison-Wesley Professional). Grady Booch prowadzi stałą kolum nę poświęconą architekturze oprogramowania dla magazynu IEEE Software. Napisał kilkaset artykułów na tem at inżynierii program ow ania. W tej grupie są między innym i artykuły opublikowane na początku lat osiemdziesiątych, opisujące terminologię i praktykę projektow ania obiektowego, oraz artykuły publikow ane w początkow ych latach XXI wieku, omawiające terminologię i praktykę kolektywnych środowisk wytwarzania oprogramowania (Collaborative Development Environments — CDE). Jest członkiem stowarzyszeń ACM (.Association for Computing Machinery), AAAS {American Association for the Advancement o f Science) oraz CPSR (Computer Professionals for Social Responsibility). Należy także do stowarzyszenia IEEE (Institute o f Electrical and Electronics Engineers). Posiada tytuły IBM Fellow, ACM Fellow, World Technology Network Fellow, Software D evelopm ent Forum Visionary. Jest zdobywcą nagrody m agazynu Dr. D obb’s — Excellence in Programming, a także trzykrotnym zdobywcą nagrody Jolta. Grady Booch był członkiem-założycielem Agile Alliance, grupy Hillside Group oraz Worldwide Institute of Software Architects. Pracuje również jako doradca w stowarzyszeniu IASA (ang. International Association o f Software Architecture). Dodatkowo pracuje w strukturach Iliff School of Theology oraz M uzeum Historii Komputerów. Jest także członkiem redakcji magazynu IEEE Software. Grady Booch brał czynny udział w pracach prowadzonych w Muzeum Historii Komputerów, dotyczących zachowania klasycznego oprogramowania. W ramach tych działań zebrał wiele zasobów historii mówionej od takich sław, jak John Backus, Fred Brooks czy Linus Torvalds. Grady Booch otrzymał w 1977 roku tytuł licencjata w Akademii Sił Lotniczych Stanów Zjednoczonych, ty tu ł magistra inżynierii elektrycznej uzyskał natom iast na Uniwersytecie Stanu Kalifornia w Santa Barbara w 1979 roku. D on Cham berlin współtworzył z Rayem Boyce’em język SQL — najpowszechniej używany język zapytań do bazy danych. Był także jednym z menedżerów Systemu R — projektu badawczego, w którego wyniku powstała pierwsza implementacja języka SQL. To on jest twórcą dużej części podstawowych technologii wykorzystywanych przez produkty bazodanowe firmy IBM. Jest także w spółautorem propozycji języka Quilt, który stał się podstaw ą języka XQuery. W czasie tworzenia języka XQuery był reprezentantem firmy IBM w grupie roboczej W 3C XML Query W orking Group. W ram ach tej grupy roboczej pełnił również funkcję redaktora specyfikacji języka XQuery. Obecnie jest adiunktem informatyki na Uniwersytecie Stanu Kalifornia w Santa Cruz. Posiada tytuł IBM Fellow (Emeritus), przyznany przez ośrodek IBM Almaden Research Center, gdzie Don Chamberlin pracował przez wiele lat. Przez ostatnie 11 lat pełnił funkcję sędziego w dorocznym międzynarodowym konkursie programistów ACM International Collegiate Programming Contest.

WSPÓŁTWÓRCY

545

Don Cham berlin posiada ty tuł licencjata inżynierii nadany przez Harvey M udd College oraz doktora w dziedzinie inżynierii elektrycznej nadany przez Uniwersytet Stanford. Posiada tytuł ACM Fellow i jest członkiem organizacji NAE (ang. National Academy o f Engineering). Za swój w kład w projekt i im plem entację systemów relacyjnych baz danych otrzymał nagrodę ACM Software Systems Award. Dr Brad Cox jest obecnie głównym architektem firmy Accenture, gdzie tworzy produkty dla klientów z instytucji rządow ych i korporacji. Specjalizuje się w bezpieczeństwie technologii SOA, interoperacyjności, standardach oraz inżynierii bazującej na komponentach. Brał udział w program ie George’a M asona PSOL (ang. Program on Social and Organizational Learning), skupiającym ekspertów z różnych dziedzin. Program ten koncentruje się na pokonywaniu przeszkód we wprowadzaniu zmian, rozwoju i nauce w czasie przechodzenia firm na globalną ekonomię, w której kluczową rolę odgrywają informacje. Dotyczy zastosowań internetu, telewizji i technologii groupw are do przyspieszenia procesu nauki przez doświadczenia oraz nauki kolektywnej. W ram ach program u PSOL prow adzone są między innym i takie kursy, jak Pokonyw anie granic elektronicznych, ABC Internetu oraz Zaawansow ane techniki obiektowe. Cox jest współautorem książki Object-Oriented Programming: An Evolutionary Approach (Addison-Wesley). Publikacji tej przypisuje się zasługi w spopularyzowaniu technik obiektowych oraz inżynierii bazującej na komponentach. W swojej drugiej książce, Superdistribution: Objects As Property on the Electronic Frontier (Addison-Wesley), Brad Cox zaproponow ał techniczno-społeczne rozwiązania dla kupow ania, sprzedawania i posiadania dóbr złożonych z bitów w odróżnieniu od dóbr złożonych z atomów, z których tworzy się towary od czasów antycznych. Był współzałożycielem firmy Stepstone Corporation, w której pracował nad językiem program ow ania Objective-C oraz bibliotekami programowych układów scalonych (Software IC). W ośrodku Schlumberger-Doll Research wykorzystywał sztuczną inteligencję, techniki obiektowe, system operacyjny Unix oraz technologie stacji roboczych w pracach związanych z poszukiwaniami ropy naftowej (pomiary otworowe). W ośrodku Programming Technology Center korporacji ITT wykorzystywał system Unix i technologie obiektowe do w spom agania procesu wytw arzania dużego, rozproszonego systemu central telefonicznych (System 1240). Posiada tytuł doktora Uniwersytetu w Chicago, uzyskany na podstawie teoretycznych i doświadczalnych prac z neurofizjologii w dziedzinie znanej jako sieci neuronowe. Brad Cox ukończył studia podyplomowe w National Institutes of Health oraz Marine Biological Laboratories w Woods Hole.

546

WSPÓŁTWÓRCY

Adin D. Falkoff (licencjat z chemii, CCNY 1941; magisterium z matematyki, Yale 1963). Zanim w stąpił do M arynarki Stanów Zjednoczonych, podczas II wojny światowej pracował nad wytwarzaniem materiałów i opracowywaniem metod masowej produkcji precyzyjnych przyrządów optycznych. Później pracował nad projektem anten lotniczych dla sam olotów wojskowych. Następnie, w 1955 roku, dołączył do zespołu firmy IBM, w której w czasie powstawania ośrodka IBM Research Division pełnił funkcję m enedżera publikacji badawczych. Pracę nad różnym i aspektami związanymi z informatyką rozpoczął pod koniec lat pięćdziesiątych. Po wstąpieniu w 1960 roku na Uniwersytet w Yale w ram ach IBM-owskiego program u Resident Scholarship Program skoncentrował się na informatyce — pracował między innymi nad językiem APL. Przez kilka lat był członkiem zespołu IBM Systems Research Institute oraz wykładowcą informatyki na Uniwersytecie w Yale. W latach 1970 - 1974 Adin Falkoff założył i kierował IBM-owskim ośrodkiem Philadelphia Scientific Center, natomiast w latach 1 9 7 7 -1 9 8 7 był menedżerem grupy APL Design Group w ośrodku Thomas J. Watson Research Center. Otrzymał nagrody IBM Outstanding Contribution Award za stworzenie języka APL oraz systemu APL\360. Falkoff to pierwszy laureat nagrody Iversona za wkład w rozwój języka APL. Był autorem lub współautorem takich publikacji, jak „Algorithms for Parallel Search Memories”, „A Formal Description of System 360”, „The Design of APL”, „A Note on Pattern Matching: Where do you find the Empty Vector”, „A Pictorial Format Function”, „Semicolonbracket notation: A hidden resource in APL”, „The IBM Family of APL Systems” i wielu innych. Adin Falkoff jest właścicielem patentów dotyczących m ateriałów i m etod wytwarzania precyzyjnych instrumentów optycznych oraz projektów systemów komputerowych. Luiz Henrique de Figueiredo posiada tytuł doktora matematyki nadany przez IMPA (ang. N ational Institute for Pure and Applied Mathematics) w Rio de Janeiro, gdzie prowadzi badania oraz pracuje w laboratorium grafiki komputerowej. Jest również konsultantem w dziedzinie modelowania geometrycznego oraz narzędzi programowych w Tecgraf, członkiem grupy Computer Graphics Technology Group na Uniwersytecie PUC-Rio. Jako pracownik tej uczelni był współtwórcą języka Lua. Oprócz prac nad językiem Lua do jego bieżących zainteresowań naukowych można zaliczyć geometrię obliczeniową, modelowanie geometryczne oraz metody interwałowe w grafice komputerowej, a zwłaszcza zastosowania arytmetyki afinicznej. Pracował między innymi na Uniwersytecie W aterloo w Kanadzie oraz w N ational Laboratory for Scientific Com putation w Brazylii. Jest członkiem redakcji magazynu Journal o f Universal Computer Science. James Gosling otrzymał tytuł licencjata z informatyki na Uniwersytecie w Calgary w Kanadzie w 1977 roku. Tytuł doktora inform atyki uzyskał w 1983 roku na Uniwersytecie Carnegie-Mellon. Tytuł jego pracy doktorskiej to „The Algebraic Manipulation of Constraints” (Algebraiczne przetwarzanie ograniczeń). Posiada tytuł VP & Fellow firmy Sun Microsystems. Zbudował między innymi system pozyskiwania danych satelitarnych, wieloprocesorową wersję Uniksa. Opracował kilka kompilatorów,

WSPÓŁTWÓRCY

5 47

systemów pocztowych i menedżerów okien. Stworzył również edytor tekstowy typu WYSIWYG, edytor graficzny oraz edytor Emacs dla systemu Unix. W firmie Sun początkowo pełnił funkcję głównego inżyniera systemu okienkowego NeWS. Stworzył pierwszy projekt języka programowania Java oraz zaimplementował jego kompilator i maszynę wirtualną. Jest współtwórcą specyfikacji Real-Time Specification for Java. Prowadzi badania w ośrodku Sun Labs, gdzie zajmuje się przede wszystkim narzędziami wytwarzania oprogramowania. Był głównym technologiem grupy Developer Products Group firmy Sun. Obecnie jest szefem technicznym grupy Client Software Group w firmie Sun. Charles (Chuck) Geschke jest współzałożycielem firmy Adobe Systems Incorporated powstałej w 1982 roku, lidera w branży oprogramowania od ponad 35 lat. W 2000 roku Geschke przeszedł na emeryturę i zwolnił stanow isko prezesa firmy Adobe. Od tamtej pory razem z drugim założycielem firmy Adobe, Johnem W arnockiem, przewodzi Radzie Nadzorczej firmy. Geschke aktywnie uczestniczy w pracach kilku wydziałów instytucji naukowych, działa w organizacjach non-profit, stowarzyszeniach technicznych oraz artystycznych. W 1995 roku został w ybrany do Państwowej Akademii Inżynierii (ang. National Academy o f Engineering), w 2008 roku zaś do Amerykańskiej Akademii Sztuk i Nauk (ang. American Academy o f Arts and Sciences). Niedawno zakończył kadencję prezesa Zarządu Powierniczego Uniwersytetu w San Francisco. Jest członkiem Rady Nadzorczej Symfonii w San Francisco oraz kierow nictw a organizacji Com m onw ealth Club of California. Pracuje także jako doradca w dziedzinie informatyki na Uniwersytecie Carnegie-Mellon, w zarządzie Egan Maritime Foundation, radzie organizacji National Leadership R oundtable O n C hurch M anagem ent, kierownictwie firmy Tableau Software oraz kierownictwie klubów Nantucket Boys i Girls Club. Przed założeniem firmy Adobe Systems Geschke w 1980 roku utw orzył Imaging Sciences Laboratory w instytucie Xerox Palo Alto Research Center (PARC), gdzie przewodniczył badaniom w dziedzinie informatyki, grafiki, przetwarzania obrazów i optyki. W latach 1972 - 1980 był głównym naukowcem w Xerox PARC’s Computer Sciences Laboratory. Przed rozpoczęciem studiów stacjonarnych w 1968 roku był członkiem wydziału matematyki Uniwersytetu Johna Carrolla w Cleveland, w stanie Ohio. Menedżerowie z branży i biznesu, między innymi z takich instytucji, jak Association for Com puting Machinery (ACM), Institute of Electrical and Electronics Engineers (IEEE), Uniwersytet Carnegie-Mellon, National Computer Graphics Association oraz Rochester Institute of Technology, przyznawali Geschkemu liczne nagrody za jego osiągnięcia techniczne i menedżerskie. W 1991 roku otrzymał nagrodę Entrepreneur of the Year Award na szczeblu regionalnym, a w 2003 roku nagrodę Entrepreneur of the Year Award na szczeblu ogólnokrajow ym . W 2002 roku został wybrany do zarządu Com puter History M useum, a w 2005 otrzym ał nagrodę Exemplary Community Leadership Award od organizacji NCCJ skupiającej menedżerów Doliny

548

WSPÓŁTWÓRCY

Krzemowej. W 2006 roku Geschke otrzym ał za swoje osiągnięcia medal od stowarzyszenia AeA (Achievement from the American Electronics Association). Geschke i John W arnock to pierwsi liderzy z branży oprogramowania, którzy zostali wyróżnieni tą nagrodą. W 2007 roku Geschke otrzymał nagrodę John W. Gardner Leadership Award. W 2000 roku w stworzonym przez magazyn Graphic Exchange rankingu osób, które wywarły największy wpływ na rozwój grafiki, Geschke został sklasyfikowany na siódmej pozycji. Geschke ma tytuł doktora informatyki nadany przez Uniwersytet Carnegie-Mellon, a także magistra matematyki i licencjata z łaciny — oba uzyskane na Uniwersytecie Xavier. Anders Hejlsberg to pracow nik ośrodka Server and Tools firmy Microsoft. Jest uznanym twórcą narzędzi wytwarzania oprogramowania i języków programowania, a także głównym projektantem języka programowania C# oraz jednym z głównych twórców platformy Microsoft .NET Framework. Od momentu powstania w 2000 roku język programowania C# zyskiwał szeroką akceptację. Dla języka C# został opracowany standard zatwierdzony przez instytucje ECMA i ISO. Przed przejściem do firmy Microsoft, co nastąpiło w 1996 roku, Anders był jednym z ważniejszych pracowników firmy Borland International Inc. Jako główny inżynier tej firmy był autorem implementacji Turbo Pascal — rewolucyjnego zintegrowanego środowiska programistycznego. Hejlsberg to również głów ny architekt następcy Turbo Pascala — języka Delphi. Anders Hejlsberg jest w spółautorem książki The C# Programming Language, opublikowanej przez wydawnictwo Addison-Wesley. O trzym ał wiele patentów na swoje programy. W 2001 roku został uhonorowany prestiżową nagrodą Excellence in Programming Award magazynu Dr. Dobb’s, a w 2007 roku jego zespół otrzymał nagrodę firmy M icrosoft Technical Recognition Award za w ybitne osiągnięcia techniczne. Anders studiował inżynierię w Technical University of Denmark. Paul Hudak jest profesorem na wydziale informatyki Uniwersytetu w Yale. Pracuje tam od 1982 roku, a w latach 1999 - 2005 był szefem tego wydziału. W 1973 roku otrzymał tytuł licencjata inżynierii elektrycznej na Uniwersytecie Vanderbilt. W 1974 roku uzyskał tytuł magistra inżynierii elektrycznej i informatyki w MIT, a w 1982 roku otrzymał tytuł doktora na Uniwersytecie Stanu Utah. Do zainteresow ań badawczych profesora H udaka należy projektow anie języków program ow ania, zarów no teoria, jak i implementacja. H udak pom agał w zorganizow aniu Kom itetu Haskella, a także m u przewodniczył. W 1988 roku kom itet ten opublikow ał pierwszą wersję Haskella — czysto funkcyjnego języka programowania. Hudak był współredaktorem Raportu języka Haskell, a także autorem popularnego przew odnika i podręcznika języka. W początkach kariery zajmował się w spółbieżnym program ow aniem funkcyjnym , abstrakcyjną interpretacją oraz deklaracyjnym podejściem do stanu.

WSPÓŁTWÓRCY

549

Później profesor H udak zajm ował się projektow aniem języków dziedzinowych dla różnych dziedzin aplikacji. Między innymi zajmował się takim i dziedzinami, jak robotyka mobilna i humanoidalna, grafika i animacja, muzyka i synteza dźwięku, graficzne interfejsy użytkownika, a także systemy czasu rzeczywistego. H udak opracow ał również techniki osadzania języków dziedzinowych w Haskellu, włącznie z w ykorzystaniem abstrakcyjnych modeli obliczeń, takich jak m onady i strzały. Ostatnio koncentruje się na wykorzystaniu Haskella do tworzenia muzyki kom puterow ej oraz syntezy dźwięku, zarów no w celach badawczych, jak i edukacyjnych. Profesor Hudak opublikował ponad 100 artykułów i jedną książkę. Jest redaktorem prowadzącym magazynu Journal o f Functional Programming oraz członkiem-założycielem grupy roboczej IFIP Working Group 2.8 zajmującej się programowaniem funkcyjnym. H udak posiada między innymi tytuł ACM Fellow. Jest też zdobywcą IBM Faculty Development Award oraz NSF Presidential Young Investigator Award. John Hughes, urodzony w Północnej Walii w 1958 roku, spędził bardzo ważny rok (1974 - 1975) pomiędzy szkołą a uniwersytetem jako programista w grupie badawczej nieżyjącego już Christophera Stracheya na Uniwersytecie w Oxfordzie. John Hughes nie tylko pomagał Stracheyowi instalować modemy, ale także zetknął się tam po raz pierwszy z programowaniem funkcyjnym. Rozwinęła się z tego pasja, która nie osłabła do dziś. W czasie studiów m atem atycznych na Uniwersytecie w Cam bridge był współtwórcą pierwszego kom pilatora języka GEDANKEN — eksperymentu Johna Reynolda w projektowaniu języków programowania. Do Oxfordu powrócił na studia doktoranckie w 1980 roku. W 1983 roku napisał pracę doktorską na temat technik implementacji języków funkcyjnych. W Oxfordzie spotkał swoją żonę, Mary Sheeran, studentkę, która należała do tej samej grupy badaczy. W latach 1984 - 1985 John pracował nad habilitacją na Uniwersytecie Chalmers w G othenburgu, w Szwecji. Prowadzono tam ważne badania na tem at kompilacji leniwych języków, takich jak Haskell. Spodobało m u się zarów no środowisko naukow e, jak i piękno zachodniej Szwecji. Kiedy Hughes zakończył habilitację, powrócił na krótko do Oksfordu jako wykładowca. Następnie, w 1986 roku, objął katedrę na Uniwersytecie Glasgow w Szkocji. W tam tym czasie wydział Uniwersytetu Glasgow bardzo m ocno się rozwijał. John Hughes mógł założyć Grupę Programowania Funkcyjnego (ang. Glasgow Functional Programming Group), która stała się najlepszą tego rodzaju grupą na świecie. Jej członkami byli między innymi Phil Wadler oraz Simon Peyton Jones. Coroczne warsztaty grupy badawczej zyskały wielką popularność, aż w końcu przekształciły się w sympozjum pod hasłem Trendy w program owaniu funkcyjnym (ang. Trends in Functional Programming), które odbywa się do dziś. Tymczasem w 1992 roku Johnow i Hughesowi zaproponow ano katedrę na uczelni w Chalmers. Skorzystał z okazji i wrócił do Szwecji. K ontynuow ał tam prace w dziedzinie programowania funkcyjnego, a od 1999 roku w dziedzinie testowania

550

WSPÓŁTWÓRCY

oprogramowania z wykorzystaniem automatycznego narzędzia o nazwie QuickCheck. W 2006 roku założył firmę Quviq zajmującą się rozwijaniem i sprzedażą programu QuickCheck. Obecnie połowę swojego czasu poświęca pracy w firmie. Jo hn ma już szwedzkie obywatelstwo. Poczynił także pierwsze kroki w kierunku nauczenia się języka szwedzkiego oraz jazdy na nartach — początek tego drugiego przedsięwzięcia był bardzo nieobiecujący. Hughes ma dwóch synów, z których jeden jest niewidomy i autystyczny. Roberto Ierusalim schy jest profesorem nadzwyczajnym w dziedzinie informatyki na uczelni PUC-Rio (Pontifical Chatolic University in Rio de Janeiro), gdzie pracuje nad projektow aniem i im plem entacją języków program ow ania. Jest czołowym architektem języka program ow ania Lua oraz autorem książki Programming in Lua (Lua.org — obecnie dostępne jest drugie wydanie książki, która została przetłumaczona na języki chiński, koreański i niemiecki). Roberto posiada tytuły magistra (1986) i doktora (1990) informatyki. Oba tytuły uzyskał na uczelni PUC-Rio. Przez pewien czas prowadził badania na Uniwersytecie W aterloo (Kanada, 1991), ICSI (CA, USA, 1994), GMD (Niemcy, 1997) oraz UIUC (IL, USA, 2001/2002). Jako wykładowca na uczelni PUC-Rio Roberto bywał także konsultantem wielu studentów , którzy później zostali wpływowymi członkami społeczności języka Lua. Ostatnio pracuje nad narzędziem LPEG — nowatorskim pakietem do dopasowywania wzorców dla języka Lua. Dr Ivar Jacobson urodził się 2 września 1939 roku w Ystad w Szwecji (jego pełne nazwisko brzmi Ivar Hjalmar Jacobson, ale nigdy nie używał środkowego członu nazwiska). Jacobson otrzym ał tytuł m agistra inżynierii elektrycznej w Chalmers Institute of Technology w G othenburgu w 1962 roku. D oktorat uzyskał w Royal Institute of Technology w Sztokholmie w 1985 roku. Jego praca doktorska dotyczyła konstrukcji językowych dla rozbudowanych systemów czasu rzeczywistego. W latach 1983 - 1984 gościnnie prow adził badania w MIT w grupie Program ow ania Funkcyjnego i Architektury Przepływu Danych (ang. Functional Programming and Dataflow Architecture Group). Otrzymał medal Gustafa Dalena (3 maja 2003 roku) nadany przez Chalmers Alumni Association. Ivar założył szwedzką firmę Objectory AB, która w 1995 roku połączyła się z firmą Rational. Z firmą Rational Jacobson współpracował w czasie jej największego rozwoju, aż do czasu przejęcia przez IBM w 2003 roku. Wtedy odszedł z firmy, ale jeszcze przez ponad rok — do maja 2004 roku — pełnił w niej funkcję konsultanta technicznego. Równolegle z pracą w firmie Rational Ivar Jacobson zajmował się innymi interesującymi zagadnieniami. Między innymi pracował w firmie Jaczone AB, którą założył w kwietniu 2000 roku wraz ze swoją córką Agnetą Jacobson. Jaczone zajmuje się implementacją starej wizji — dąży do tego, aby proces wytwarzania oprogramowania był aktywny, a nie pasywny. W aktywnym procesie tworzenia oprogramowania kierownik projektu wspomaga programistów w realizacji ich projektów.

WSPÓŁTWÓRCY

551

Ivar Jacobson doskonale zdaje sobie sprawę, że społeczność program istów bardzo potrzebuje uspraw nienia w zakresie stosowania m echanizm ów w ytw arzania oprogramowania. W 2004 roku założył więc firmę Ivar Jacobson International, której celem jest prom ow anie zespołów program istycznych na świecie i pom oc im w stosow aniu dobrych praktyk wytwarzania oprogramowania. Firma Ivar Jacobson International obecnie działa za pośrednictwem osobnych podmiotów w sześciu krajach: Wielkiej Brytanii, Stanach Zjednoczonych, Szwecji, Chinach, Australii i Singapurze. W 2007 roku jego nowa firma przejęła firmę Jaczone. Od tamtej pory obie firmy są połączone. Sim on P eyton Jones jest absolw entem Trinity College Cambridge z 1980 roku. Po dwóch latach pracy w branży przez siedem lat był wykładowcą na Uniwersytecie College London, a przez dziewięć lat wykładał na Uniwersytecie w Glasgow. Następnie, w 1998 roku, przeniósł się do ośrodka badawczego Microsoft Research (Cambridge). Do jego głównych zainteresowań badawczych należą funkcyjne języki programowania — ich implementacja oraz zastosowania. W swojej karierze prowadził szereg projektów badawczych skoncentrow anych w okół zagadnień projektow ania i im plementacji wysokiej jakości systemów języków funkcyjnych zarów no dla systemów jednoprocesorowych, jak i maszyn równoległych. Był jednym z głównych twórców projektu popularnego dziś języka funkcyjnego Haskell. Jest wiodącym projektantem powszechnie używanego kom pilatora GHC (ang. Glasgow Haskell Compiler). N apisał dwa podręczniki na tem at implementacji języków funkcyjnych. W śród jego bardziej ogólnych zainteresowań znalazły się projektowanie języków, bogate systemy typów, architektury kom ponentów program ow ych, technologia kompilatorów, generowanie kodu, środowiska uruchomieniowe, maszyny wirtualne oraz m echanizm y odśmiecania. D odatkow ą motywację stanow i dla Jacobsona bezpośrednie wykorzystanie teorii do praktycznego projektowania i implementacji języków — jest to jeden z powodów, dla których tak bardzo lubi program ow anie funkcyjne. Brian Kernighan otrzymał tytuł licencjata Uniwersytetu w Toronto w 1964 roku oraz tytuł doktora inżynierii elektrycznej Uniwersytetu w Princeton w 1969 roku. Do 2000 roku pracował w ośrodku Computing Science Research instytutu Bell Labs. Obecnie pracuje na wydziale informatyki w Princeton. Jest autorem ośmiu książek i kilku artykułów technicznych. Jest też właścicielem czterech patentów. W 2002 roku został wybrany do organizacji National Academy of Engineering. Do jego zainteresowań badawczych należą języki programowania, narzędzia i interfejsy ułatwiające użytkownikom — często niebędącym specjalistami — korzystanie z komputerów. Jest także zainteresowany nauczaniem technologii osób bez przygotowania technicznego. T hom as E. Kurtz urodził się w okolicach Chicago, w stanie Illinois, 22 lutego 1928 roku. Uczęszczał do Knox College w Illinois, który ukończył w 1950 roku.

552

WSPÓŁTWÓRCY

Następnie studiował na Uniwersytecie Princeton, gdzie w 1956 roku uzyskał tytuł doktora matematyki. Od 1956 roku Kurtz był członkiem kadry naukowej Dartmouth College i pracował tam aż do emerytury w 1993 roku. Prowadził wykłady ze statystyki, analizy numerycznej i informatyki. W latach 1 9 6 3 -1 9 6 4 wraz z Johnem Kemenym (który później został rektorem Dartmouth College) opracował język programowania BASIC. Dzięki rozwojowi systemów z podziałem czasu oraz rewolucji w dziedzinie kom puterów osobistych BASIC przez kilka dziesięcioleci był najpowszechniej używanym językiem program ow ania na świecie. W latach 1966 - 1975 Kurtz był dyrektorem ośrodka Kiewit Com putation Center w Dartmouth. Pracował w różnych grupach badawczych i komisjach. Wraz z dr. Kemenym napisał kilka książek na temat programowania. Po przejściu na emeryturę i zaprzestaniu pracy w D artm outh aktywnie działał w firmie True BASIC Incorporated, która stworzyła i sprzedawała język program ow ania BASIC, a także inne program ow e produkty edukacyjne przeznaczone dla komputerów osobistych. Tom Love uzyskał tytuł doktora nauk poznawczych Uniwersytetu w Waszyngtonie, gdzie studiował charakterystyki poznawcze programistów komputerowych, którzy odnieśli sukces. Pierwszą pracę Tom Love podjął w firmie General Electric, gdzie projektow ał interfejs użytkow nika silnika wyszukiwania tekstu — coś w rodzaju pudełkowej wersji Google! Kilka miesięcy później otrzym ał propozycję z Office of Naval Research dotyczącą kontynuowania jego badań w ramach pracy doktorskiej. To doprowadziło do stworzenia grupy Psychologii Oprogramowania w firmie GE. Firma ITT pozyskała Toma Love z GE i poleciła stworzenie grupy wiodących badaczy w dziedzinie oprogramowania. To w tym zespole Brad Cox zaprojektował i stworzył pierwsze obiektowe rozszerzenie języka C. Grupa ITT zajm owała się także oprogramowaniem groupware, przetwarzaniem rozproszonym oraz interaktywnymi środowiskami wytwarzania oprogram ow ania (już w 1982 roku!). Bazując na tych doświadczeniach, w 1982 roku Tom Love stał się pierwszym kom ercyjnym użytkownikiem języka Smalltalk. W 1983 roku Tom Love i Brad Cox założyli pierwszą firmę zajmującą się tworzeniem produktów technologii obiektowej. W firmie Stepstone prom ow ali technologię obiektową, opracowali koncepcję programowych układów scalonych oraz stworzyli pierwszy niezależny zbiór klas wielokrotnego użytku IC-pak 201. Obok innych dokonań udało im się przekonać Steve’a Jobsa do wykorzystania języka Objective-C jako języka program ow ania dla firmy NeXT Com puter (później stał się on podstaw ą systemu operacyjnego OS X stworzonego przez firmę Apple). Tom Love był także pomysłodawcą konferencji OOPSLA stowarzyszenia ACM oraz organizatorem grupy ochotników, którzy stworzyli tę konferencję. Po pięciu latach pracy w roli jednoosobowego konsultanta Tom Love dołączył do firmy IBM C onsulting i założył Object Technology Practice — firmę zajmującą się w ytw arzaniem oprogramowania, która zrealizowała szereg ważnych projektów

WSPÓŁTWÓRCY

5 53

oprogramowania dla ważnych klientów firmy IBM. Sukces tych rozwiązań przyczynił się do ściągnięcia Toma Love’a do firmy Morgan Stanley. Tam Love zmodernizował system zarządzania ryzykiem kredytowym. Wdrożył go na dwa dni przed upadkiem banku Barings. W 1997 roku Tom Love razem z dr. Johnem Wootenem założył firmę Shoulders Corp. W firmie ShouldersCrop Love kierował kilkunastom a pomyślnie zrealizowanymi 100-dniowymi projektami, włącznie z największym znanym projektem wytwarzanym w technologii Agile, który został zakończony w 2001 roku. Wiele doświadczeń z technologią obiektową opisał w swojej książce Object Lessons, wydanej w 1993 roku przez Cambridge University Press. Bertrand Meyer jest profesorem inżynierii programowania na uczelni ETH w Zurichu (Szwajcarski Federalny Instytut Techniczny) oraz głównym architektem w firmie Eiffel Software z siedzibą w Santa Barbara, w stanie Kalifornia. Kariera Bertranda Meyera ma kilka kierunków. Pracował jako menedżer projektów oprogramowania (nadzorował proces tworzenia narzędzi i bibliotek zawierających łącznie kilka milionów wierszy kodu). Zajm ow ał się projektow aniem architektury oprogram ow ania, edukacją, badaniami naukowymi, pisaniem książek oraz świadczył usługi doradcze. Opublikował 10 książek, w tym kilka bestsellerów, między innymi Object-Oriented Software Construction (Prentice Hall — nagroda Jolt Award 1998), Eiffel: The Language, Object Success oraz Introduction to the Theory o f Programming Languages (Prentice-Hall PTR). Jego najnowsza książka Touch o f Class: An Introduction to Programming Well — podręcznik programowania dla początkujących, wykorzystujący w pełnym zakresie techniki obiektowe i kontrakty — została wydana przez wydawnictwo Springer-Verlag w m arcu 2009 roku. Publikacja ta jest rezultatem sześciu lat pracy w charakterze wykładowcy podstaw programowania na uczelni ETH. Jako pracow nik naukow y Meyer opublikow ał ponad 200 artykułów związanych z oprogram ow aniem . Najwięcej artykułów napisał na tem at architektury i projektow ania oprogram ow ania (projektow anie kontraktow e), języków programowania (Eiffel — obecnie standard ISO), testowania i metod formalnych. Do głównych zainteresow ań badawczych Bertranda Meyera oraz grupy w spółpracow ników z ETH należą bezpieczne i proste programowanie współbieżnej architektury wielordzeniowej (SCOOP), zautomatyzowane testowanie (AutoTest), dowodzenie poprawności programów, narzędzia pedagogiczne (Trucstudio), nauczanie inform atyki, środowiska projektow ania (EiffelStudio, Origo), kom ponenty wielokrotnego użytku, proces tworzenia oprogramowania oraz utrwalanie obiektów. O trzym ał nagrodę ACM Software System Award (2006) oraz pierwszą nagrodę Dahla-Nygaarda za techniki obiektowe (2005). Jest członkiem stowarzyszenia ACM, a także Francuskiej Akademii Technicznej. Robin M ilner ukończył Uniwersytet Cambridge w 1958 roku. Po krótkim okresie pracy w kilku firmach w 1973 roku zaczął pracować na Uniwersytecie w Edynburgu,

554

WSPÓŁTWÓRCY

gdzie w 1986 roku był współzałożycielem Laboratory for Foundation of Computer Science. W 1988 roku został wybrany przedstawicielem w Royal Society, a w 1991 roku otrzym ał nagrodę AM Turinga przyznaną przez ACM. W 1995 roku trafił ponownie na Uniwersytet Cambridge, gdzie przez cztery lata prowadził laboratorium komputerowe. W 2001 roku przeszedł na emeryturę. Do jego osiągnięć badawczych (w wielu przypadkach we współpracy z innymi naukowcami) można zaliczyć: system LCF — model będący bazą wielu późniejszych systemów interaktyw nego w nioskow ania; Standard ML — ścisły język programowania funkcyjnego; rachunek systemów komunikujących się ze sobą (Calculus of Communicating Systems — CCS) oraz rachunek pi. Obecnie pracuje nad systemem Bigraphs — topograficznym modelem m obilnych systemów interaktywnych. Model ten łączy w sobie siłę rachunku pi, który podkreśla możliwości m odyfikowania powiązań m obilnych agentów, z technologią Mobile Ambients (Cardelli i Gordon) opisującą sposób ich poruszania się w przestrzeni siatkowej. W połączeniu tych dw óch własności są one traktow ane jako odrębne: „Miejsce, w którym się znajdujesz, nie ma wpływu na to, z kim możesz rozmawiać” . Prowadzi to do utworzenia ogólnego modelu, który nie tylko podsumowuje wiele rachunków procesów, ale także dostarcza rygorystycznej platformy projektowania powszechnych systemów komputerowych, które zdominują technikę obliczeniową w XXI wieku. Charles H. M oore urodził się w 1938 roku. Wychowywał się w Michigan. Uzyskał tytuł licencjata fizyki MIT. Jest żonaty z W inifred Bellis i m a syna Erica. Obecnie mieszka w Incline Village nad pięknym jeziorem Tahoe. Jeździ WRX, chodzi po górach szlakami Tahoe Rim Trail i Pacific Crest Trail, dużo czyta. Lubuje się w wyszukiwaniu prostych rozwiązań. Jeśli jest taka potrzeba, modyfikuje problem. W latach sześćdziesiątych pracował jako wolny programista. W 1968 roku opracował język Forth (Forth jest prostym, wydajnym i uniwersalnym językiem programowania, z którego Charles H. Moore jest dumny). Właśnie tego języka użył do programowania teleskopów w obserwatorium NRAO (ang. National Radio Astronomy Observatory). W 1971 roku jako jeden ze współzałożycieli zainicjował działalność firmy Forth Inc., której celem było programowanie aplikacji czasu rzeczywistego. W 1983 roku, zirytowany słabą jakością sprzętu, założył wraz z grupą innych osób firmę Novix, Inc., która zaprojektowała układ mikroprocesorowy NC4000. Kolejną wersją tego układu był Harris RTX2000, który miał jakość pozwalającą na stosowanie go w statkach kosmicznych. W łaśnie ten mikroprocesor okrąża Saturna na statku Cassini. Gdy pracow ał w firmie Com puter Cowboys, wykorzystał własnej konstrukcji oprogramowanie do zaprojektowania mikroprocesorów ShBoom, Mup20, F21 oraz i21. W szystkie one bazują na języku Forth. M oore jest bardzo dum ny z tych małych, szybkich i energooszczędnych układów.

WSPÓŁTWÓRCY

555

Już w tym wieku Moore był jednym z założycieli firmy IntellaSys, która stworzyła język colorForth służący do projektowania narzędzi dla układów wielordzeniowych. W roku 2008 firma Intellasys stworzyła i dostarczyła na rynek 40-rdzeniową wersję procesora. Obecnie Moore pracuje nad przenoszeniem swoich narzędzi projektowych na ten imponujący układ. Jam es R um baugh otrzym ał tytuł licencjata fizyki w MIT, m agistra astronom ii w Caltech oraz doktora informatyki w MIT. D oktorat w MIT realizował w grupie struktur obliczeniowych profesora Jacka Dennisa — jednej z pierwszych grup zajmujących się podstawowymi modelami obliczeniowymi. W swojej pracy doktorskiej zaprezentował język oraz architekturę sprzętową dla kom putera do przetwarzania danych — była to w maksymalnym stopniu współbieżna architektura komputerowa. Przez 25 lat pracował w ośrodku Research and Development Center firmy General Electric w Schenectady, w stanie Nowy Jork, gdzie zajmował się różnymi projektami. Między innym i stworzył jeden z pierwszych wieloprocesorowych systemów operacyjnych, opracow ał algorytmy rekonstrukcji rentgenow skich zdjęć tom ograficznych, system projektowania układów VLSI, framework dla interfejsów graficznych oraz język program ow ania obiektowego. We w spółpracy z kolegami z firmy GE opracował technologię OMT (ang. Object Modeling Technique) i napisał książkę Object-Oriented Modeling and Design (Prentice Hall), która spopularyzowała technologię OMT. Przez sześć lat prow adził popularną comiesięczną kolum nę w magazynie Journal o f Object-Oriented Programming (JOOP). W 1994 roku dołączył do zespołu firmy Rational Software Corporation w Cupertino, w stanie Kalifornia, gdzie wraz z Gradym Boochem opracował język UML (ang. Unified Modeling Language). Później w pracy nad UML brał udział Ivar Jacobson, a także współpracownicy z grupy OMG (ang. Object Modeling Group). Standaryzacja języka UML przeprowadzona przez grupę OMG doprowadziła do rozpowszechnienia się języka UML jako wiodącej notacji modelowania oprogramowania. Język UML został spopularyzowany przez książki autorstwa Rumbaugha, Boocha i Jacobsona. Rumbaugh pomagał w dalszym rozwoju języka UML. Jest gorącym zwolennikiem wykorzystywania dobrych zasad inżynieryjnych w projektowaniu oprogramowania. Po przejęciu firmy Rational przez firmę IBM w 2006 roku ostatecznie przeszedł na emeryturę. James Rumbaugh doskonale jeździ na nartach, słabo gra w golfa, a w weekendy chodzi po górach. Bywa w operze, teatrze, na balecie oraz odwiedza muzea sztuki. Lubi dobre jedzenie, podróże, fotografowanie, pracę w ogrodzie i naukę obcych języków. Czyta dużo na temat kosmologii, ewolucji, nauk poznawczych, czasami czytuje również poezję epicką, mitologię, fantastykę, historię oraz publicystykę. Razem z żoną mieszka w miejscowości Saratoga w stanie Kalifornia. Mają dwóch synów, którzy studiują w college’u. Bjarne Stroustrup zaprojektował i zaimplementował język C++. W ciągu ostatniej dekady stał się on najczęściej w ykorzystywanym językiem program ow ania

556

WSPÓŁTWÓRCY

udostępniającym obiektowe techniki. Dzięki językowi C++ można było zastosować techniki abstrakcji w najważniejszych projektach. Posługując się językiem C++ jako narzędziem, Stroustrup był pionierem wykorzystania technik program ow ania obiektowego oraz generycznych technik programowania w tych obszarach, w których najw ażniejsza jest wydajność. Do przykładów tego rodzaju zastosow ań m ożna zaliczyć ogólne systemy programowania, przełączniki, symulacje, grafikę, interfejsy użytkownika, systemy wbudowane oraz obliczenia inżynierskie. Zakres oddziaływania języka C++ oraz idee, które ten język spopularyzował, są wyraźnie widocznie także poza społecznością języka C++. Takie języki, jak C, C#, Java i Fortran 99, dostarczają w łasności, których używanie zostało zapoczątkowane w języku C++. Podobnych własności używają także takie technologie, jak COM i CORBA. Książka Bjarne’a Stroustrupa The C++ Programming Language (Addison-Wesley — pierwsze wydanie 1985, drugie wydanie 1991, trzecie wydanie 1997, wydanie specjalne 2000) jest najczęściej czytaną publikacją inform atyczną tego rodzaju. Została przetłum aczona na 19 języków1. W swojej późniejszej książce The Design and Evolution o f C++ (Addison-Wesley, 1994) Stroustrup stworzył nowe podstawy opisu tego, jak kształtował się język programowania przez pomysły, ideały, problemy i praktyczne ograniczenia. Nowa książka Programming Principles and Practice Using C++ ma spełniać rolę pierwszego wprowadzenia do programowania oraz języka C++. Oprócz sześciu książek Stroustrup opublikował ponad sto artykułów akademickich i popularnonaukow ych. Stroustrup odegrał aktyw ną rolę w tw orzeniu standardu ANSI/ISO języka C++. W dalszym ciągu kontynuuje pracę nad utrzym aniem i weryfikacją tego standardu. Bjarne Stroustrup urodził się w Aarhus, w Danii. Tytuł magistra m atem atyki i inform atyki zdobył na Uniwersytecie w Aarhus. Pracę doktorską z dziedziny przetwarzania rozproszonego obronił na Uniwersytecie Cambridge w Anglii. W latach 1979 - 2002 pracował jako naukowiec, a później menedżer w ośrodkach Bell Labs oraz AT&T Labs w New Jersey. Obecnie jest szefem katedry College of Engineering oraz profesorem informatyki na Uniwersytecie A&M w Teksasie. Jest członkiem organizacji NAE (ang. National Academy o f Engineering) oraz stowarzyszeń ACM i IEEE. W swojej karierze otrzymał wiele nagród zawodowych. G uido van R ossum jest tw órcą Pythona, jednego z ważniejszych języków programowania wykorzystywanych w internecie. Społeczność użytkowników Pythona określa go skrótem BDFL (Benevolent Dictator For Life — życzliwy dyktator życia). Tytuł ten z powodzeniem mógłby pochodzić z parodii Monty Pythona (choć tak nie jest). Guido dorastał w Holandii. Przez długi czas pracował na uczelni CWI w Amsterdamie, gdzie narodził się język Python. W 1995 roku przeniósł się do Stanów Zjednoczonych. Zamieszkał w Północnej Virginii. Tam się ożenił i tam urodził mu się syn. W 2003 roku wraz z rodziną przeprowadził się do Kalifornii, by podjąć pracę w firmie Google. 1 Wydanie polskie: Język C++, wydanie VII, WNT, Warszawa 2004.

WSPÓŁTWÓRCY

5 57

Połowę swojego czasu poświęca na projekt open source Pythona, a resztę spędza na używaniu Pythona do realizacji wewnętrznych projektów firmy Google. P hilip W adler lubi wykorzystywać teorię w praktyce, a praktykę w teorii. Dwa przykłady wykorzystania teorii w praktyce: GJ — podstawa nowej wersji Javy firmy Sun z obsługą typów generycznych wywodzi się od kwantyfikatorów z logiki drugiego rzędu. Jego prace nad językiem XQuery to jedna z pierwszych prób zastosowania matematyki do stworzenia standardu branżowego. Przykład wykorzystania praktyki w teorii: lekka jak piórko Java pozwala na wyspecyfikowanie rdzenia języka Java za pomocą mniej niż jednej strony reguł. Wadler jest głównym projektantem języka programowania Haskell. Najistotniejszym wkładem Wadlera w rozwój języka są dwie najważniejsze innowacje: klasy typów i monady. W adler jest profesorem inform atyki teoretycznej na Uniwersytecie w Edynburgu. Jest honorow ym członkiem stowarzyszeń Royal Society-Wolfson Research Merit Fellowship, Royal Society of Edinburgh oraz ACM. Dawniej pracował lub studiował w takich instytucjach, jak Avaya Labs, Bell Labs, Glasgow, Chalmers, Oxford, CMU, Xerox PARC i Stanford oraz gościnnie wykładał na Uniwersytetach w Paryżu, Sydney i Kopenhadze. Zajmuje 70. pozycję na liście najczęściej cytow anych autorów inform atycznych według m agazynu Citeseers. Jest zdobywcą nagrody POPL Most Influential Paper Award. Philip Wadler pracował jako redaktor naczelny magazynu Journal o f Functional Programming, a także w Komitecie Zarządzającym Specjalnej Grupy Zainteresowań Językami Programowania stowarzyszenia ACM. Napisał między innym i takie artykuły, jak „Listlessness is better th an laziness”, „H ow to replace failure by a list of successes”, a także „Theorems for free”. Jest współautorem książek XQuery from the Experts (Addison-Wesley, 2004) oraz Java Generics and Collections (O’Reilly, 2006). Wygłaszał referaty w wielu różnych miejscach na świecie — od Aizu po Zurich. Larry W all zdobywał wykształcenie w wielu miejscach, między innymi w Cornish School of Music, Seattle Youth Symphony, Seattle Pacific University, M ultnom ah School of the Bible, SIL International, UC Berkeley i UCLA. Chociaż kształcił się głównie w dziedzinach muzyki, chemii i lingwistyki, pracuje z kom puteram i od co najmniej 35 lat. Najbardziej znany jest z napisania narzędzi rn , patch oraz języka programowania Perl, ale woli myśleć o sobie jako o hakerze-humaniście, którego życiowym powołaniem jest wniesienie odrobiny radości do ponurego żywota programistów. Larry pracował dla takich firm, jak Seattle Pacific, MusiComedy Northwest, System Development Corporation, Burroughs, Unisys, NSA, Telos, ConTel, GTE, JPL, NetLabs, Seagate, Tim O’Reilly, Perl Foundation, oraz dla siebie samego, przy czym zwrot „pracował dla” mieści w sobie wiele definicji słowa „pracował” . Obecnie jest zatrudniony w firmie NetLogic Microsystems w M ountain View w stanie Kalifornia. W drodze do pracy mija siedziby Computer History Museum oraz firmy Googleplex, co musi coś znaczyć. Najpewniej jest to coś absurdalnego.

558

WSPÓŁTWÓRCY

John E. W arnock jest wiceprzewodniczącym rady nadzorczej Adobe Systems, Inc. — firmy, którą założył w 1982 wspólnie z Charlesem Geschkem. Dr W arnock był prezesem firmy Adobe przez pierwsze dwa lata jej istnienia oraz dyrektorem zarządzającym przez następnych 16 lat. Jest pionierem najbardziej now atorskich na świecie technologii graficznych, publikacyjnych, internetowych oraz zarządzania dokumentami elektronicznymi. Technologie te zrewolucjonizowały dziedzinę publikacji online oraz wizualnej komunikacji. Dr W arnock jest właścicielem sześciu patentów. Sukces przedsiębiorczości W arnocka został odnotow any przez niektóre najbardziej wpływowe czasopisma biznesowe oraz branżowe. W arnock otrzymał wiele nagród za osiągnięcia z zakresu techniki i zarządzania. O to lista w ybranych nagród: Entrepreneur of the Year przyznany przez firmy Ernst & Young, Merrill Lynch oraz czasopismo Inc. Magazine; Distinguished Alumnus Award przyznana przez Uniwersytet Stanu Utah; Software Systems Award przyznana przez stowarzyszenie ACM; Award for Technical Excellence przyznana przez stowarzyszenie NGA (ang. National Graphics Association); m iędzynarodow a nagroda Distinguished Service to Art and Design przyznana przez First Rhode Island School of Design. Dr W arnock otrzym ał też nagrodę Edwin H. Land Award od stowarzyszenia Optical Society of America, medal Bodleiana od Uniwersytetu Oxford, a także medal Lovelace od stowarzyszenia British Computer Society. Jest wybitnym członkiem National Academy of Engineering, a także Amerykańskiej Akademii Sztuk i Nauk. Otrzymał honorowe doktoraty od Uniwersytetu Stanu Utah oraz American Film Institute. W arnock zasiada w radach nadzorczych firm Adobe Systems Inc., Knight-Ridder, Octavo Corporation, Ebrary Inc., M ongonet Inc., Netscape Com m unications oraz Salon Media Group. W przeszłości był dyrektorem Tech M useum of Innovation w San Jose. Jest członkiem Zarządu Powierniczego uczelni American Film Institute oraz członkiem zarządu Sundance Institute. Przed założeniem firmy Adobe Systems Warnock był głównym naukowcem w ośrodku Xerox Palo Alto Research Center (PARC). Przed rozpoczęciem pracy w Xerox Warnock piastował kluczowe stanowiska w firmach Evans & Sutherland Computer Corporation, Computer Sciences Corporation, IBM oraz na Uniwersytecie Stanu Utah. W arnock posiada tytuł licencjata i magistra matematyki oraz doktora w dziedzinie inżynierii elektrycznej nadany przez Uniwersytet Stanu Utah. Peter W einberger pracuje w now ojorskim oddziale firmy Google od połowy 2003 roku. W firmie tej zajmuje się różnym i projektam i dotyczącymi obsługi bądź przechow ywania dużych ilości danych. Przedtem (od czasu, kiedy rozdzieliły się firmy AT&T i Lucent) pracował w firmie Renaissance Technologies — słynnym funduszu hedgingowym — gdzie rozpoczynał jako dyrektor do spraw technicznych i był odpowiedzialny za technikę komputerową, oprogramowanie oraz bezpieczeństwo informatyki. W ubiegłym roku uciekł od tego wszystkiego i pracował nad systemem obrotu papierami wartościowymi.

WSPÓŁTWÓRCY

559

Do m om entu rozdzielenia firm AT&T i Lucent pracował w firmie Computer Science Research w ośrodku Bell Labs w Murray Hill. Zanim osiągnął szczebel kierowniczy, zajm ow ał się bazami danych, językiem AWK, sieciowymi systemami plików, wydajnością kompilacji i profilowaniem oraz — bez wątpienia — wykonywał również inne zadania uniksowe. Następnie piastował stanowiska kierownicze, między innymi był W icedyrektorem do spraw Badań Inform atycznych (takie nazwy lubią tylko naprawdę duże korporacje). Peter Weinberger zarządzał prawie jedną trzecią badań związanych z matematyką, statystyką i informatyką. Ostatni rok w AT&T spędził w dziale długoterminowych klientów, gdzie próbował przewidywać przyszłość. Zanim zaczął pracę w Bell Labs, w ykładał m atem atykę na Uniwersytecie Stanu M ichigan w Ann Arbor. O publikow ał wiele artykułów . O statni z nich ukazał się w 2002 roku. Peter W einberger posiada tytuł licencjata nadany przez Swarthm ore College w Swarthmore w stanie Pensylwania oraz tytuł doktora matematyki (teoria liczb) nadany przez Uniwersytet Stanu Kalifornia w Berkeley.

560

WSPÓŁTWÓRCY

SKOROWIDZ

.NET, 28, 57, 306, 368 3GL, 372 4GL, 372

A A+, 310 ABC, 43, 46, 52 abstrakcja, 231, 292, 347, 420, 442, 458 abstrakcja danych, 15 ACID, 289 Ada, 525 Adobe AIR, 508 Adobe Systems, 485 Agile, 411 Aho Al, 136 Aho-Corasick, 153 akademicki język programowania, 528 aktualizacja do nowej wersji, 177 aktualizowanie kodu, 218 alfa, 42 Algol 60, 418 Algol 68, 52 algorytmiczna dekompozycja, 442 algorytmy, 71, 136, 137, 147, 204, 206, 351 Aho-Corasick, 153 CFD, 355 dopasowywanie wzorców, 138 drzewo rozpinające, 481 zależne liniowo od czasu, 137 analiza OLAP, 296 analiza systemów współbieżnych, 260 analizowanie kodu źródłowego, 259 anonimowe delegaty, 245 ANSI C, 223 API, 61, 267, 346, 363, 371, 383 API usług sieciowych, 178 APL, 65, 66, 81, 287, 307, 460 automatyczna współbieżność, 78 bloki budulcowe, 73 błędy w projektowaniu języka, 75 czystość pojęciowa, 69 druga klasa, 75 duże zbiory danych, 78 element neutralny, 74 elementy tablic, 78 forma wyrażeń, 69 funkcje, 75 kolekcje, 77, 78 kompatybilność, 69

kompilator, 70 nauczanie programowania, 71 nauka języka, 68 notacja Iversona, 66 operatory, 75 opracowywanie składni, 67 pliki, 79 podejmowanie decyzji projektowych, 75 programowanie współbieżne, 77 projektowanie systemów, 81 przestrzenie nazw, 79 przystosowanie języka do warunków współczesnych, 81 składnia, 67 standaryzacja, 69 tablice, 78 tablice ogólnego przeznaczenia, 76 techniki projektowe, 74 w arunki brzegowe, 74 wskaźniki, 80 współbieżność, 76 zakres akceptacji języka, 67 zarządzanie sieciami w komercyjnych systemy z podziałem czasu, 69 zestaw znaków, 68, 76 zmienne współdzielone, 77 APL2, 78 APL\360, 66, 76 aplikacje biznesowe, 454 internetowe, 336, 340 mashup, 28 wbudowane, 21 webowe, 28 wolno stojące, 208 Apple, 305, 324 Apple Soft BASIC, 127 architekt projektu wytwarzania oprogramowania, 399, 413 architektura RISC, 330 architektura SOA, 332, 431 architektura von Neumanna, 58, 433 architektura zorientowana na usługi, 330 ArchiText, 529 argumenty-flagi, 61 arkusze kalkulacyjne, 297, 310 ARPA, 498 ASCII, 199 asembler, 14 asercje, 176 asocjacje, 434

561

ASP.NET, 368 assert, 360 Astrahan Morton, 290 AT&T, 33 ataki wstrzykiwania kodu SQL, 296 attached type, 540 auto, 29 AutoMath Bruijna, 258 automaty komórkowe, 433 automaty skończone, 152 automatyczna inicjalizacja zmiennych, 169 automatyczna współbieżność, 78 automatyczne sprawdzanie kodu, 360 automatyzacja procesu testowania, 531 autoryzacja, 335 AWK, 135, 147 algorytmy, 137 automatyczna inicjalizacja zmiennych, 169 błędy, 169 debugowanie, 169 dopasowywanie wzorców, 138 formalizm, 144 implementacja, 191 mechanizm rozszerzeń, 146 ograniczenia, 170 paradygmat wzorzec - działanie, 142 programowanie przez przykład, 201 programy, 136 projekt języka, 138, 191 projektowanie, 136 rozmiar danych, 137 składanie funkcji, 144 stan zmiennych, 170 tworzenie dużych programów, 195 zastosowania, 136, 143, 160 zestaw testów regresji, 149 zmiany w języku, 146 AWT, 347

B badania, 497, 499 badania w informatyce, 279 Balaban David, 231 BASIC, 109 biblioteki, 130 brak w rażliwości na spacje, 128 deklaracje zmiennych, 119 działania arytmetyczne, 117 ewolucja w sprzęcie, 116 FORMAT, 111 GOSUB, 117 GOTO, 117 implementacja języka, 113 kod B, 114 komentarze, 122 liczby, 111

562

SKOROWIDZ

moduły, 114 numery wierszy, 110 operacje arytmetyczne, 111 optymalizacja, 119 PRINT, 111 projekt języka, 113, 122, 124 przenośność, 121 przystosowanie języka do warunków współczesnych, 125 REM, 122 tekst programu, 113 wartości domyślne, 111 wprowadzanie nowych własności do języka, 118 założenie projektowe, 110 baza danych, 437 Bell Labs, 14, 144, 153, 154 beta, 42 bezpieczeństwo, 50, 317, 334, 377, 436 bezpieczeństwo kodu, 51, 192 bezpieczeństwo na poziomie wątków, 356 bezpieczeństwo oprogramowania, 20 bezpieczeństwo typologiczne, 21 bezpieczeństwo wątków, 356 bezszwowe wytwarzanie oprogramowania, 518, 529 biblioteki, 38, 130, 144, 255, 315, 347, 35 9 ,3 6 3 STL, 24 Bignum, 43 blisko sprzętu, 18 BLISS, 506 Bloch Josh, 61 blokady, 18 bloki budulcowe, 73 błędy, 156, 158, 185, 193, 206, 224, 322, 437 błędy użytkowników, 281 przepełnienie bufora, 416 w projekcie języka, 270 zarządzanie pamięcią, 359 BNF, 321, 418 Booch Grady, 391, 438 Boost, 23, 32 boost::networking, 27 bootstrapping, 89 Boyce Ray, 284 Boyce Raymond, 283 BPEL, 455 branżowy język programowania, 528 brown field, 458 budowanie abstrakcja, 442 duże systemy, 167 implementacja, 183 komponenty systemu, 192 modele, 262

społeczność, 474 systemy z fragmentów wielokrotnego użytku, 262 bufory TLB, 98 BUGS, 156 bycie zwinnym, 396

c C, 14, 168, 172, 323, 515 C#, 25,193, 3 5 8 ,3 6 5 ,3 7 8 debugowanie, 377, 383 decyzje projektowe, 368, 369 kod zarządzany, 369 LINQ, 376 rozwój języka, 373 standaryzacja, 381 wsteczna zgodność, 368 zespół projektowy, 380 C++, 13, 44, 57, 145, 172, 193, 305, 316, 324, 374 bezpieczeństwo oprogramowania, 20 bezpieczeństwo typologiczne, 21 debugowanie kodu, 19 decyzje projektowe, 14 delete, 21 klasy bazowe, 24 kontrola typów, 23 model maszyny, 28 nazwane operacje, 27 new, 21 obiekty futurę, 28 obsługa interfejsu GUI, 23 ogólne zarządzanie zasobami, 18 praca sieciowa, 28 programowanie generyczne, 22, 23 programowanie obiektowe, 24 projektowanie systemów wbudowanych, 21 pule wątków, 28 rozmieszczenie danych w pamięci, 16 semantyka wartości, 18 STL, 22, 24 string, 17 tablice, 17 testowanie, 19 układ dół-góra, 18 używanie języka, 19 wątki, 26 wskaźniki, 16 współbieżność, 24, 25, 26 zarządzanie zasobami, 17 zasada „blisko sprzętu”, 18 zasięg, 17 złożoność języka, 15 C++ 2.0, 29 C++0x, 26 C++0x FAQ, 29

C++98, 27 C89, 216, 223 C 9 9 ,223 CAD, 304 Calculus of Communicating Systems, 260 Caml, 257 captures, 481 Cayenne, 245 CCS, 260, 261 cel implementacji, 178 cele pracy, 130 Celes Waldemar, 207 certyfikacja, 335 CFD, 355 CCI, 468 Chamberlin Donald, 283, 284 char, 18 Cherry Lorinda, 161 ciągi znaków, 199, 350 ciągła alokacja, 348 ciężkie wątki, 330 Clean, 245 closure, 210 cloud computing, 506 CLU, 516 CMMI, 398, 400 CNRI, 40 COBOL, 316, 323, 335, 379 Codd Edgar F., 284 Code Contracts, 524 Codeplex, 373 colorForth, 85, 102 COM, 167 common criteria, 335 Comm on Lisp, 197, 357 complex, 23 Computer Science Research Center, 14 concatenative, 85 concept, 29 Concurrent Haskell, 238 Console.Writeline, 383 const, 199 constexpr, 29 C ontinuation Passing Style, 371 co-operative concurrency, 211 COQ, 258 Corasick Margaret, 153 CORBA, 28 Cox Brad, 303 CP AN, 461, 475 CPS, 371 CPython, 57, 58 cross-cutting teams, 402 Curry, 245 currying, 46 cytowanie błędów, 156 czynnik ludzki, 399

SKOROWIDZ

5 63

czystość pojęciowa, 69 czytanie danych, 180 czytelność, 93

D dane const, 199 multimedialne, 69 XML, 299 Dartm outh BASIC, 114, 115 data cubes, 296 data mining, 137 Data Structure Manager, 434 Data Structures Library, 513 data-parallel, 388 de Figueiredo Luiz Henrique, 208 debuger, 215 debugowanie, 38, 59, 129, 141, 151, 157, 169, 176, 185, 194, 215, 224, 320, 360, 377, 383 błędy zarządzania pamięcią, 359 C++, 19 nauka, 60 programowanie funkcyjne, 234 urucham ianie kodu w środowisku maszyny wirtualnej, 223 dedukcja typów bazująca na inicjalizatorach, 29 definicje typów, 245 definiowanie formalnej semantyki dla języka, 249 deklaracje zmiennych, 119 delete, 21 Delphi, 369, 370 design by contract, 511 determinizm, 299 Developers Kit for iPhone, 305 diagramy przepływu, 392 diagramy UML, 241, 362 diff, 60, 175 Display PostScript, 496 długowieczne oprogramowanie, 188 długowieczność języka programowania, 502 DMCA, 339 dobre komunikaty o błędach, 224 dobry badacz, 499 dobry kod, 105 dobry programista, 47, 105, 215, 314, 501 dobry styl projektowania, 133 dodatki, 359 dodatki do bibliotek, 42 dodatkowa złożoność, 346 dodawanie nowych własności języka, 307, 380, 537 dokumentacja, 272, 392, 402 dokumentacja API, 384 dokumentacja opisująca język, 148

564

SKOROWIDZ

dokumentacja oprogramowania, 106, 122, 147, 361 dokumentacja wzorców, 451 dokumentowanie projektu, 216 dokumenty HTML, 504 RFC, 477 XML, 299 dołączanie kodu źródłowego do mikrokomputerów, 102 Domain-Specific Languages, 159, 246, 404 domknięcia, 27, 210, 218 domknięcia funkcyjne, 244 dopasowywanie wzorców, 135, 137 współbieżność, 153 dostarczanie wiedzy, 409 dostęp do danych XML, 299 dostępność zasobów, 136, 158, 482 doświadczenie, 59 double, 18 dowody, 534 dowody właściwości programów, 243 dowodzenie programów, 532 dowodzenie twierdzeń, 258, 264 dół-góra, 18, 50 Drake Fred, 40 druga klasa, 75 drukarka laserowa, 488 DSL, 159, 246, 404, 513 DSM, 434 DTP, 485, 496 duże programy, 114 duże zbiory danych, 78 dynamiczne własności języka, 50 dynamiczny język programowania, 386 dyspozytor, 223 dyspozytor kodu bajtowego, 55 działania arytmetyczne, 117 działania arytmetyczne na wskaźnikach, 17 dziedzictwo, 318 dziedziczenie, 120, 324, 325, 389, 522, 534 wielokrotne, 534 dzienniki systemowe, 181

E ECMA, 381 edukacja, 312, 497, 501 edytor, 106 edytor WYSIWYG, 129 efekt drugiego systemu, 318 efekt zamknięcia, 235 efekty uboczne, 231, 233 EIFFEL, 511 cele projektowe, 523 dodawanie nowych własności języka, 537 dowody, 533, 534

dziedziczenie, 522, 534 ewolucja programów, 521 klasy, 534 kontrakty, 517, 524, 531 mechanizm dowodzenia, 534 modyfikacja programów, 521 projektowanie kontraktowe, 516 serializacja strumieni, 529 testowanie, 531 typy generyczne, 522, 524 ukrywanie informacji, 521 wątki, 521 wielokrotne wykorzystanie kodu, 521 współbieżność, 521 zastosowanie, 512 zgodność wstecz, 538 Eiffel Base, 513 Eiffel Studio, 533 Eiffel Testing Framework, 533 EiffelBase, 523 EiffelThreads, 521 EiffelVision, 523 element neutralny, 74 Emerald, 26 enkapsulacja, 114, 126, 329, 332, 347 enumeracje, 15 EQN, 159, 161, 174 equality-type variable, 240 Erlang, 208, 231, 233, 389 Escher, 245 Essential UML, 405 Essential Unified Modeling Language, 404 EUML, 404 ewolucja, 379, 478, 534 ewolucja języka, 452 ewolucja kodu, 216, 521 ewolucja w sprzęcie, 116 Excel, 168, 297 explicit strictness, 230

F F#, 257, 379 faktoryzacja, 103 Falkoff Adin, 65, 66 FAQ, 472 Featherweight Java, 251 features, 393 Feldman Stuart, 145 fileopen, 209 filter, 46 filtrowanie danych wejściowych, 296 FIPS-127, 294 Firefox 3, 62 first-class function, 210 Flash, 496, 507

flooding algorithm, 481 for, 218, 385 forall, 253 formalizm, 144, 249, 250, 443 formalizm semantyki, 144 formalna modyfikacja wersji, 275 formalna specyfikacja, 199 formalna specyfikacja projektu, 361 formalna specyfikacja rdzenia języka, 443 formalne aspekty projektowania języka, 382 format PDF, 496 Forth, 73, 85, 128, 262, 490 błędy, 103 budowanie słowników na podstawie słów podstawowych, 86 głębokość stosu, 99 instrukcje warunkowe, 103 kod zszywany pośrednio, 90 komentarze, 106 nazwy, 101 operacje asynchroniczne, 98 operatory, 93 pętle, 104 procedury, 87 projektowanie dziedzinowe, 88 projektowanie języków, 86 projektowanie programów, 88 składnia, 86 słowniki, 101 stos, 87 sygnały wejścia-wyjścia, 88 wątki działające w trybie kooperacyjnym, 99 wygląd kodu, 89 wykrywanie błędów, 92 wywoływanie procedur, 87 zarządzanie stosem, 103 FORTRAN, 26, 80, 323, 418 forward transfer, 118 frame shift, 463 framework, 14, 333, 367 funkcje, 75, 210 funkcje anonimowe, 237 funkcje definiowane przez użytkowników, 75 funkcje lambda, 27 funkcje pierwszej klasy, 210 funkcje skrótów, 434 funkcje wyższego rzędu, 387 futurę, 28, 29

G Gang Czterech, 423 garbage collection, 16, 56, 237, 348, 368 GCC, 22 Generic Programming, 22 genericity, 526

SKOROWIDZ

565

generowanie broszury, 503 kod, 119 kod implementacji, 406, 417, 439 przypadki testowe, 532 generyczność, 521, 524, 526 Geschke Charles, 485, 486, 498 getlocal, 223 GHC, 227, 234, 254 GIL, 58 Glasgow Haskell Compiler, 227 globalna poprawka, 186, 220 głębokość stosu, 99 GNU MP, 43 Google, 64, 298 Google Maps, 178 Gosling James, 346 GOSUB, 117 goto, 233, 234 GOTO, 117, 118, 129 góra-dół, 18, 50 GP, 22, 23 graficzny interfejs użytkownika, 61, 143, 168, 182 grafika, 493 gramatyka, 174 gramatyka bezkontekstowa, 141 Green, 356 grep, 175 grupa OMG, 415 GUI, 13, 23, 45, 61

H haki dla debugera, 209 Hal, 245 Hamm ond Mark, 56 hashing, 434 Haskell, 44, 144, 227, 239, 257 biblioteki, 255 debugowanie, 234 domknięcia funkcyjne, 244 forall, 253 kolejność wartościowania, 232 leniwe wartościowanie, 243, 247 listy składane, 244 metody, 241 obsługa błędów, 234 pojęcia, 241, 245 Prelude, 255 programy, 231 projektowanie systemu klas, 239 przekazywanie informacji o typach, 239 przepływ sterowania, 243 przystosowanie do warunków współczesnych, 256 semantyka formalna, 249

566

SKOROWIDZ

silna typizacja, 241 sprawdzanie typów, 241 stan, 232 system klas, 239, 240 testowanie, 231 typy danych, 241, 242 zarządzanie efektami ubocznymi, 231 Haskell 98, 253 Haskell Prime, 254 Hejlsberg Anders, 365 Hellerman Herb, 66 hermetyzacja, 291, 389, 431 Hibernate, 386 hierarchia modeli, 419 Hillside Group, 424 hipoteza Sapira-Whorfa, 442, 468 HIRO, 60 HLA, 327 Hoare Tony, 512 HOL, 258 HOPL, 211 host logiki, 259 HotSpot, 348 HTML, 299, 464, 507, 509 Hudak Paul, 228 Hughes John, 228 Huguin Jim, 56

I IBM, 295 ICFP, 252 Ichbiach Jean, 528 idea hierarchii modeli, 419 idea wielu poziomów wyłaniających się widoków, 420 ideologia szybkiego wytwarzania aplikacji, 31 IDUG, 295 Ierusalimschy Roberto, 207, 208 if, 385 implementacja, 183, 191, 518 implementacja języka, 161, 224 implementacja systemów operacyjnych, 172 implementacja własności języka, 38 implementacja współbieżności, 433 indirect threaded, 90 infix notation, 503 informatyka, 70, 129, 152, 187, 213, 277, 341,385 informatyk-naukowiec, 278 innowacje, 452, 505 inspiracja do projektu, 451 instalacja łatek, 175 int, 18, 43 integracja na poziomie układów, 328 integracja wielopoziomowa, 328, 329 integralność bazy danych, 285

inteligentne agenty, 410 inteligentny pył, 100 interakcje z interfejsem GUI, 45 interfejsy, 24, 61, 502 interfejsy API, 178, 363 internet, 279, 339, 350, 507, 508 inżynieria, 277 inżynieria oprogramowania, 148, 408 inżynieria społeczna, 400 iPhone, 305, 306 IronPython, 54 Isabelle, 245, 258 ISO, 69 iteratory, 237 Iverson Ken, 65, 76

J J2EE, 343 Jacobson Ivar, 391, 392 jakość oprogramowania, 337 JaM, 486 Java, 15, 25, 32, 145, 325, 345 abstrakcja, 347 AWT, 347 bezpieczeństwo na poziomie wątków, 356 biblioteki, 347 debugowanie, 360 dokumentacja oprogramowania, 361 enkapsulacja, 347 HotSpot, 348 kod źródłowy, 362 kompilator JIT, 348, 356 kompilator kodu bajtowego, 348 kompilatory, 348 maszyna wirtualna, 348, 351, 357 mechanizm odśmiecania, 16, 348, 357 obsługa wyjątków, 352 optymalizacja, 348 paradygmat obiektowy, 353 projektowanie języka, 356 prostota, 346 przestrzenie nazw, 347 referencje, 16, 359 stos, 358 Swing, 347 tablice, 349 technika ciągłej alokacji, 348 typy definiowane przez użytkownika, 18 typy generyczne, 524 udostępnienie kodu źródłowego, 362 współbieżność, 354 zarządzanie pamięcią, 348 zgodność wstecz, 347 Java Business Integration, 331 Java Community Process, 345 Java EE, 346, 347

java.net, 333 Java3, 237 Javadoc, 361 JavaScript, 57, 504 jawna ścisłość, 230 jądra systemów Linux lub BSD, 22 JBI, 328, 329, 331, 334 JBoss, 333 JCP, 345 jednorazowe skrypty, 189 jednowierszowe programy, 202 język, 9 3GL, 372 4GL, 372 A+, 310 ABC, 43, 52 Ada, 525 algebraiczny, 112 Algol 68, 52 APL, 65, 66, 81, 287, 307, 460 Apple Soft BASIC, 127 AWK, 135, 147 BASIC, 109 bazujący na przepływie danych, 434 BLISS, 506 C, 13, 14, 168, 172, 323, 515 C#, 358, 365, 378 C++, 13, 172, 305, 324, 374 CLU, 516 COBOL, 316 colorForth, 102 Common Lisp, 197, 357 Dartm outh BASIC, 115 deklaracyjny, 288 Delphi, 369 DLS, 246 domenowy, 159 DSL, 404 dynamiczny, 64, 208, 386 dziedzinowy, 374, 385, 404, 405 edukacyjny, 366 EIFFEL, 511 Emerald, 26 EQN, 159, 161, 174 Erlang, 389 Featherweight Java, 251 Forth, 73, 85, 128, 490 FORTRAN, 26 funkcyjny, 228, 433 GP, 23 Haskell, 144, 227, 239 HLA, 327 HTML, 509 hybrydowy, 45 JaM, 486 Java, 15, 325, 345 JavaScript, 504

SKOROWIDZ

5 67

język JOSS, 110 konkatenacyjny, 487 Lab View, 453, 454 LaTeX, 503 Lisp, 75 Lua, 207 M, 455 ML, 239, 257 Modula, 366 Modula 2,211 MUMPS, 310 naturalny, 88, 326, 469 niskiego poziomu, 193 o dynamicznej typizacji, 525 o ścisłej typizacji, 14 Oberon, 366 obiektowy, 124 Objective-C, 125, 303, 323 OCaml, 237 ogólnego przeznaczenia, 196, 203, 374 OO, 23 Pascal, 366 Perl, 143, 461, 462 PHP, 64 PIC, 159 PostScript, 485 Prolog, 268 prototypowy, 49 przeznaczony do tworzenia profesjonalnego oprogramowania, 114 przyjazny debugowaniu, 141 Python, 25, 37 Quick Draw, 489 rozszerzalny, 196 Ruby on Rails, 386 SAIL,325,513 Scala, 244, 245, 356 Scheme, 127, 197, 366 SDL, 406 SEQUEL, 286 Simula 67, 512 skryptowy, 136, 208 Smalltalk, 25, 31, 307 SQL, 283 Standard ML, 262 SWL, 298 Tel, 177, 221 TeX, 161 True BASIC, 111, 113, 114 UML, 391, 403, 438 UML 3.0, 440 UNCOL, 268 VDM, 210 Visual Basic, 124 XML, 375 XQuery, 299, 301

568

SKOROWIDZ

zaprojektowany pod kątem nauczania, 114 język blisko sprzętu, 18 język modelowania, 427, 438 język programowania funkcyjnego, 231 język programowania systemowego, 348 język wzorców, 459 język zapytań, 287 JIT, 57, 209, 348 JNI, 358 Johnson Steve, 147 Jones Simon Peyton, 144, 228 JOSS, 110 JSF++, 21 JSR, 42 JUnit, 360, 531 just-in-time, 351 JVM, 57, 338, 347, 352 Jython, 54

K karty, 403 KemenyJohn, 109 Kernighan Brian, 14, 135, 139, 154, 375 kerning, 494 klasy, 15, 239, 320, 520, 522, 534 abstrakcyjne, 24 bazowe, 24 wewnętrzne, 15 klasy typów, 245, 251 Koch Phil, 132 kod B, 114 kod dobrej jakości, 105 kod maszynowy, 87 kod niskopoziomowy, 14 kod obsługi błędów, 155 kod profesjonalnej jakości, 190 kod wielokrotnego wykorzystania, 120, 523 kod zarządzany, 369 kod zszywany pośrednio, 90 kod zszywany procedurami, 90 kod źródłowy, 531 kodowanie JSF++, 21 kolejność wartościowania, 232 kolekcje, 77, 78 kombinatory parserów, 246 komentarze, 106, 122, 215, 384 komentarze dokumentacyjne, 384 komisja do spraw kolekcji oprogram ow ania, 451 kompatybilność, 69 kompilacja dokładnie na czas, 351 kompilacja programów, 175 kompilator, 105, 118 dwuprzebiegowy, 114 GCC, 22 GHC, 227, 254

jednoprzebiegowy, 118 JIT, 348, 356 kod bajtowy Javy, 348 projektowanie, 153 wieloprzebiegowy, 118 kompletność, 291 komplikowanie kodu, 53 komponenty, 168, 333, 519 komponenty JBI, 334 komponenty systemu, 192 komponenty wielokrotnego użytku, 375 komputery wielordzeniowe, 100 komunikacja, 149, 419 komunikacja bezprzewodowa, 101 komunikacja z komputerem, 410 komunikaty o błędach, 195, 224, 225 poziom szczegółowości, 195 koncepcja wielu małych programów działających razem, 182 koncepcja wyłaniających się systemów, 424 koncepcja zbieżnych odkryć, 516 konflikty shift-reduce, 141 kontekst problemu, 467 kontrakty, 517, 524, 531, 534 kontrola wcięć, 193 koordynacja pracy w dużym zespole programistów, 419 kopiowanie pamięci, 358 kostki danych, 296 koszt uruchomienia operacji na innym procesorze, 26 kreatywność w programowaniu, 451 kreatywność w zespole tworzącym oprogramowanie, 186 krótkie skrypty, 189 Księga Czerwonego Smoka, 138 kultura, 398 kultura systemu Unix, 142 Kurtz Tom, 109, 110, 190

L Lab View, 453, 454 lambda, 27, 46 Language-Integrated Query, 376 languge reference, 148 LaTeX, 503 Launchbury J o h n ,236 lazy evaluation, 231 LCF, 258 legacy software, 186 lekkie wątki, 330 leniwe wartościowanie, 231, 243, 244, 247 lex, 141, 175, 225 liberal arts, 501 licencje, 477

liczba wierszy kodu źródłowego, 316 liczby, 117, 209 całkowite dowolnej dokładności, 43 zespolone, 18 lider, 500 ligatura, 494 LIN, 237 linking list, 118 LINQ, 244, 368, 371, 376 Linux, 446 Liskov Barbara, 516 Lisp, 44, 46, 56, 73, 75, 97, 197, 246, 262, 306, 357 list comprehensions, 230 listy FAQ, 472 listy łączenia, 118 listy składane, 230, 244 logika biznesowa, 88 lokalne obejście, 186, 220 long, 43 Love Tom, 303, 304 Lua, 207 bezpieczeństwo, 208 błędy w projekcie, 212 domknięcia, 210, 218 for, 218 funkcje, 210 funkcje pierwszej klasy, 210 funkcje-generatory, 218 generatory, 218 haki dla debugera, 209 liczby, 209 mechanizm odśmiecania, 209 ograniczenia języka, 209 piaskownice, 209 podprogramy asymetryczne, 211 projekt języka, 217 projektowanie programów, 208 przenośność, 222 przystosowanie języka do warunków współczesnych, 217 skrypty, 208 tabele, 210 wersje, 218 wielozadaniowość, 211 współbieżność, 211 zastosowanie, 210 Lua/Wireshark, 221 Lua/WoW, 221 LuaJIT, 220

Ł łatki, 175 łatwość tworzenia prototypów, 49

SKOROWIDZ

569

M M, 455 MAC, 154 Mac OS X, 303 Mac Toolbox, 122 machine learning, 183 Make, 63, 145, 175, 178 Makefile, 63 makra, 54 malloc(), 348 małe języki, 173 Manheimer Ken, 40 map, 46, 255 mapowanie, 387 mapy, 210 maszyna von Neumanna, 261 maszyna wirtualna, 50, 121, 367 bazująca na rejestrach, 223 HotSpot, 348, 356 Java, 351 matematyka, 70, 152, 183, 187, 214, 252, 278, 489, 527 maximal fixed points, 261 McCarthy John, 260 Mcllroyow Doug, 144 mechanizm kontroli wcięć, 193 mechanizm niezależności od sprzętu, 364 mechanizm obsługi dużej liczby obiektów, 167 mechanizm odśmiecania, 16, 56, 209, 324, 348, 357, 368 mechanizm typów dołączonych, 540 mechanizmy zamiast strategii, 221 Meertens Lambert, 52, 60 menedżer, 500 Mercury, 245 metajęzyk, 258, 262 metaprogramowanie, 385 metaprogramy, 203 metoda SCOOP, 520 metodologia CMMI, 398 Objectory, 394 RUP, 400 metody, 241 metody nauczania programowania, 110 metody programowania, 400 metodyka Serum, 396 Meyer Bertrand, 512 Microsoft BASIC, 127 mikrooptymalizacje, 435 Milner Robin, 257, 258, 337 minimalizm metacyklicznych implementacji, 89 minimalizm w zakresie pojęć językowych, 89 ML, 44, 231, 239, 257 analizowanie kodu źródłowego, 259 dowodzenie twierdzeń, 258 host logiki, 259

570

SKOROWIDZ

mechanizm równoczesnego dopasowywania, 273 reguły wnioskowania, 259 specyfikacja języka, 272 strategia, 260 system typów, 270 taktyki, 260 właściwości, 259 zastosowanie, 274 mniej znaczy więcej, 61 model, 266 model bezpieczeństwa, 20 model maszyny, 28 model MSC, 265 model pojedynczej dyspozycji, 16 model systemów mobilnych, 266 modele logiczne, 263 modelowanie, 424 Modula, 366 Modula 2,211 modularność projektu, 319 modularyzacja, 75 moduły, 114, 126 moduły testowe, 165 modyfikacja języka, 40 modyfikacja kodu, 189 monolityczne komponenty, 333 Moore Chuck, 73, 85, 128 Morgan Stanley, 310 move semantics, 29 Mozilla, 61 MSC, 265 Mscorelib, 524 multimedia, 182 MUMPS, 310 My dostarczamy mechanizmów, 221 myślenie grupowe, 105 myślenie o komponentach jak o twierdzeniach, 204 myślenie programistów, 158, 205, 216

N nadmierna specyfikacja rozwiązania, 517 najlepsze pomysły, 229 NaN, 234 narzędzia CAD, 304 narzędzia działające w wierszu polecenia, 143, 182 narzędzia programowania systemowego, 379 nauczanie, 392 debugowanie, 151, 157, 185, 215 informatyka, 129, 213, 248 język programowania, 123, 154 matematyka, 187 programowanie, 71, 110, 235, 366 programowanie funkcyjne, 247

naukow e podejście do projektow ania języka, 194 naukowiec komputerowy, 278 nazwane operacje, 27 nazwy słów, 101 nazwy zmiennych, 127 NetBeans, 360, 362 new, 21 niebieski ekran śmierci, 349 niedostateczna specyfikacja, 517 niezależność danych, 292 niezależność od platformy, 364 NIST, 294 notacja algebraiczna, 72 notacja BNF, 321, 418 notacja Iversona, 66 notacja OMT, 415 notacja postfiksowa, 93 notacja przyrostkowa, 93 notacja węgierska, 342 notacja wrostkowa, 503 nuli, 538, 540 nullptr, 29 numerowanie wierszy, 129

O Oak, 356 OASYS, 329 Oberon, 366 obiecujące projekty, 499 obiektowy język programowania, 307 obiektowy model komponentów, 167 obiekty, 168, 353, 519 obiekty biznesowe, 454 obiekty futurę, 28, 29 obiekty java.net, 333 obiekty o niskiej ziarnistości, 333 obiekty SOA, 327 Object, 15 Object Management Group, 415 Object Oriented, 22 Objective-C, 125, 303, 304, 323 cele projektowe, 308 elegancja, 308 ewolucja języka, 307 inżynieria języka, 304 nawiasy kwadratowe, 308 prostota, 308 sygnalizacja komunikatu, 308 TaskMaster, 330 wielozadaniowość, 327 zalety języka, 304 zasady organizacyjne, 317 Objective-C 2.0, 303, 324

Objectory, 394 objętość danych, 205 objętość kodu źródłowego, 92 obliczenia matematyczne, 70 obliczenia naukowe, 26 obracanie czcionek rastrowych, 493 obsługa błędów, 234 obsługa dużej liczby obiektów, 167 obsługa interfejsu GUI, 23 obsługa liczb całkowitych dowolnej dokładności, 43 obsługa problemów wstecznej zgodności, 253 obsługa skryptów, 208 obsługa strumieni wejścia-wyjścia w sieci rozległej, 28 obsługa urządzeń, 94 obsługa współbieżności, 388, 433, 521 obsługa wyjątków, 352 OCaml, 56, 237 odporność na błędy, 292 odpowiedzialność za problemy bezpieczeństwa, 317 odśmiecanie, 237, 357 ogłoszenie o zatrudnieniu dla programistów, 47 ogólne zarządzanie zasobami, 18 ogólny kod bajtowy, 164 ograniczenia, 453, 459 OLAP, 294 OMG, 415 OMT, 415, 443 OO, 22, 23, 24 OOP, 325, 343 OOPSLA, 251 opakowanie wskaźnika, 358 open source, 22, 339, 340, 373, 395, 506 OpenGL, 346 OpenOffice.org, 105 operacje arytmetyczne, 111 operacje asynchroniczne, 98 operatory, 75, 93 opis funkcji programu, 88 opis stochastyczny, 266 opracowywanie składni, 67 oprogramowanie, 9 czwarta generacja, 86 komercyjne, 340 odziedziczone, 315, 335, 401, 451 open source, 339 przyjazne dla użytkowników, 132 systemowe, 21 optymalizacja, 119, 348 ortogonalność, 44, 291 OSGI, 338 otwarty standard, 507 oznaczenie znaku, 199

SKOROWIDZ

571

p pakiet struktury danych, 434 pamięć, 121 pamięć STM, 237 paradygmat obiektowy, 389 Java, 353 współbieżność, 432 paradygmat wzorzec - działanie, 142 paradygmaty programowania, 269 PARC, 497 parser, 54 góra-dół, 225 parser combinators, 246 parsowanie, 119 patch, 175 patentowanie technologii, 107, 338 Pathologically Eclectic Rubbish Lister, 461 Pattern Movement, 413, 423, 424 PDF, 495, 496 PEP, 40, 41, 42 Perl, 64, 80, 143, 169, 461, 462 CP AN, 461, 475 dopasowywanie wzorców, 473 ewolucja języka, 472, 478 implementacja, 480 kontekst problemu, 467 operacje na odległość, 463 paradygmaty programowania, 468 rozwój języka, 465 społeczność, 477 tworzenie języka, 467 zarządzanie różnorodnością językową, 471 zasady języka, 462 zasięg leksykalny, 471 zasięg zmiennych, 463 zastosowanie, 464 zmienne, 464 Perl 5, 57, 171, 465 Perl 6, 465, 482 perspektywy, 289 pętle, 104 PHP, 64 piaskownice, 209 PIC, 159, 162 pielęgnacja kodu, 48 pisanie dokumentacji, 320 pisanie kompilatorów, 94 pisanie od podstaw, 189 pisanie oprogramowania, 9, 190 PL1, 80 platforma .NET, 368 pliki, 79, 143 pliki tekstowe, 181 plugboard, 385 plug-in, 20 podejmowanie decyzji projektowych, 75

572

SKOROWIDZ

podejście ewolucyjne, 478 podejście matematyczne, 527 podprogramy asymetryczne, 211 podręcznik dla języka programowania, 155 podwójne licencjonowanie, 477 pojedyncza dyspozycja, 16 pojedyncze dziedziczenie, 325 pojęcia, 241, 245 poliglotyzm, 526 polimorfizm, 119, 120, 123, 389 połączenia sieciowe, 69 połączenia z internetem, 350 ponow na implementacja systemu, 316 pop, 85 poprawki, 220 poprawność implementacji, 151 POSIX, 446 postać kanoniczna, 273 postać normalna, 273 postęp w sprzęcie, 75 postrzeganie sprzętu, 95 PostScript, 485 czytelność programów, 495 ewolucja języka, 496 kerning, 494 operatory, 487 projektowanie opisu alfabetu, 494 rozmiar stosu, 491 stos, 490, 491 zalety języka, 502 zastosowanie, 486 PostScript Level II, 496 potoki, 143, 144 potrzeba powstania nowej wersji głównej, 41 powtarzające się problemy, 186 poziom szczegółowości komunikatów o błędach, 195 półcienie, 493 półformalizm, 250 późne wiązanie, 120 praca nad twierdzeniami, 183 praca w grupie, 230 praca zespołowa, 105, 123, 130, 149, 167, 186, 441 pragmatyzm, 51, 468 praktyki programistyczne, 122 prawo beta, 243 prawo eta, 243 prawo Moore’a, 354 prefiksy, 101 Prelude, 255 prezentacja kodu, 34 problem błędu milenijnego, 272 problem Halloween, 290 problem komiwojażera, 355 problem niezależności od platformy, 364

problem oprogramowania odziedziczonego, 401,451 problem przepełnień bufora, 193 problem wstecznej zgodności, 253 problemy w informatyce, 275 problemy z oprogramowaniem odziedziczonym, 335 proces budowania dużych systemów, 167 proces iteracyjny, 453 proces JSR, 42 proces OMG, 416 proces PEP, 40, 42 proces powstawania aplikacji, 49 proces programowania, 400 proces projektowania, 49 proces RUP, 394 proces standaryzacji, 300, 445, 508, 540 proces UP, 404 procesory wielordzeniowe, 237 produktywność, 317 profilowanie pamięci, 235 programista, 47, 115 programowanie, 104, 139 aspektowe, 394 bez efektów ubocznych, 233 ekstremalne, 523 funkcyjne, 46, 184, 210, 231, 235, 237, 247, 256, 269, 328, 389, 468 generyczne, 15, 22, 29 imperatywne, 235 logiczne, 269 obliczenia matematyczne, 70 proceduralne, 45 przez przykład, 201 rozproszone, 26 równoległe, 26 strukturalne, 233, 528 systemy równoległe, 23 7 w parach, 402, 409 w wielu wątkach, 355 w zespołach, 123 współbieżne, 77, 237 zwinne, 523 programowanie obiektowe, 15, 25, 45, 126, 167, 192, 221, 269, 353, 389, 428, 528 wielokrotne wykorzystanie kodu, 429 współbieżność, 24, 25 programowy układ scalony, 329 programy, 66 CGI, 468 na telefony komórkowe, 305 open source, 22 projekt języka, 102, 113, 122, 124, 138, 191, 217, 224, 245, 359, 366, 442 projekt obiektowy, 353 projekt open source, 340, 395, 506 projekt oprogramowania, 38, 102, 122

projektant bibliotek, 367 projektant oprogramowania, 271 projektowanie, 49 algorytmy, 66, 153 API, 61, 371 aplikacje, 100 biblioteki, 144, 371 dziedzinowe, 88 interfejsy, 61 interfejsy API, 178, 363, 383 klasy bazowe, 24 kompilator, 118, 153 kontraktowe, 511, 512, 516, 517 oprogramowanie, 339, 398 programy typu dół-góra, 50 system klas, 239 systemy, 31, 81 systemy wbudowane, 21 projektowanie języka programowania, 31, 38, 73, 86, 160, 166, 269, 363, 364, 366, 371 styl, 162 systemowy języka programowania, 348 Prolog, 268 prostota, 187, 291, 346, 373, 411, 421 prostota kodu, 104 prostota projektu, 321 prostota systemu, 457 protokoły, 325 prototypy, 49, 190 przechwytywanie, 481 przeciwdziałanie problemom z oprogramowaniem, odziedziczonym w oprogramowaniu, 319 przeglądanie kodu w formie łatek, 175 przejrzysty projekt, 203 przekazywanie informacji o typach, 239 przekazywanie sterowania w przód, 118 przekazywanie wiedzy, 394, 402, 412 przekształcanie niewielkiego języka w język bardziej uniwersalny, 173 przełączanie kontekstu, 463 przenoszenie kodu z języka C do C++, 23 przenoszenie środowiska, 122 przenośność języka, 98 przenośność projektu, 89 przepełnienie bufora, 17, 193, 416 przepisywanie programów, 171 przepływ danych, 434 przepływ sterowania, 243 przeproj ektowywanie odziedziczonego oprogramowania, 318 przestrzenie nazw, 79, 101, 324, 347 przetwarzanie danych, 136 przetwarzanie dokumentów, 297 przetwarzanie równoległe, 91, 96, 233 przetwarzanie strumieni wideo i audio, 125 przykłady kodu, 34, 155

SKOROWIDZ

5 73

przypadki ruchu, 393 przypadki stosowania, 393 przypadki testowe, 532 przypadki użycia, 224, 393, 444 przystosowanie języka do warunków współczesnych, 81 pule wątków, 28 Purify, 215 push, 85 PyPy, 54, 55 Python, 25, 37, 44, 64, 163, 193 bezpieczeństwo, 50 bezpośrednie pisanie aplikacji, 49 biblioteki, 38 czas uruchamiania, 56 decyzje projektowe, 41 dynamiczne własności języka, 50 dyspozytor kodu bajtowego, 55 generator kodu, 53 GIL, 58 implementacje, 55 kompilator, 53 liczby, 43 makra, 54 mechanizm odśmiecania, 56 moduły rozszerzenia, 63 obsługa liczb całkowitych dowolnej dokładności, 43 obsługa wersji języka, 63 parser, 54 PEP, 40 pielęgnacja kodu, 48 pisanie kodu, 49 poziomy rozszerzalności, 63 pragmatyzm, 51 proces projektowania, 49 programowanie obiektowe, 45 programowanie proceduralne, 45 prototypy, 49 sprawdzanie bezpieczeństwa kodu, 51 styl, 38 ścisłe formatowanie, 55 ścisłe parsowanie w kodzie źródłowym, 54 wersja główna, 41 wersje, 53 wspieranie wielu paradygmatów, 45 współbieżność, 58 wymagania języka, 46 zbiór reguł, 39 zwięzłość, 48 Python Enhancement Proposal, 40

Q Quick Draw, 489 Quill, 297

574

SKOROWIDZ

R rachunek lambda, 127, 528 rachunek pi, 261 rachunek systemów komunikujących się ze sobą, 260 RAD development, 31 RAII, 18, 33 Rails, 328 Rational Unified Process, 394 ray tracing, 355 read, 182 recursive descent parser, 54 Red Dragon book, 138 reduce, 46 redukowanie, 387 refaktoryzacja, 25, 454 referencje, 16, 17, 359 reguła mniej znaczy więcej, 61 reguły biznesowe, 454, 459 reinżynieria, 318 Reisner Phyllis, 296 rekurencyjny parser zstępujący, 54 relacje, 435 relacje dwukierunkowe, 434 relacyjna baza danych, 80 relacyjny model danych, 287 REM, 122 renderowanie obrazu, 355 repeat until, 175 repozytorium CPAN, 475 Resource Acquisition Is Initialization, 18, 33 rewolucja, 478, 479 RFC, 477 RIAA, 339 RISC, 125, 330 Ritchie Dennis, 14, 146 rozmiar aplikacji, 327, 428 rozmiar danych, 137, 205 rozmiar oprogramowania, 24 rozmiar projektu oprogramowania, 407, 422 rozmiar zespołu, 301 rozmieszczenie danych w pamięci, 16 rozproszone wytwarzanie oprogramowania, 441 rozprowadzanie aplikacji internetowych, 336 rozszerzalność, 291 rozwiązania praktyczne, 59 rozwój języka, 307, 373 równoległy dostęp do współużytkowanych danych, 289 Ruby, 57, 64, 463 Ruby on Rails, 386 ruch Pattern Movement, 423 Rumbaugh James, 391, 411 RUP, 394, 400

s SaaS, 337 SAIL, 325, 513 samodzielne pisanie kodu, 219 sanity check, 176 SAP, 115 SCA, 329, 331 Scala, 244, 245, 356 Schema Chaos, 300 schematy XML, 343 Scheme, 75, 127, 197, 208, 246, 366, 409 SCOOP, 520 scope, 17 screen scraping, 169 Scrum, 396 SDL, 405, 406, 407 Second Life, 441 second system effect, 198 sedno problemu współbieżności, 96 sekcje BUGS, 156 sekwencje, 210 Selectric, 66 self, 51 Selinger Pat, 290 selling top-down, 32 semafory, 521 semantyczna gęstość, 455 semantyka formalna, 249, 492 semantyka przenoszenia, 29 semantyka wartości, 18 separacja problemów, 332 SEQUEL, 285 serializacja strumieni, 529 Service-Oriented Architecture, 330 sesja programowania, 151 setlocal, 223 shift-reduce, 141 Shneiderman Ben, 133 sieci Petriego, 260 sieć, 339 silna typizacja, 241 Simonyi Charles, 454 Simula, 15 Simula 67, 25, 512 single dispatch, 16 skanowanie danych, 137 Sketchpad, 416 sklejanie, 85 składanie, 387 składanie fragmentów kodu z różnych źródeł, 188 składanie funkcji, 144 składniow y język dopasow ywania wzorców, 135 skrypty, 208 skrypty krzyżowe, 193

skrypty powłoki, 175 słabe tabele, 218 słowniki, 128 Smalltalk, 25, 31, 75, 120, 307, 323, 386, 512 Smalltalk-80, 370 smart dust, 100 smoke testing, 220 SMOP, 481 SMP Erlang, 238 SOA, 327, 328, 329, 330, 332, 336, 340, 379, 431 Software as a Service, 337 Software Collections Committee, 451 Software Com ponent Architecture, 331 sort, 175 sort(), 61 sortowanie, 120 SPARK, 533 Spec#, 532 specjalizacja, 342, 426 specyfikacja, 518 społeczność, 474 open source, 32, 373 Tcl/Tk, 41 sposób myślenia programistów, 136, 158 spotkania zespołu, 381 spójność, 291 spójność bazy danych, 289 sprawdzanie bezpieczeństwa kodu, 51 sprawdzanie typów, 92, 241 Spring, 386 sprzedaż góra-dół, 32 sprzęt, 75, 95, 116, 142, 158, 205, 331, 346, 505 SQL, 181, 205, 283 abstrakcja, 292 ACID, 289 ataki wstrzykiwania kodu, 296 decyzje projektowe, 288 ewolucja języka, 292 filtrowanie danych wejściowych, 296 grupowanie, 286 HAVING, 286 hermetyczność, 291 kompletność, 291 logika trójwartościowa, 286 możliwości optymalizacji, 292 niezależność danych, 288 odporność na błędy, 292 optymalizacja, 288, 290 ortogonalność, 291 perspektywy, 289 popularność języka, 293 problem Halloween, 290 projektowanie języka, 284 prostota, 291 przystosowanie do warunków współczesnych, 290

SKOROWIDZ

575

SQL rozszerzalność, 291 równoległy dostęp do współużytkowanych danych, 289 spójność, 291 standard ISO, 287, 294 tabele, 289 transakcje, 289 wartości nuli, 286 wydajność użytkowników, 288 stabilność platformy, 379 Stackless, 55 stan, 231 standard ISO, 69 Standard ML, 257, 262 standardy, 254, 300, 508, 540 standaryzacja, 69, 300, 381, 382, 426, 445, 508 Static Driver Verifier, 233 static_assert, 29 statyczne asercje, 29 statycznie kontrolow ane interfejsy hierarchii klas, 25 Stein Greg, 56 sterowniki urządzeń, 145 STL, 13, 22, 24 STM, 237 stos, 87, 125, 358, 490, 491 C, 358 stosowanie typów, 264 strategia, 260 string, 17, 23 strongly typed, 14 Stroustrup Bjarne, 14 Structured English Query Language, 285 struktura modeli, 275 Struts, 386 styl prezentacji kodu, 34 styl programowania, 156 CPS, 371 styl projektowania języka, 162 styl przekazywania kontynuacji, 371 stymulacja zespołu badawczo-rozwojowego, 301 subroutine-threaded, 90 sukces języka programowania, 212 sukces w pracy, 130 S u n ,335 superdystrybucja, 336, 342, 343 Swing, 346, 347 Swiss-Army Chainsaw, 461 SWL, 298 symulowana współbieżność, 456 synchronizacja implementacji ze specyfikacją, 275 synchronizacja wątków, 356 syndrom drugiego systemu, 198 system CCS, 261

576

SKOROWIDZ

System R, 286, 290 system rozproszony, 389 system SAP, 115 system TBL, 179 system typów, 270 system uczący się, 183 system Unix, 14, 142 system wejścia-wyjścia, 98 system X, 172 system z podziałem czasu, 123 system zupełny w rozumieniu Turinga, 159 systemowy język programowania, 348 systemy wbudowane, 21 systemy wielordzeniowe, 237 szablony, 524 szacowanie czasu napisania programu, 132 szkolenie, 312 szkolenie programistów, 449 sztuczna inteligencja, 280 sztuki wyzwolone, 501

ś ścisła typizacja, 14 ścisłe parsowanie w kodzie źródłowym, 54 ślad wykonywania, 234 środowisko wykonawcze, 50

T tabele, 289 tablica symboli, 119 tablice, 17, 349 tablice ogólnego przeznaczenia, 76 tablice programowe, 385 taktyka zbiorcza, 260 taktyki, 260 TaskMaster, 327, 330 TBL, 179 Tcl, 57, 64, 177, 221 Tcl/Tk, 41, 177 technika ciągłej alokacji, 348 techniki obiektowe, 342, 455 techniki programowania, 20 techniki projektowe, 74 techniki śledzenia promieni, 355 technologia LINQ, 371 technologia WYSIWYG, 129 technologie transformacji, 174 tekst programu, 113 telefony komórkowe, 305 teoria automatów skończonych, 152 teoria złożoności, 346 testow anie, 19, 59, 175, 176, 215, 231, 320, 531 testy czarnej skrzynki, 176

testy dymne, 220 testy jednostkowe, 175 testy poprawności wewnętrznej, 176 testy regresji, 220 testy użyteczności, 296 TeX, 161, 182 thinkos, 60 timesharing, 69 TLB, 98 TOPLAS, 251 Torvalds Linus, 446 TOT, 127 traffic cases, 393 transformacja, 174 transformacje kierowane składnią, 140 trudność programowania, 528 True BASIC, 109, 111, 113, 114 Turbo Pascal, 366, 370 Turing-complete, 159 twierdzenia, 183, 204, 259, 264 twierdzenie o maksymalnych punktach stałych, 261 tworzenie implementacja, 184 język programowania, 38, 160, 190, 443 lepsze oprogramowanie, 338 oprogramowanie, 396 prototypy, 49 type erasure, 368 type safety, 21 typy definiowane przez użytkownika, 18 typy dołączone, 540 typy dynamiczne, 44 typy generyczne, 25, 241, 244, 368, 522, 524 typy hybrydowe, 45 typy polimorficzne, 23 7 typy statyczne, 44 typy wyliczeniowe, 15

U uchwyty, 18 uczenie się, 392 ukrywanie informacji, 521 Ullman Jeff, 147 ułatwienia w posługiwaniu się językami programowania, 115 UML, 362, 391, 403, 407, 438, 519 generow anie kodu im plem entacji, 406, 417, 439 komunikacja, 419 praktyki, 405 projekt języka, 442 przypadki użycia, 393 przystosowanie języka do warunków współczesnych, 415 rozmiar projektu oprogramowania, 407, 422

standaryzacja, 426 uproszczenie języka, 421 zastosowanie, 408, 417, 438 zgodność wsteczna, 425, 445 zmiany w języku, 404 UML 2.0, 404,416, 421 UML 3.0, 439 UNCOL, 268 Unified Modeling Language, 391, 438 Unified Process, 404 Unix, 14, 135, 142, 146, 436 unordered_map, 29 uogólnione listy inicjalizacyjne, 29 uogólnione wyrażenia stałe, 29 UP, 404 upraszczanie interfejsu użytkownika, 61 urządzenia, 94 urządzenia przenośne, 305, 349 usługi sieciowe, 178, 331 usługi SOA, 340, 342 ustawienia domyślne, 325 UTF-8, 199 utrzymanie istniejących programów, 186 uwagi użytkowników, 219 uwzględnianie uwag użytkowników, 219

V Valgrind, 215 van Rossum, Guido, 37 VDM, 210, 406 vector, 23 vector, 15 Vienna Development Method, 406 Visual Basic, 109, 113, 124 VLSI, 95 void, 538 void-safe, 538

w W3C, 252 Wadler Philip, 241 walka z językiem, 51 Wall Larry, 462 W arnock John, 485, 486 warstwy, 423 wartości, 231 wartości domyślne, 111 warunki brzegowe, 74 wątki, 26, 27, 330, 521 działanie w trybie kooperacyjnym, 99 Weinberger Peter, 135, 139, 171, 179 wejście-wyjście, 45 wektory, 18 wersja alfa, 42 wersja beta, 42

SKOROWIDZ

5 77

wersja główna, 41 wersje Pythona, 53 weryfikacja modeli, 276 wewnętrzna poprawność, 176 wiązanie w fazie wykonywania, 120 widoki, 420 wiedza z inżynierii oprogramowania, 408 wiele małych programów działających razem, 182 wielokrotne dziedziczenie, 324 wielokrotne wykorzystanie kodu, 315, 428, 522 EIFFEL, 521 techniki obiektowe, 429 wielopoziomowa integracja, 329 wielowątkowość bazująca na wspólnej pamięci, 356 wielozadaniowość, 211, 327, 456 wiersz polecenia, 143, 168, 182 W iltamuth Scott, 381 W irth Niklaus, 366, 537 wizualny język programowania, 453 własności, 393 Word, 329 w prow adzanie now ych w łasności do języka, 118 wprowadzanie poprawek w języku, 275 write, 182 Write Once, Run Anywhere, 345 wskaźniki, 16, 121, 358, 359, 434, 437, 538 self, 51 wspólne kryteria, 335 współbieżność, 24, 25, 26, 76, 96, 180, 260, 354, 372, 432, 456, 520 APL, 76 C++, 26 dopasowywanie wzorców, 153 EIFFEL, 521 implementacja, 433 Java, 354 kooperatywna współbieżność, 211 Lua, 211 na poziomie danych, 388 Objective-C, 327 obsługa, 521 podejście obiektowe, 327 programowanie obiektowe, 520 projekt języka, 388, 433 Python, 58 wytwarzanie oprogramowania, 456 współdzielenie kodu, 221, 373 współdzielona pamięć, 482 współdzielona pamięć bez wywłaszczania, 211 współpraca podczas wytwarzania oprogramowania, 456 wsteczna zgodność, 253, 368 wtyczki, 20 wybór języka programowania, 192

578

SKOROWIDZ

wyciąganie, 89 wydajność, 349 wydajność algorytmów, 206 wydajność programisty, 131, 191, 321, 376 wydajność sprzętowa, 142 wydajność wysokopoziomowa, 435 wydajny kod niskopoziomowy, 14 wydobywanie danych, 137, 180 wygląd kodu, 89 wykrywanie błędów, 92 wykształcenie, 411 wyłaniające się systemy, 424 wymagania języka, 46 wymazywanie typów, 368 wyrażenia lambda, 29, 244 WYSIWYG, 110, 129 wyszukiwanie problemów, 206 wytwarzanie oprogramowania, 332, 397 wytwarzanie typu brown field, 458 wywołania zwrotne, 20 wywoływanie procedur, 87 w znawianie procesu program ow ania, 60, 95, 151 wzorce projektowe, 423, 457, 459

X X Window, 172 X II, 177 XAML, 367 Xerox PARC, 202, 486 Xlib, 172 XML, 299, 343, 375 XMonad, 232 XQuery, 252, 299, 301, 328 XSLT, 328 XVT, 122

Y yacc, 141, 147, 174, 175, 225 Yahoo! Pipes, 168

z zabezpieczenia, 331, 334 zachowanie zgodności wstecz, 173 zaćmienia w myśleniu, 60 zakres akceptacji języka, 67 zarządzanie efektami ubocznymi, 231 zarządzanie ewolucją, 31 zarządzanie pamięcią, 322, 348 zarządzanie projektem, 149, 315 zarządzanie sieciami w komercyjnych systemach z podziałem czasu, 69 zarządzanie systemem bez widocznej architektury, 402

zarządzanie wzrostem, 534 zarządzanie zasobami bazujące na zasięgach, 17 zarządzanie zespołem projektowym, 380 zasada „blisko sprzętu”, 18 zasada podstawień Liskov, 516 zasięg, 17, 463 leksykalny, 471 zasoby sprzętowe, 158, 205, 216 zatrudnianie programistów, 47 zaufanie, 334 zbiory, 210 Zen of Python, 37, 39, 44, 52 zespoły przekrojowe, 402 zespół badawczo-rozwojowy, 301 zespół języka funkcyjnego, 228 zespół projektowy, 380 zestaw testów regresji, 149 zestaw znaków, 68, 76 zgodność wsteczna, 173, 347, 369, 425, 445, 538 złączenia, 181

złożoność, 428 język, 15 oprogramowanie, 24, 327, 455 zmiany, 425 zmienne, 119 zmienne typu porównawczego, 240 znacznik HIRO, 60 znaczniki HTML, 299 znajomość rozwiązywanych problemów, 156 znaki, 199 zunifikowany język modelowania, 391 zwinność, 319, 396, 411

ż żargon, 101

SKOROWIDZ

579