Maskinspråkens skönhet

Vad finns av mänskligt språk i programmeringsspråken? Språkhistorikern Ola Wikander funderar över dessa konstruerade språk och ser fler paralleller till mänsklig formuleringskonst än man kanske kunde ana.

Att kommunicera med datorer på deras binära modersmål är inte det lättaste. För den som är lagd åt nördig matematikhumor kan det vara värt att citera det gamla skämtet om att det finns 10 slags människor: de som förstår binära siffror och de som inte gör det.

Datorernas modersmål är den så kallade maskinkoden. En intern kod av binära ettor och nollor som datorns processor – dess ”hjärna” – arbetar sig igenom och tolkar som olika kommandon för att göra diverse mycket basala saker. Det kan röra sig om att addera två siffror, skriva ett tal på någon plats i minnet eller hoppa till någon annan plats i listan av kommandon (programmet).

Att det alls kan kallas modersmål beror på att det är språket som de flesta datorer har från födseln, från fabriken.

I dag är det inte många som sitter och skriver datorprogram direkt i binär kod, men i maskinernas ungdom kunde det ofta vara nödvändigt.

Det måste ha varit ett slitgöra utan särskilt mycket rum för språklig kreativitet och finess. Om man till exempel vill säga till en Motorola 68000-processor (som bland annat fanns i de grå-beigea Apple Macintosh-lådor som prytt månget skrivbord i vårt land) att hoppa tillbaka till huvudprogrammet från ett underprogram, fick man ge den det inte helt glasklara meddelandet 0100 1110 0111 0101.

Om man vill göra det lite lättare för sig kan man översätta den binära koden till ett annat talsystem. Oftast rör det sig då om hexadecimal kod, det vill säga ett talsystem baserat på 16 (precis som vårt vanliga är baserat på 10 och det binära på 2; hexadecimala tal är väl vad som skulle uppstå om en art av utomjordingar med sexton fingrar skulle få för sig att syssla med aritmetik). I hexadecimal kod behöver man av naturliga skäl sexton siffror, och eftersom vi bara har tio (0–9) brukar man lägga till siffrorna A (10), B (11), C (12), D (13), E (14) och F (15). Om man översätter meddelandet ovan till hexadecimal form får man fram (den i och för sig fortfarande tämligen svårgenomträngliga) kombinationen 4E75. Men vips är vi ett litet steg närmare begriplig kommunikation mellan människa och maskin.

Den första egentliga kommunikationen mellan människa och maskin uppstår först med de så kallade assemblerspråken. Denna svengelska term betecknar ett uttryckssätt där datorns interna sifferkombinationer ersätts med små symboliska förkortningar, som är bra mycket enklare att läsa för oss människor. Det ovannämnda fallet med kommandot som fick en gammal Macintosh från 1980-talet att hoppa tillbaka från ett underprogram brukar på assemblerspråk återges med bokstavskombinationen RTS, vilket ska utläsas return from subroutine.

Datorernas egna modersmål (oavsett om det uttrycks med siffror eller bokstavskoder) är ju inte precis föremål för Nobelpriset i litteratur. Ett av skälen till detta kan ju vara att maskinspråk har en ganska sned fördelning i sitt ordförråd. Det rör sig nästan bara om verb i imperativ, det vill säga kommandon att göra det ena eller det andra. Substantiv är det sämre med: de kan bestå av adresser till olika ställen i datorns minne eller av namn på register, processorernas inbyggda små minnesceller för smått och gott. Någon större poesi blir det kanske inte av detta.

Men programmeringsspråken utvecklades snabbt bortom stadiet av att ”tala med lärde män på latin och med datorer på maskinkod”. Ganska snart uppstod det som kallas högnivåspråk, det vill säga programmeringsspråk som rör sig på en mycket abstraktare nivå och inte är så tydligt knutna till en enda processor. Dessa språk är alltså inte modersmål för någon dator, utan ett slags raffinerat symbolspråk som hamnar någonstans mitt emellan mänskligt språk och maskin­kod. Dessa högnivåspråk finns det många av: det äldsta heter Fortran, som snart följdes av många flera. När program skrivna på dessa språk ska ut­föras av en dator måste de först översättas till maskinkod av ett program som kallas ”kompilator” eller också simultantolkas till maskinkod av ett program som i logikens namn kallas för ”tolk”.

Det alla dessa språk gör är att uttrycka algoritmer. Ordet algoritm kommer ursprungligen från namnet på den arabiske medeltidsmatematikern Al-Khwarizmi, och det betecknar enkelt uttryckt en stegvis beskriven metod för att utföra en uppgift. Man skulle symboliskt kunna uttrycka en algoritm för att koka och äta potatis på följande sätt (skrivsättet jag använder brukar kallas för pseudokod, ett slags formaliserat mellanting mellan vanligt språk och programmeringsspråk, vilket ger ett exempel på hur formellt man måste tänka rent språkligt när man formulerar sig på programmeringsliknande sätt):

Att koka och äta potatis

Lägg potatis i kastrullen

Sätt på vattenkranen

UPPREPA: Häll vatten i kastrull TILLS kastrullen är full

Stäng av vattenkranen

Ställ kastrullen på kokplattan

Vrid på värmen

Lägg på locket

UPPREPA: värm vattnet TILLS vattnet kokar

Sänk värmen

KOKNING:

Låt potatisen koka

