Beszéljünk emberebbül a számítógéppel!

A programok és a nyelv nyelvtana

Karinthy Frigyes szerint az irodalomban Jack London volt az első, aki ahelyett, hogy az állatokat mindenféle emberi tulajdonsággal ruházta volna fel, a fordított irányra helyezte a hangsúlyt, arra, hogy az emberi érzelmekben, szokásokban stb. hogyan ismerhetjük fel az állatvilágból öröklött mintákat. A 20. század nyelvészetének meghatározó vonása volt, hogy a matematika, a logika és a számítógépes programok formális nyelveinek a mintájára próbálta leírni az emberi (természetes) nyelvek rendszereit. Most néhány részben szeretném Jack London módjára megfordítani az irányt, és arról beszélni, hogy a programozási nyelvekben mennyire lehet, és mennyire kellene a természetes nyelv részrendszereiről példát venni. 

Azt hiszem, Donald Knuth mondta egyszer, hogy a programokat nem számítógépeknek írjuk, hanem más embereknek. Az első hallásra furcsa állítás valójában kézenfekvő igazságot mond. A számítógépnek teljesen mindegy, hogy mi milyen programozási nyelven, hogyan fogalmazunk meg, állítunk össze egy programot, ugyanis a mi szövegesen leírt programunkat (hacsak nem közvetlenül gépi utasításokat írunk, de az nagyon ritka eset) úgyis átalakítja majd egy okos program a gép számára „közvetlenül érthető” utasítássorrá. Tehát az ember által írt programokkal szemben nem a számítógép igényei támasztanak követelményeket, hanem az, hogy másvalaki (vagy maga a program írója) később módosítani, fejleszteni, folytatni szeretné a programot, ehhez pedig meg kell értenie, át kell tudnia tekinteni, és az a jó, ha erre nem kell túl nagy időt és energiát pazarolnia. 

A programozó rengeteget tehet azért, hogy emberbarát legyen a programja, kezdve azzal, hogy jól érthető fantázianeveket ad mindennek, amit ő nevez el, azon keresztül, hogy nem ír túl hosszú összefüggő programrészeket (egy-egy egységnek nem szabadna többet elfoglalnia, mint egy képernyőnyi), egészen odáig, hogy bőségesen ír megjegyzéseket a programjának minden részletéhez. Van azonban egy olyan vonása a programozási nyelveknek, amibe állandóan mindenki beleütközik, akinek már egyáltalán volt dolga velük, és amin a programozó nem sokat tud segíteni, mert a létező programozási nyelvek lényegéből fakad. Ez pedig a különböző programrészletek paraméterezése. Mielőtt elmagyaráznám, hogy mit jelent ez pontosan, gyorsan megvilágítom egy rövid példával. 

Grafika: Tóth Róbert Jónás / Qubit

Sok programozási nyelvben van olyan lehetőség, eljárás, aminek a segítségével egy stringnek (karaktersorozatnak) egy részét egy másikkal helyettesíthetjük. Ezt az eljárást alkalmazhatjuk például akkor, ha a facsimile szóban (stringből) a fakszimile szót (stringet) akarjuk előállítani. Az ilyen eljárásnak tehát tipikusan legalább három paramétere van: az egyik meghatározza, hogy mi a kiinduló string (ez itt a facsimile), hogy mit akarunk benne kicserélni (esetünkben a cs stringet, másképpen az elejétől két karakter távolságra kezdődő, és két karakterből álló részt), és hogy mire akarjuk kicserélni (példánkban a ksz stringre). De ha különböző programozási nyelvekben az ennek megfelelő eljárást keressük, nemcsak az lesz idegesítő, hogy az eljárásoknak más-más neve van (ez lenne a kisebbik baj), hanem főleg az, hogy különböző sorrendben kell megadni a paramétereket. Például az Awk nyelvben az első paraméter az, hogy milyen részt akarunk helyettesíteni, a második az, hogy mivel akarjuk helyettesíteni, és a harmadik az, hogy mi a kiinduló string. Ezzel szemben a Python programozási nyelvben a hasonló szerepű eljárásnak az első paramétere a kiinduló string, a második arra utal, hogy mit kell benne helyettesíteni, és a harmadik arra, hogy mivel. 

