Tehnologije

Kronika elektroničke lingvistike

Oton Ribic nedjelja, 19. travnja 2020. u 17:15

Otkud prvotna potreba za programskim jezicima? Kako su se razvijali, i zašto su se razvijali baš tako? Odgovori na ova pitanja, osim neizostavnog tehničkog aspekta, sadrže raznorazne zanimljive priče

Zapitate li tipičnog, televizijama omiljenog “građanina s ulice” zašto postoje programski jezici, pod pretpostavkom da zna što su, vjerojatno će vam slijeganjem ramenima odgovoriti da postoje – kako bi se u njima pisali programi. Jednostavno pitanje, jednostavan (i točan) odgovor. No samo dodajmo malo potpitanje zašto su nastali takvima kakvi jesu, i odgovor se može proširiti na volumen enciklopedije.

Logično, ova priča započinje otprilike s erom modernih računala. Premda u toj ranoj zori informatike jezici u smislu u kojem ih poznajemo danas nisu još postojali, nekoliko velikih umova tog vremena već je razmišljalo desetljećima unaprijed i slutilo kako će se za njima pojaviti potreba. No što ih je navelo na to? Odgovor leži u tome kako su se programi pisali u to, prema računalnim mjerilima, prapovijesno doba, ali ljudski ne nezamislivo davno: sredinom prošlog stoljeća.

Teška zora

Ti prvi procesori programirali su se na toliko rudimentarnoj razini da u suvremenom IT-u praktički nemamo ništa slično tome; danas se natruhe izravno pisanog strojnog kôda još vide u industrijskim procesima, ali i tamo tek povremeno i za specifične stvari. Doslovno direktna manipulacija bitovima u vrlo malim registrima, i logika koja ne ide mnogo dalje od “ako je zadovoljen ovaj jednostavan uvjet, nastavi sa sljedećom instrukcijom, a ako nije, nastavi s onom iza nje”. Uostalom, tko je imao priliku propatiti se uz rad sa strojnim kôdom (ili možda uživati u njemu?), ta razina mu je dobro poznata.

I iako se kod tih praračunala radilo o programima koji poznaju samo nekoliko operacija, i čiji se rad može bez mnogo nevolje polako simulirati ručno na povećem listu papira s kvadratićima, ljudi koji su s njima radili uspjeli su, kombinirajući te najjednostavnije koncepte, postići sve osnovne numeričke operacije. Zbrajanje, množenje, pa zatim i eksponenti, korijeni, logaritmi i slične stvari, ako je bilo dovoljno memorije i vremena, izvedeni su iz najosnovnije manipulacije bitovima, većinom na temelju teorije koju su matematičari bili razvili već ranije.

Treba reći i to da se u ta pionirska vremena od računala i nije očekivalo više: njih se smatralo dobrom metodom uštede ljudskog truda u situacijama gdje treba obaviti veliki broj relativno jednostavnih numeričkih proračuna brže i pouzdanije od ljudi. Sama pomisao na datoteke, periferije ili nedajbože grafičko sučelje, bili su samo tema dalekih vizionara i autora znanstvene fantastike.

Bljesak ideje

U svakom slučaju, nešto kao Mooreov zakon naslućivalo se i tada: računala će postajati sve moćnija, ili drugim riječima, performanse u odnosu na cijenu mogu samo rasti. U svaku će se sekundu moći ugurati sve više operacija, s većim brojevima, i većim setovima podataka – i sve se to vremenom pokazalo točnim. Stoga, potkraj 1940-ih, među tim prainženjerima počelo se formirati mišljenje koje će utemeljiti programske jezike.

Oni su točno naslutili kako će porastom performansi računala, isto tako porasti i njihov praktični potencijal. Zašto bi se ona koristila samo za numeriku, ako u teoriji mogu služiti za još milijun drugih svrha, daleko kompleksnijih i korisnijih? Treba priznati da je to bio neosporno zdrav pristup, ali s jednom važnom preprekom: ako bi se pisao program za nešto iole kompleksnije od numerike, aktualnom razinom programiranja to bi bilo krajnje teško, ako ne i nemoguće.