Pröva hårdheten med gaffel

OM hårdheten är ”hård” GÅ TILLBAKA till KOKNING

ANNARS stäng av plattan och häll av vattnet

Lägg potatisen på en tallrik

UPPREPA:

Skär potatisen i bitar

Öppna munnen

För in potatisbitar i munnen med hjälp av gaffel

Tugga

Svälj

TILLS potatisen är slut

SLUT PÅ Att koka och äta potatis.

Kanske inte stor poesi, men ändå ...

Algoritmen är alltså en mycket exakt beskrivning av ett skeende. Den beskriver alla steg som måste gås igenom, allt som måste upprepas tills vissa villkor är uppfyllda.

Så här såg programmeringsspråken ut i grunden under lång tid. De var byggda för att på ett strukturellt sätt uttrycka en algoritm för något som man ville att datorn skulle utföra. Steg för steg beskriver de ett handlingssätt på ett så begripligt och snabbutfört sätt som möjligt, och ligger till sin struktur mycket nära hur datorernas egna maskinspråk fungerar: först det ena, sedan det andra, på en lång och lite monoton lista. Lite som isländska sagor.

Snart blev det mer och mer populärt att organisera sina program i underprogram eller funktioner, som man kunde använda för att göra sitt skrivande mer strukturerat och lätt att återanvända. Det skapades ett antal sådana programmeringsspråk, som var olika väl strukturerade och har upplevt olika grad av popularitet.

Pascal sågs under många år på 1970- och 80-talen som det ”professionella” språket framför andra, en position som snart togs över av C, ett språk som hade en viss ”hackeraura” och framstod som väldigt invecklat och bildat på grund av sina många snirklade hakparenteser, som gjorde koden närmast oläslig för en oinitierad. Längst ner på skalan över ”häftighet” stod Basic, beginners all-purpose symbolic instruction code, som på grund av sin brist på struktur och allmänna stolpighet fick sina användare att framstå som ute. Numera finns det moderna Basic-dialekter som är alldeles utmärkt strukturerade, men stigmat hänger i viss mån kvar.

Efter hand uppstod nya idéer om hur man borde beskriva kommunikationen med datorer, och man skapade sådana varianter som funktionell programmering, som framställer mer eller mindre allt i programmen som funktioner – lite som att ha ett språk som bara består av verb. Den talade språkvärldens motsvarighet till funktionella programspråk skulle kunna vara semitiska språk, som hebreiska och arabiska, som har enorma verbböjningskomplex. De flesta ord i dessa språk kan dessutom härledas ur verb på ett eller annat sätt.

De mest namnkunniga funktionella språken hette saker som Lisp, ML och Haskell, och den extrema hängivenhet som funktionsprogrammeringens adepter visat kan kanske antydas av att det på fullt allvar (nåja nästan i alla fall) komponerats en sång med namnet God wrote in Lisp. En gång i tiden trodde man att Gud talade hebreiska, men se tiderna förändras.

Dessa verbgalna språk fick konkurrens av programmeringsspråk som till sist började uppmärksamma charmen hos vad man skulle kunna kalla datorvärldens substantiv. Resultatet av denna utveckling blev den så kallade objektorienterade programmeringen. Det hela började i Norge, där man skapade språket­ Simula, som till skillnad från många tidigare programmeringsspråk började betrakta ”saker” som det centrala, inte bara händelser. Man började tänka i termer av ”objekt”, abstrakta representationer av företeelser, som tillhörde logiskt definierade ”klasser”. Dessa objekt kunde sedan agera med varandra och äga objekt inom sig själva. I objektorienterade språk kan man beskriva en abstrakt ”byrå” som har flera ”lådor” som i sig kan ha ”papper”. Alla dessa objekt kan sedan ha ”metoder”, olika sätt att agera med varandra och utbyta information. Programmeringsspråken hade äntligen nått fram till att ”substantiv är namn på ting, såsom cykel,­ boll och ring”, och detta gav möjlighet till oanat praktiska sätt att beskriva processer som påminde om sådana ute i verkligheten.

Inflytelserika objektorienterade språk som C++ (uttalas /se-plus-plus/) tog världen med storm  under 1990-talet, och med alla  sina objekt och klasser blev de efter hand rent ut sagt konstnärliga. Språket Java är till och med så kompromisslöst att själva programmet självt är en ”klass”.

Fast det programmeringsspråk som jag finner allra mest estetiskt fascinerande är ändå Forth, ett datortungomål som inte är så populärt i dag men som haft sin storhetstid också det. Speciellt för detta språk är att det använder sig av så kallad polsk notation, ett slags matematiskt ”baklängesspråk” som även används i vissa miniräknare. Vill man lägga ihop 2 och 3 och lägga resultatet i variabeln A skriver man inte A = 2 + 3 som vanligt folk, utan 2 3 + A =. Forth var också känt för att vara exceptionellt kortfattat. Ett litet program (betitlat GCD och skrivet­ av Hans Lunell) som beräknar den största­ gemensamma divisorn mellan två heltal kan i Forth uttryckas med följande­ koncisa orda­lydelse, som beräknar den största gemensamma divisorn mellan två heltal:

: GCD BEGIN SWAP OVER MOD ?DUP 0 = UNTIL ;

Om inte det är poesi så vet jag inte vad som är det.

Publicerad i nummer: 
December 2012