Szerintem nem csak engem bosszantanak ezek az eltérések. Azt eredményezik, hogy az embernek vagy minden nyelvhez külön meg kell ezeket tanulnia (én képtelen vagyok rá, különösen a ritkábban használt eljárásoknál), vagy pedig folyton a dokumentációt kell lapozgatnia. Nem sokat könnyít a helyzeten, ha a paraméterek szerepét nem a pozíciójuk jelzi (tehát hogy hányadikként soroljuk fel őket), hanem valamilyen szimbolikus névvel kell őket azonosítani, mert akkor meg ezeket a szimbolikus neveket kell megtanulni, hiszen ezek is önkényesen választott, nyelvenként eltérő szavak. 

Érdekes módon az emberi nyelvek megtanulásakor sokkal kevesebb hasonló gondunk van. Vannak hasonló esetek, mégis kevesebb gondot okoznak. Például az angol nyelv megtanulásánál meg kell jegyeznünk, hogy a replace `helyettesít' ige a magyar megfelelőjéhez hasonlóan viselkedik: a tárgya az, amit helyettesítünk, és egy with elöljárós másik bővítmény fejezi ki azt, hogy mivel. Viszont a szintén `helyettesít' értelmű substitute igének a tárgya az, amivel helyettesítünk valamit, és egy for elöljárós bővítmény utal arra, hogy mit helyettesítünk. De a természetes nyelvekben legalább az nem fordul elő, hogy négy-öt bővítmény szerepét csak az jelzi, hogy milyen sorrendben tesszük őket egymás után. Ráadásul a természetes nyelvekben nagyon gyakori a rugalmasság: többféleképpen is jelölhetjük a bővítmények funkcióját. Végül pedig, és ez a legfontosabb, az emberi nyelvek hajlamosak arra, hogy hasonló jelentésű igék hasonló módon jelölt bővítményekkel járjanak, ezért könnyebb az elsajátításuk, mint a számítógépes nyelveké. Például azoknál a magyar igéknél, amik olyan szándékos cselekvést fejeznek ki, ami egy dolog elmozdítására irányul, a cselekvőt mindig az alany, a másik dolgot meg a tárgy fejezi ki. 

A programok ugyanis nem természetesen alakulnak ki, nem egy közösség kollektív és állandóan változó alkotásai, hanem tervezik őket. És a programok tervezői teljesen önkényesen dönthetik el, hogy milyen paramétert hogyan kelljen megadni. Egyes eljárásoknál egész kis külön nyelvet kell használnunk a sokféle paraméter megadásához. Például az olvasó utánanézhet annak, hogy a Linux rendszer sort nevű eljárása (ami szövegsorok sorrendbe rendezésére való) hányféle paramétert enged meg, és azokat hogyan kell jelölni, és összehasonlíthatja, hogy mennyire eltér a paraméterek jelölése attól, ahogy ez más parancsok esetén történik (pl. tessék megnézni a cut vagy a grep nevű eljárás dokumentációját). 

Mint említettem, ennek a problémának a megoldása, ha egyáltalán lehetséges, a programozási nyelvek lényegét érinti, közelebbről azt, hogy másmilyen a „mondattanuk” és a „jelentéstanuk”, mint a természetes nyelveké. Ennek megértéséhez sajnos egy kicsit közelebbről szemügyre kell vennünk őket. 

A paraméterek fogalma

Minden számítógépes program, programrészlet, eljárás, szubrutin, akárminek nevezzük is, a számítógépben lezajló folyamatokra utal. Ezt is sokféleképpen mondhatjuk, például úgy, hogy a számítógép által végrehajtott tevékenységeket ír elő. A lényeg az, hogy a programrészletet előbb-utóbb lefuttatjuk, végrehajtjuk stb. (ezt is sokféleképpen lehet mondani), és akkor valami történik a számítógépben, így a programok jelentését azzal jellemezhetjük, hogy miféle események történhetnek meg a futtatáskor. 