Naravno, ne teoretski nemoguće, jer se svaki kompleksniji program na kraju može razložiti na osnovne strojne instrukcije. Problem je bio u praksi: pisanje takvog programa u primitivnom strojnom kôdu tražilo bi mentalni kapacitet koji bi u ludnicu otjerao i najdublje umove, a i vjerojatno bi trajalo toliko dugo da bi programerski tim prije bio penzioniran, a tvrtka propala, nego što bi projekt bio gotov.

Rješenje, čiji tvorac – na žalost povjesničara informatike, nije jasno poznat, već se radilo o postupno formiranom mišljenju zajednice – bilo je genijalno: dat ćemo računalima da obave taj posao. Na nama, ljudima, je da napišemo samo nijansu apstraktniji kôd u kojem se ne moramo baviti pojedinim bitovima i tehnikalijama procesora, nego samim problemom koji želimo riješiti. Primjerice, ako želimo pomnožiti dva broja, samo među njih trebamo staviti znak množenja, ne se baviti tehnikalijama množenja na strojnoj razini. Računalo će potom – opet prema programu koji za tu svrhu trebamo napisati – generirati svoje gigantske količine strojnog kôda na temelju našeg. I koncept programskog jezika bio je rođen.

Početak povijesti

Protivnici ovog pristupa argumentirali su da generiranje strojnog kôda na temelju ovog apstraktnog procesa (koji je dobio poseban termin compilation, a program koji ga izvodi compiler), nikad neće biti toliko učinkovito, niti može biti toliko optimizirano koliko je to slučaj ako krajnji kôd pišu ljudi. Bili su u pravu, ali zdravorazumska poanta bila je da je bolje imati funkcionirajući program nakon pola godine rada, nego idealan nakon pola milenija. Uostalom, brojne lukave optimizacije, pokazalo se kasnije, mogle su se ugraditi u compilere.

Postoje razne suparničke teorije, ali većina se ipak slaže da je prvi koji je s tih riječi prešao na djela bio Britanac Alick Edwards Glennie, teoretičar koji je 1952. na sveučilištu u Manchesteru smislio jezik koji je nazvao Autocode i napisao compiler za njega, te ga upogonio na sveučilišnom računalu Manchester Mark 1. Sam termin programskog jezika još je bio stran te je on Autocode nazvao “sustavom pojednostavljenog kodiranja”, ali za sve svrhe i namjere, to je bio prvi legitiman i doista upogonjen programski jezik, i to kompajliranog tipa (inače, bilo je drugih kandidata i ranije, ali samo na papiru – nisu, naime, nikad ugledali svjetlo dana).

Zanimljivo, Glenniejev rad i objava uglavnom su prošli nezapaženo, ne samo u široj, nego čak i njegovoj, manchesterskoj akademskoj zajednici. Doduše, lako je zaboraviti da mi s današnjeg gledišta dobro znamo koliko su programski jezici postali važni – ali u to doba dio ljudi ipak je još sve promatrao kao eksperimentalni projekt bez širokog utjecaja i s primjenjivošću koja se tek treba dokazati.

Korak u mainstream

Razne slične ideje i implementacije kotrljale su se po sveučilišnim institutima još godinu-dvije, ali ozbiljna lavina krenula je 1954. kad je IBM lansirao Fortran. Iako iz današnjeg gledišta prestrog i neelegantan, u to doba radilo se o jeziku koji otvara gigantski novi horizont – a za neke specifične, obično vrlo numerički orijentirane primjene, koristi se i danas. No, veći dio vremena programeri u Fortranu tada nisu više trebali trošiti na mozganje što se zbiva s procesorom, memorijom ili sličnim tehnikalijama, što je značilo novi način razmišljanja, daleko fleksibilniji, a ujedno se radilo o masovno prihvaćenom jeziku, tako da su se pojavili njegova zajednica, literatura, teorija, i slično, koji su ga nastavili gurati dalje.

