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

  • 0 0 0
  • Gefällt Ihnen dieses papier und der download? Sie können Ihre eigene PDF-Datei in wenigen Minuten kostenlos online veröffentlichen! Anmelden
Datei wird geladen, bitte warten...
Zitiervorschau

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ć t