Alig tudunk olyan programról, aminek minden egyes végrehajtásakor, futtatásakor egészen pontosan ugyanaz történik. Lehetnek, vannak is ilyen programok, csak nem túl érdekesek. (Például ilyen programocska az, ami a telefonunk képernyőjét elsötétíti. Valószínűleg egyetlen utasításból áll, ami a telefon hardverjét arra kéri, hogy kapcsolja ki a képernyőt.) Az érdekesebb programok jelentése tehát nem egyetlen lezajló eseménnyel jellemezhető, hanem események egy egész családjával, amik sok mindenben eltérnek egymástól. A program jelentését úgy írhatjuk le, hogy miben közösek ezek az események, és miben mennyire térhetnek el egymástól. Ez komoly bonyodalmat jelent a programok jelentéstana számára. Kicsit hasonló ez ahhoz, hogy például mi mindenre, milyen helyzetekben használjuk a magyar nyelvben a vezet igét. (Például: autót vezet, az erdőbe vezet az út, körbevezet az épületben stb.) Nehéz lenne körülírni, hogy miben közösek az ilyen helyzetek, és miben mennyire térhetnek el egymástól. De ne szaladjunk előre, abban már most biztosak lehetünk, hogy nem lesz nagyon közeli a hasonlóság a természetes nyelv jelentéstanával. 

Azt, hogy egy program egy adott helyzetben hogyan viselkedik, mi történik a futtatásakor, nyilvánvalóan külső tényezők határozzák meg: a felhasználó által megadott vagy máshonnan (például a gép memóriájából, merevlemezről vagy a hálózatról) származó adatok. A program ezekről az adatokról többféleképpen értesülhet, például figyelheti az egér mozgását és a kattintásokat, elolvashatja egy file tartalmát, és így tovább. Akárhogyan is értesül róluk, ezeket az adatokat, amiktől a program viselkedése függ, a program paramétereinek nevezzük. Attól függően, hogy hogyan értesül róluk a program, magában a program szövegében más és más módon jelenhetnek meg a paraméterek, de ha általánosságban akarunk beszélni a programok jelentéstanáról, akkor eltekinthetünk ezektől a különbségektől, beszélhetünk megkülönböztetés nélkül paraméterekről, vagyis külső adatokról, amik a programon belül szimbolikusan úgy jelennek meg, mint a programban nem rögzített, változó értékű szimbólumok. A program olyan eseménysorokat ír le, amik ezeknek a paramétereknek az értékében különböznek, pontosabban csak olyasmiben, ami a programban leírtaknak megfelelően ezektől az értékektől függ, és minden másban közösek. Az egyszerűség kedvéért vehetjük úgy, mintha a program minden paramétere ún. formális paraméter lenne, vagyis olyan adat, amit a program futtatásakor felsorolunk (a megfelelő sorrendben vagy máshogyan jelezve, hogy melyik adat melyik paraméternek felel meg, mint a természetes nyelvi igék mellett a bővítményeiket). 

Mondanom sem kell, ameddig most eljutottunk, az már világossá teszi, hogy amit a természetes nyelvben jelentésnek nevezünk, arról mennyire másképpen gondolkozunk. Gondoljunk csak bele, ha úgy kellene felfognunk, hogy a vezet ige használatai abban térnek el egymástól, hogy bizonyos paramétereknek mi az értékük, akkor képeseknek kéne lennünk megadni, hogy mik is ezek a paraméterek. Sőt, ezeket a paramétereket úgy is meg kellene tudnunk adni, hogy egy-egy bővítménnyel fejezzük ki őket. Eleve nehéz megmondani, hogy mi a közös ezekben a használatokban (valami valamit eljuttat valahova, vagy képes eljuttatni, vagy segít eljutni), még nehezebb a különbségeiket paraméterek formájában megfogalmazni. 

De ha ilyen nagy a különbség a programok és a természetes nyelvi kifejezések jelentéstana között, akkor lehet-e egyáltalán esélyünk arra, hogy „emberibb” nyelven írhassunk programokat? A kilátások nem túl jók, de valamit talán javíthatunk a helyzeten – talán úgy, hogy a programok jelentéséről gondolkodunk másképpen. De erről majd a következő részben.

A szerző nyelvész, az MTA Nyelvtudományi Intézetének főmunkatársa. A Qubit.hu-n megjelent írásai itt olvashatók