Iz te povijesne točke krenulo je toliko raznih smjernica da bi njihov opis mogao lako ispuniti nekoliko cijelih Bugova, ali zadržimo fabulu na onom “teoretskom” dijelu početaka.

Još od spomenutih početaka 1952., postojala je ideja da se napisani kôd u programskom jeziku, umjesto prvotnog prevođenja u strojni pomoću compilera, prevodi odmah za vrijeme izvršavanja. Ovo je koncept interpretiranog (za razliku od kompajliranog) jezika, i unatoč svojem očitom nedostatku – sporijem izvršavanju i većim resursima potrebnima za to – interpreteri su našli svoje mjesto pod suncem zahvaljujući svojim drugim prednostima: lakšem otkrivanju i ispravljanju grešaka, manje problema s kompatibilnošću na raznim platformama, brže promjene kôda, i sličnom. Prvi široko popularan interpretirani jezik bio je Lisp, i to 1958. godine, iako je manje poznato da je originalno bio osmišljavan kao kompajlirani jezik. No zapravo granica između njih nije oštra, jer postoje jezici koji funkcioniraju na oba načina.

Apstraktno, apstraktnije

Kako je vrijeme odmicalo, računala su postajala sve moćnija i programi sve kompleksniji, pa se počeo pojavljivati problem sličan prvotnom: čak i razina pojednostavljenja, odnosno apstrakcije, koju su nudili prvi jezici nije bila dovoljna za hvatanje u koštac s još kompleksnijim problemima. To je, pak, izazvalo poboljšanja postojećih jezika, kao i uvođenje novih. To se događalo cijelo vrijeme, i to sve do danas, kao svojevrstan kontinuiran proces malih koraka, bez velikih pojedinačnih zemljotresa koji bi okrenuli svijet programiranja naopako. Vjerojatno najistaknutiji korak u tom razdoblju bilo je uvođenje objektno orijentiranih jezika, tj. još jedne razine apstrakcije kod koje su programeri mogli upravljati na razini objekata, odnosno entiteta koji među sobom mogu biti nezavisni (i time jednostavniji za pojedinačno razmatranje), ali prema potrebi povezani i lako proširivi.

No kad su nastajali sve apstraktniji jezici, ili kako se vole nazivati, jezici sve viših i viših razina, oni nisu u potpunosti nadomještali one nižih; iako danas imamo na raspolaganju izuzetno apstraktne jezike, gdje se jedna kratka naredba “ispod haube” na kraju prevodi u tisuće ili milijune strojnih instrukcija, oni se ne mogu svugdje koristiti, koliko god ideja bila načelno privlačna. Ista ograničenja koja su važila 1950-ih, važe i danas: jezici niže razine, s manje apstrakcije, i dalje proizvode programe manje konačne veličine, koji se brže izvršavaju te su manjih hardverskih zahtjeva.

Stoga izbor jezika danas u praksi ovisi o velikom broju faktora, kako tehničkih, tako i poslovnih, ali česta logika nalaže da se za zadani projekt izabere najapstraktniji jezik koji još uvijek proizvodi dovoljno brz, malen i hardverski nezahtjevan program za ono čemu je u praksi namijenjen. Većina vrhunskih igara, ili barem njihovi ključni dijelovi, i dalje se pišu u jezicima niže razine, jer se tamo maksimalno treba koristiti svaki djelić hardvera. S druge strane, korištenje takvog jezika za pisanje programa za, recimo, izradu glazbenih notnih zapisa, bilo bi nerazumno rasipanje programerskog vremena i energije da bi se dobile performanse tamo gdje ionako nisu toliko važne, jer se u radu s takvim programima ogroman udio vremena samo čeka korisnika da nešto napravi.

Čak i ako odbacimo eksperimentalne projekte, dosad je tako nastalo više tisuća programskih jezika, no drastična većina danas otpada na njih dvadesetak najpopularnijih. Treba reći da to nije nužno i dvadesetak najboljih jezika, jer su neki popularnost stjecali time da su bili nametnuti od velikih igrača, ili standarda, ili, pak, bili na pravom mjestu u pravo vrijeme za neki novi važan trend. Općenito je u programerskoj praksi rijedak luksuz izbora programskog jezika u kojem će se projekt raditi: češće kôd već postoji otprije, i nema druge nego nastaviti raditi u njemu, a i kad se kreće iznova, znanje raspoloživog tima i podrška platformi već sami za sebe napola određuju konačnu odluku.

Konstrukcijska pitanja

Stavivši sad malo postrani početke programskih jezika, logično je zapitati se kako se sami programski jezici razvijaju, bilo “od nule”, bilo nadogradnja postojećih? Radi se o suptilnoj kombinaciji znanosti i umjetnosti, za koju je potrebno mnogo iskustva, razumijevanja trendova, potreba tržišta, ali i odvažnosti donositi neke teške odluke.

Neke su stvari unaprijed jasne. Što se same sintakse tiče, do danas se već formiralo nekoliko standarda koji su se tijekom desetljeća toliko rafinirali i potvrdili u praksi da bi bilo samoubojstvo pokušati ih mijenjati. Promijeniti ključne riječi poput if ili else, ili ne koristiti standardne računske simbole za numeričke operacije, navoditi argumente funkcija u ičemu osim zagrada, i slično, bila bi ludost. Isto tako, nezaobilazan je zahtjev da sintaksa, osim što mora biti logična i intuitivna, mora biti i konzistentna unutar sebe same. To sve zvuči jednostavnije nego što uistinu jest.

Većina glavobolje dolazi od izbora koji se moraju napraviti, a neizostavno tjeraju neke od korisnika. Primjerice, odlučite li se za vrlo jasno čitljivu i opširnu sintaksu gdje nema mjesta dvosmislenosti, vjerojatno ćete otjerati napredne korisnike koji vole pisati jasno, čitko i sažeto. Opet, izađete li u susret baš takvima i napravite sofisticiran sustav operatora, njihovih kombinacija i intrigantnih detalja koji negdje uštede dva znaka, nitko neće imati živaca baviti se stoljetnim učenjem sve te, rekli bismo, kamuflirane kriptografije. Dalje: hoćete li prepustiti korisniku da se bakće s čišćenjem memorije ili će jezik to raditi automatski? Koji god odgovor dali, a jedan morate, dio korisnika je antagoniziran. I takvih pitanja ima vrlo, vrlo mnogo.

Ako se jezik razvija za neku specifičnu svrhu ili barem ima u startu naglasak na nešto, odgovori na ova pitanja su jasniji, ali i dalje traže pametno odvagivanje i svijest da vjerojatno stoga neće biti omiljeni nikome izvan te niše. No radi li se o programskom jeziku opće namjene, nema druge nego pokušati naći dobar kompromis, razumjeti većinu svojih korisnika, razmisliti što je doista važno za njih, ostavlja li se prostor nadogradnjama, itd. No na kraju ni kompromis ne implicira uspjeh, jer prijeti opasnost da jezik, u želji da bude pogodan za sve i svašta, na kraju ispadne nekako prihvatljiv za svakoga, ali ni za koga uistinu dobar.

“Sve” je previše

Čak i kad se u razvoj programskog jezika ide s logikom švicarskog noža, alata koji ima sve i može sve na sve načine, ali nikoga ne sili da sve i koristi, to nije nikakvo jamstvo popularnosti. U praksi se pokazalo pametnijim izabrati jednu logiku, tj. jednu općenitu filozofiju jezika i držati je se. Ili kako se to voli reći, bolje je da programski jezik nudi jedan očiti način kako riješiti zadani problem, nego nekoliko ekvivalentnih. Zgodan primjer toga je danski računalni znanstvenik Bjarne Stroustrup koji je, proširujući već otprije poznati C jezik, razvio C++.

Kad je Stroustrup sa svojim suradnicima tijekom godina razvoja jezika C++ bio u situaciji odlučiti kojim od više mogućih pristupa provesti neko proširenje tog jezika, često je odlučio napraviti ih više ili čak sve paralelno, te prepustiti korisnicima da izaberu koji im najbolje odgovara.

Taj pristup rezultirao je jezikom koji je neosporno moćan, ali je toliko kompleksan, i za korištenje u punom kapacitetu traži toliko specifičnog znanja, da se malo tko doista odlučuje za to. Premda je C++ razmjerno popularan jezik, naročito za pisanje programa gdje su brzina i resursi važni, malo tko koristi sve njegove mogućnosti. Razlog ne leži samo u potrebnom znanju, već danas sve više i u činjenici da je nečiji tuđi C++ kôd napisan tako izuzetno teško i sporo (dakle i skupo) shvatiti, održavati i proširivati. Razvojni timovi koji koriste C++ zato nerijetko među sobom dogovorom ne koriste sve njegove mogućnosti, nego se samovoljno ograniče na podskup njegovih mogućnosti, upravo kako bi se projekt uopće držao pod kontrolom. No, ako jezik ima jedan očit pristup već u početku, takvi problemi su izbjegnuti. Ima nešto duhovito u tome, pa je taj problem neizravno kasnije priznao i sam Stroustrup.

Nova područja, novi jezici

I povrh svega toga, novi programski jezik teško će steći popularnost ako samo zamjenjuje ekvivalentnu ulogu koju već sad ima neki postojeći. Dolazak novih jezika na scenu stoga je češće izazvan velikim promjenama trendova ili dolaskom novih tehnologija koje nose svoje posebnosti, nego samo zato što su se, eto, pojavili. Štoviše, i bolje zamjene postojećih jezika sporo napreduju zbog poslovne tromosti, zdravog razuma programera koji ne žele tri četvrtine svojeg vremena učiti nove jezike, raznih standarda, i sličnog. Ne treba daljnji dokaz tome od činjenice da neke velike organizacije i dalje koriste Cobol, jezik koji je upravo navršio šezdeset godina, iako se u međuvremenu pojavio karneval boljih, logičnijih, učinkovitijih i održivijih jezika.

Zbog svega toga očito je zašto bi predviđanje daljnjeg razvoja programskih jezika, barem na ovoj općenitoj razini, bilo vrlo teško. Apstrakcija će vjerojatno rasti i dalje, dok se ne dosegnu razine “prirodnog razgovora” s računalima, kakve viđamo u znanstveno-fantastičnim filmovima – a možda i dalje od toga. S druge strane, ona je već sada na razini da većina današnjih programera nikad u životu neće imati apsolutno nikakvu realnu potrebu proučavati strojni kôd, stvarno funkcioniranje procesora, njegovih instrukcija, i slično. I to ne treba nužno smatrati lošom stvari, štoviše, pravilo važi i općenito: što god se može automatizirati i prepustiti stroju, neka to stroj i radi, a čovjek neka se bavi nečim pametnijim, što se automatizirati ne može. Još.

Asemblerski jezik je komotniji od izravnog rada sa strojnim kôdom i programeru daje mogućnost kontrole svake pojedine krajnje instrukcije
Asemblerski jezik je komotniji od izravnog rada sa strojnim kôdom i programeru daje mogućnost kontrole svake pojedine krajnje instrukcije

 

Pseudokôd je potpuno lišen tehničkih aspekata rada računala, a služi kao skica algoritma i teoretska osnova za razvoj konkretnog programa za računalo
Pseudokôd je potpuno lišen tehničkih aspekata rada računala, a služi kao skica algoritma i teoretska osnova za razvoj konkretnog programa za računalo