Friday, June 27. 2008Haskell og DebianTilbage i 2007 var mit nytårsforsæt at lære Haskell. Eftersom jeg skriver om det nu, må det vel antages at det ikke blev opfyldt, hvilket er korrekt. Dog er jeg nu begyndt at rette op på det ved at læse lærebogen Programming in Haskell og løse Euler-problemer. Lærebogen er ganske udemærket, dens eneste brist er at den ikke forudsætter nogen form for forgående programmeringserfaring og således bruger en del sider på at beskrive grundlæggende programmeringskoncepter (og bogen er ikke for tyk til at begynde med). Det lader til at være et generelt problem med mange af de funktionsorienterede sprog: Stort set alle tilgængelige bøger er lærebøger beregnet til helt nye programmører, en situation hvor Common Lisp, der har mesterværker som Art of the Metaobject Protocol, skiller sig noget ud. For at opnå erfaring med brug af Haskell i rigtige projekter er jeg gået over til en ny window manager, xmonad, baseret på den tese at jeg vil blive så irriteret over diverse ubekvemmeligheder at jeg bliver motiveret til at ændre i koden. Der er allerede adskillige ting der irriterer mig (først og fremmest de elendige genvejstaster, et punkt som ratpoison har forvænt mig med). Jeg er også begyndt at overveje at droppe Debian som styresystem til fordel for Ubuntu. Min oprindelige argumentation for at bruge Debian var at det ville være lettere at vedligeholde Debian end at tilpasse Ubuntu til mine til tider usædvanlige præferencer. Efterhånden er jeg begyndt at tvivle på dette, især efter mit system er begyndt at nægte at vise output på både laptop-skærmen og den tilsluttede VGA-skærm på samme tid. Monday, February 19. 2007Comments (0) Trackbacks (0) Min testing-filosofiNu synes jeg efterhånden jeg har nok erfaring med skrivning og vedligeholdelse af test-suiter til middelstore programmer, til at jeg på den vanlige vis kan eksponere mine holdninger og meninger om emnet på denne blog. Unit testing er jeg ikke den store tilhænger af - unit testing er alt for baseret på infleksible retningslinjer, og skaber et alt for stort overhead i forhold til hvad jeg får ud af mine tests. Tag som eksempel - jeg har i den seneste uge skrevet nogen tests der tester diverse funktioner i forbindelse med Lisp-syntax parseren i Climacs. Disse funktioner er afhængige af at et større generelt framework fungerer korrekt, for slet ikke at snakke om at de også tester at selve parseren parser korrekt. Ifølge unit-testing-metodologien skulle jeg teste hver funktion isoleret (i virkeligheden hver enkelt klasse, men det giver ikke mening når objektsystemet, som CLOS, er metode-fikseret og ikke klasse-fikseret), og producere mock-objekter som overholder det offentlige interface, men derudover blot producerer dummy-resultater. Det skulle sikre at hver enkelt test kun tester ét eneste aspekt af den samlede kodebase - en enkelt funktion, i dette tilfælde. Jeg må indrømme at jeg ikke kan se den store idé i dette, og jeg bruger derfor rigtige objekter, klasser og funktioner i mine tests. For mig fungerer test-suiten primært som en regressionstest, og selv hvis vi ser bort fra det massive overhead som det ville kræve at lave mock-objekter for hele den stak af funktionalitet de testede funktioner afhænger af, ville jeg aldrig ende med at opnå mere dækkende tests, end med min nuværende ad-hoc testing. Det betyder måske at en fejl i en funktion kunne komme til udtryk i at en urelateret test fejler, men det er ikke et problem, som jeg ser det. Når man først er klar over at en fejl eksisterer, er det ofte ret nemt at spore, og test-suiten har opfyldt sit primære mål, nemlig at rejse et flag om at programmet som helhed ikke fungerer korrekt. Jeg går meget mindre op i om den enkelte funktion er korrekt, end jeg går op i om hele programmet kan arbejde sammen (og logisk følger førstnævnte af sidstnævnte). Derfor forsøger jeg så vidt muligt at skrive mine tests, så de kræver at så meget som muligt af programmet fungerer korrekt, for at testen er succesfuld. Interaktionen mellem delene i et program, er nok en af de ting som oftest kan forfalde med tiden, og derfor finder jeg det fjollet at bruge ekstra energi på at undgå at teste disse interaktioner i ens test-suite. Sunday, February 11. 2007Comments (0) Trackbacks (0) Optimering i højniveausprogDette blog-indlæg gør mig glad fordi det viser hvordan ydelse kan komme fra de former for inferens en compiler kan udføre omkring højniveausprog, i stedet for at delegere det ud til programmøren at lave mikrooptimeringer. Det viser hvordan vi har en reel chance for at gøre højniveausprog (nok især de statiske funktionsorienterede) bedre ydende i det almene tilfælde end traditionelle super-ydelses-sprog som C. Efterhånden som vores viden om automatisk optimering vokser, vil begrænsninger i hvad sprogene garanterer nok betyde mere og mere for hvor meget der kan optimeres, og det vil ramme især C hårdt, da compileren ikke rigtigt kan regne med noget som helst. Wednesday, January 31. 2007Comment (1) Trackbacks (0) Reformatterings-cirkussetNormalt ville jeg ikke tage mig af hvordan folk (mis)bruger deres computere, men det er lang tid siden jeg har blogget, og jeg kom næsten til omtrent at love at skrive et indlæg hvori jeg på diskret vis kritiserer en unavngivnet person, så here goes. En af de ting jeg erindrer fra da jeg brugte Windows, og en ting som jeg stadigvæk observerer på Internettet, er at mange teknisk vidende brugere ofte reformaterer deres maskine (i virkeligheden, reformatterer (system)filsystemet på deres harddisk(e), men herefter blot refereret til som "formattering af maskinen") og geninstallerer styresystemet. Dette er til tider nødvendigt - Windows er karakteristisk for at kunne ende i en tilstand af næsten total infunktionalitet, hvor det er markant nemmere at rydde systemets tilstand og starte forfra, end at forsøge at uddrive de onde ånder som har besat systemet. Da jeg brugte Windows var 4-5 måneder en rimelig levetid for en installation, og det var nærmest forventet at man benyttede sig af reformattering for at løse tekniske problemer (om folk der vælger denn løsning vitterligt er "teknisk vidende" skal jeg lade gå ubesvaret). Min nuværende styresystemsinstallation, Gentoo GNU/Linux, er fra 1. januar 2004, og har lært mig et og andet omkring glæderne, eller manglen på samme, ved hyppige formatteringer. Et af de mest meningsløse argumenter jeg har hørt som forsvar for hyppig formattering er, at det skulle give en bedre backups. Hvis man konstant formatterer sin maskine ville jeg da ellers mene at man til sidst bliver træt af at tage fuldt dækkende backups, og begynder at sjuske ("det der har jeg ikke ændret på siden sidst!"), eller begynder at smide ting væk som man ikke umiddelbart kan se hvad man skal bruge til (disse ting får man ofte lyst til at se på adskillige år efter, noget jeg selv har oplevet for nyligt). Den idéelle løsning er naturligvis at gemme al ens data på en seperat partition, eller fysisk disk, hvis man er voldsom, og nøjes med at formatere det filsystem som selve styresystemet, og måske programmer, er installeret på. Men det er måske for kompliceret... Et andet argument er at man holder sit system rent ved hyppige formatteringer. Det åbenlyse svar er nej, man holder det ikke rent, man udsletter og genskaber det rent. Det er ikke en løsning, det er en genstart. Computersystemhygiejne er ikke en handling, det er en process, en måde at arbejde med sit system på. Det handler om ikke at køre tilfældige programmer i non-sandboxes, og lade være med at bruge for mange unødvendige programmer (de daemons som Windows-programmer er så begejstrede for at starte tæller som "unødvendige"). En reformattering af hygiejneårsager er en falliterklæring, det er som at brænde Rom af, af renoveringshensyn. Hvad er så de forbudne frugter som venter, hvis man undlader at lade ens system fungere som metafor for fugl Føniks? Det vigtigste er at det pludselig giver mening at bruge meget tid og energi på at tune og customize systemet så det passer til ens egen smag og arbejdsrytme. Jeg har brugt mange timer på at skrive scripts og arrangere opsætning af mit nuværende styresystem (og arbejdsmiljø), noget jeg aldrig ville have gjort hvis det skulle gøres om flere gange om året. De fleste Windows-brugere bruger ikke ret meget tid på at customize deres arbejdsmiljø, måske fordi Windowskulturen bare ikke lægger op til den slags, måske fordi de programmer de bruger ikke er synderligt velegnede til det, eller måske fordi det med så hyppige formatteringer er spild af tid. Det er lidt en skam, for et miljø opsat til ens personlige præferencer og vaner er nu en ret rar ting - det forvandler computeren til en udvidelse af ens bevidsthed, i stedet for blot at være et komplekst værktøj med et begrænset interface. Så, for jeres egen skyld, drop det der reformatteringscirkus, lad jeres system vokse med jer, lad være med at starte forfra hele tiden. Der er heller ingen der køber nye sko, lige når de gamle er ved at være trådt til. Udøv lidt basal computerhygiejne, så bliver det pludseligt meget mere effektivt at undlade at foretage regelmæssige reformatteringer. Tuesday, January 23. 2007Comments (0) Trackbacks (0) Blogindlæg og OOPD-eksamenPå fredag skal jeg til eksamen i Maskinarkitektur, og jeg terper således hårdt i denne uge for at være klar. Indtil videre har mine studier lært mig at slagskibe er alt for svage i Civilization IV, og at Oblivion stadigvæk er et rigtigt sprødt spil. Derudover har jeg læst nogle over-middel interessante blogindlæg, som jeg i mangel af kreativitet vil linke til her:
Siden sidst er min gruppe og jeg også blevet færdige med vores eksamensopgave i Objektorienteret Programmering og Design - opgaven gik ud på at implementere en simulation af en biotop hvori finker interagerer med hinanden. Det var et overraskende interessant projekt, der var velegnet til Java-style OOP (surprise?), selvom jeg i nogle dele af programmet savnede multiple inheritance og mixins. Og i de fleste dele af programmet savnede jeg anonyme funktioner (det der fis med anonyme indre klasser giver mig skrivekrampe). Sunday, January 7. 2007Emacs-paradoksetDer er visse ting, som de fleste programmører er enige om resulterer i bedre software - kommentarer, klare funktioner, minimal tilstandsmutation, unit-tests, indkapsling, osv. Emacs er interessant i den forbindelse at de fleste af disse ting ignoreres, men at det resulterende program alligevel er yderst anvendeligt, og endda let at ændre for folk der ikke er bekendte med kodebasen. Hvilke af disse facetter af traditionel softwareudvikling-best-practice er det helt præcist Emacs ser stort på?
Til trods for disse brud på almindelige konventioner er Emacs et utroligt stabilt og velfungerende program, og at skrive kode, der benytter sig af Emacs' store funktionsbibliotek er ganske behageligt, til trods for manglen på indkapsling og abstraktion. Det er ikke ligetil at foreklare årsagen til dette fænomen, men jeg har en vag mistanke om at det er fordi det skaber en tæt forbindelse imellem manuel tekstredigering af programmering af tekstmanipulationsfunktioner. At alle funktioner, selv de "intern", er direkte tilgængelige for alle Emacs Lisp-programmer, kan kun fungere fordi Emacs fra bunden af blev designet til at understøtte dette - alle funktioner er veldokumenterede, og hvis man vil være helt sikker på at man har forstået dem, er kildekoden kun et enkelt tastetryk væk. Emacs' arkitektur er, set fra et softwareudviklingsynspunkt, ikke synderligt elegant, korrekt eller ren, men man føler sig så herligt hjemme i den - den er super-pragmatisk og behagelig, ligesom man føler sig mere komfortabel i ens mindre-til-voldsomt rodede og beskidte værelse, end man gør i en steriliseret hospitalsgang der skinner af marmor. Tekstredigering er ofte et beskidt og hacky arbejde, og måske skaber Emacs' knapt-så-teoretisk-perfekte arkitektur et passende værktøj til en sådan opgave (det samme fænomen kan observeres i programmeringssproget Perl). Thursday, December 14. 2006Comments (3) Trackbacks (0) Syntaksklasser for programmeringssprogFor mig at se, når jeg vurderer syntaksen for et programmeringssprog, findes der tre primære klasser af syntakse (som er en total uformel klassifikation og ikke har noget med sprogteori at gøre, så lad være med at tro det har):
Når jeg vurderer et sprog opfatter jeg, stort set, alle syntakse inden for hver af de tre klasser for ækvivalent - hvis det er en grammatisk syntaks vil der være operatorpræcedens, der vil være regler for hvordan visse ting bare skal se ud, og jeg vil sandsynligvis ikke være i stand til at lave meningsfyld preprocessing på kodens parse-træ (jeg ved der er undtagelser, jf. Smalltalk der ikke har operatorpræcedensregler). Til gengæld forventer jeg også syntaktiske genveje, såsom C's array-indekserings-syntaks eller Haskell's syntaks for uendelige lister. Hver af disse former for syntakser har fordele og ulemper - personligt hælder jeg mest til de strukturerede syntakse når det kommer til større programmer, simpelthen fordi det er langt simplere at lave intelligente editorer der kan bearbejde udtryk, når det er helt klart hvad et udtryk dækker over. Desuden gør den regulære syntaks det muligt at lave effektiv preprocessing uden alt for meget smerte (C++ tilbyder template metaprocessing, der er en svagere og mere smertefuld implementation i en grammatisk syntaks). Dog ville jeg til et on-and-off scriptsprog til små opgaver nok foretrække et sprog med en grammatisk syntaks propfyldt med genveje - for der er det ikke sandsynligt at jeg vil gå ud over hvad sproget direkte er designet til, og derfor ikke have behov for mange af de faciliteter som en struktureret syntaks tilbyder. Saturday, December 9. 2006Comments (0) Trackbacks (0) Bootstrapping og design af højniveau-sprogI dag læste jeg en interessant historie om hvordan Squeak-miljøet blev til, og hvordan det er designet. Udvikling og vedligeholdelse af disse højniveau-systemer interesserer mig meget, fordi det for mig at se næsten er magi at de overhovedet kan fungere. Smalltalk, der er et meget højniveau-sprog, bruges til at skrive sin egen virtual machine - det er respekt. Generelt synes jeg at sprog, der afhænger af komplekse runtime-miljøer, har nogle fascinerende detaljer omkring porting til nye systemer, og grundlæggende bootstrapping - hvor C på triviel vis omdannes til totalt flad og statisk maskinkode, så kræver det lidt mere at få et Smalltalk-image op og køre. Ydermere kan der opstå interessante komplikationer og detaljer når man laver selv-kompilering med den slags runtime-systemer - CMUCL, den klassiske frie implementation af Common Lisp, er berygtet for at være ekstremt kompliceret at kompilere, og er kendetegnet ved at nye kompileringer af CMUCL arver karakteristika fra den CMUCL det bliver kompileret med - OOP go home, det her er ægte inheritance! En af de mest underholdende konsekvenser af dette, som jeg har læst om, er at man, når man porter CMUCL til en ny platform - det være sig en ny maskine, styresystem eller bare nyt sæt libraries - anbefales at rekompilere CMUCL med sig selv et par gange, bare lige for at få ryddet ordentligt op og få reduceret antallet af compiler-advarsler. Thursday, September 28. 2006Hvorfor er der...... ikke flere sprog der har valgt at navngive deres standardfunktioner efter IBM 704-instruktionssættet? Når man ser på de sprog der følger denne designfilosofi må man da formode at det er en god idé? Det kan da ikke være værre end at bruge glorificeret PDP-11 makroassembly? Sunday, September 3. 2006Comments (0) Trackbacks (0) Sprogfleksibilitet versus værktøjerI dag så jeg denne halvgamle posting på en af Squeak's mailing-lister (Squeak er et Smalltalk-miljø, prøv det, hvis du ikke allerede har - det kan være noget af en øjenåbner og er sjovt at lege med). Beskedens kerne er at Smalltalk's stringente holdning til hvordan et computerprogram skal designes og struktureres, betyder at man kan bruge utroligt avancerede værktøjer til at skrive, og analysere, Smalltalk-kode. Idet programmer altid er organiserede i objekthierarkier, kan værktøjerne gå ud fra nogle grundlæggende formodninger, der gør at de har let ved at gennemskue koden. Det hjælper naturligvis også at Smalltalk-miljøer typisk ikke bruger filer, og at man bare siger at man vil redigere i en given metode for en given klasse, og så får et editor-vindue med kildekoden. Så behøver værktøjerne ikke beskæftige sig med inddeling af koden i filer og den slags pjat. Common Lisp er lidt anderledes - der har man ubegrænset fleksibilitet, selv den grundlæggende syntaks kan ændres, hvis man skulle nære trang til dette. Jeg har selv oplevet konsekvensen af dette i forbindelse med mit arbejde på Climacs - Lisp er (retteligt) berømt for sine muligheder for introspection - at se ind i, og ændre, programmets tilstand, mens det kører, men lige så nemt som det er at analysere kørende kode, ligeså besværligt er det at analysere koden, mens den endnu blot er tekst i et editorvindue. Når syntaksen kan redefineres efter forgodtbefindende, er det ofte umuligt at vide hvordan et givent symbol og bruges, og derfor lave rigtig intelligent symbolfuldføring (alá Microsoft's IntelliSense). Man kan naturligvis lære editoren om hvordan den skal opfatte specifikke makroer/readermakroer, men det er umuligt at gøre det generelt for alle makroer programmøren kunne finde på at definere, med mindre man rent faktisk implementerer en halv compiler i kode-analyse-modulet. Er det så værd at ofre sprogfleksibilitet, for til gengæld at få bedre værktøjer? Det afhænger af situationen - det er nok nødvendigt at have visse begrænsninger i sproget, for at gøre det muligt at have intelligent værktøjer i det hele taget. Men generelt er jeg af den oplevelse at værktøjer altid kan skabes, ændres og forbedres ("hvis en compiler kan gøre det, så kan et eksternt redskab også"), mens man som oftest er låst fast på hvad sproget kan, så derfor er det vigtigst at sørge for at sproget er fleksibelt og kraftfuldt til at begynde med. Friday, August 11. 2006Comment (1) Trackbacks (0) Denne side bør I allerede kendeFor en god ordens skyld - hvis I ikke allerede kender til thedailywtf.com, så gør I det nu. Det er en side hvor der bliver postet eksempler på hvorfor det ikke kræver ret meget kompetence at få et job i computerindustrien, og hvor grimt man rent faktisk kan skrive Visual Basic-kode. Og så har jeg desuden også fundet nogle projekter der kan holde mig beskæftiget resten af ferien. Det er jo godt nok. Edit: Indlæg version 2.0, link fikset.
Friday, June 16. 2006Eksempel på CLIMFor et stykke tid siden skrev jeg om CLIM og var især begejstret for presentation types. Det er jeg stadigvæk, og for at konkretisere hvorfor jeg synes de er så geniale, vil jeg nu komme med et eksempel på hvorledes jeg har brugt dem i løbet af de sidste uger. Jeg arbejder i min fritid på Climacs - en næstegenerations Emacs skrevet i Common Lisp. Derudover arbejder jeg også på CLIM-desktop - integration mellem en række forskellige CLIM-programmer. I CLIM-desktop har jeg skrevet kode der udnytter presentation types på en ret interessant måde - en standard presentation type er symbol, som bliver brugt når et program ønsker at vise brugeren et almindeligt Lisp-symbol. Ved at definere en presentation command translator har jeg kædet alle presentations af denne typer sammen med en kommando, der får Climacs til at redigere definitionen af symbolet. Resultatet af dette er at når et program producerer output, kan det blot sige at et givent stykke output repræsenterer et givent symbol, hvorefter brugeren til hver en tid kan holde ALT inde, klikke på symbolet, og blive taget til den fil, og position i filen, hvor symbolets funktion, klasse, type, osv., bliver defineret. Dette sker fuldstændigt uden at applikationsprogrammøren skal tænke nærmere over det, han kan blot gå ud fra at miljøet giver brugeren nogle relevante interaktionsmuligheder for en given presentation type (selvom programmet selvfølgelig også kan definere sine egne). På samme måde er andre presentation types også kædet til kommandoer - filnavne giver mulighed for at åbne filen, klassenavne mulighed for at redigere i filen, og kommandoer og kommandonavne mulighed for at redigere kommandoens definition. Det er ret komfortabelt at bruge et miljø hvor man, hvis man pludselig opdager en bug i en kommando, som regel bare kan holde ALT inde og trykke på kommandoen, og fikse den. Og denne funktionalitet kommer fuldstændigt gratis for applikationsprogrammøren, hvor det i de fleste toolkits ville være ganske besværligt at implementere. Monday, May 15. 2006Comments (0) Trackbacks (0) Go with the control-flow!I dag er fælleshadet rettet imod en ellers typisk blogpost med titlen Signs You're a Crappy Programmer (and don't know it). Jeg forstår inderligt ikke hvorfor den har fået så meget opmærksomhed, så mange kommentarer og en så prominent placering på had-vejviserne Digg og Reddit. Nuvel, hvem er jeg at skulle gå imod blogosfærens strømninger (wow, det ord får mig til at føle mig helt metroseksuel og Web 2.0-sej - betyder det at jeg kan få en bog udgivet hos O'Reilly?): Jeg må omgående ytre min mening om indlægget fra min egen piedestal. De fleste af indlæggets punkter er ded sædvanlige, korrekte, men efterhånden noget almindelige, kritik af begrebet enterprise ready og den sædvanlige parodi af enterprise programmøren. Der er dog to punkter som jeg synes er interessante, og som jeg tilfældigvis går meget op i, og det er de to punkter der omhandler længden af funktioner og antallet af exit points i en funktion. Jeg er tilhænger af funktionsorienteret programmering, og opfatter derfor funktioner som små, isolerede bidder, der udfører beregninger eller handlinger. Det betyder også at jeg opfatter funktioner fra et matematisk standpunkt, og forsøger at minimere, eller eliminere, side-effekter og funktioners afhængighed af miljøet. Jo større funktioner er, desto sværre er det at undgå side-effekter, derfor foretrækker jeg store mængder små funktioner, der hver løser en enkelt opgave, og som idéelt set ikke har sideeffekter, samt en større interface-funktion, der organiserer kaldene til beregningsfunktionerne og udfører nødvendige side-effekter. De små side-effekt-frie funktioner tillader mig også at bruge matematisk logik til at ræsonnere omkring dem - f.eks. verificere deres korrekthed eller ændre på hvordan de bliver kaldt. Kernen i denne organisering af funktioner er at det som oftest er lettere at fejlrette, eller verificere, 3 funktioner á 10 linjer, end én funktion på 25 linjer. Derfor er jeg som udgangspunkt også imod funktioner med mange exit points - eller helt præcist, jeg er imod funktioner med mange eksplicitte exit points. I funktionsorienterede sprog har en funktion, eller et udtryk, blot den returværdi, som sidste udtryk i funktionen har - returværdien, og exit pointet, bliver altså automatisk udledt af hvornår funktionen simpelthen ikke har mere kode at køre. Det er for mig den optimale løsning, og samtidigt en gylden regel man kan følge stort set hvor som helst: Lad control-flowet fremgå af logikken. Hvis man har behov for tidlig exit, så bør logikken formes så denne tidlige exit er en del af den, ikke ved at lave vilkårlige exits hid og did. Sunday, April 30. 2006Climacs pr0n og jubilæumFor præcist et år siden gik jeg i gang med at lære Common Lisp, det sprog, der sidenhed er blevet mit absolutte favoritsprog (og som tilmed var med til at introducere mig til rigtig datalogi). Jeg har i løbet af året arbejdet på en række mindre ting - mindre, fordi jeg ikke har været tilstrækkeligt familiær med sproget og dets redskaber til at overkomme større opgaver, men i løbet af de seneste måneder har jeg alligevel været involveret i et større projekt: Climacs - en moderne implementation af en Emacs-editor i Common Lisp. Kombineret med CLIM-Desktop er Climacs allerede et udemærket udviklingsmiljø til Common Lisp - og det er allerede begyndt at se ganske fancy ud (Climacs pr0n!). Monday, April 17. 2006WebprogrammeringJeg har før skrevet om min datalogiundervisning på gymnasiet, hvor jeg har beskæftiget mig med så fantastiske teknologier som VBA og VB.NET. Nu er vi startet på webprogrammering, og min lærer har i et svagt øjeblik ladet os bruge et hvilket som helst serverside-sprog som vi måtte ønske. Jeg har aldrig brudt mig synderligt om webprogrammering - behovet for at kæmpe med diverse browseres fejlfortolkninger og det generelt meget primitive interface har genereret mig en del. Jeg håber at dette bliver et mindre problem hvis jeg finder et tilstrækkeligt interessant webframework - jeg har kigget på Ruby on Rails, men var ikke synderligt imponeret. Det lignede mest af alt en autogenerator til databaseinterfaces... ret uinteressant. Og så er det i Ruby. I stedet kigger jeg på UnCommon Web, der også har den fordel at der er utroligt lidt dokumentation, så det kan blive et helt eventyr at lave noget i det. Måske kan det give noget så dødssygt som webprogrammering en smule spænding. Edit: Holy shit, UnCommon Web er fantastisk fedt. Tuesday, April 11. 2006Derfor Common LispFor snart et år siden begyndte jeg at lære mig selv Common Lisp. Jeg havde tidligere arbejdet med Emacs Lisp i forbindelse med udvidelse af GNU Emacs-editoren, men det var ikke det helt store. Jeg var i stand til at læse Lisp-kode, men jeg havde ikke forståelse for Common Lisp's kultur, teknologi, features og idiomer. Common Lisp blev meget hurtigt mit yndlings-programmeringssprog til næsten alle opgaver og har markant ændret hvordan jeg tænker på programmering, programmer, datalogi og computere i det hele taget. I dette indlæg vil jeg forsøge at beskrive hvorfor Lisp er vigtigt, hvorfor Lisp er sejt, hvorfor netop du bør lære Lisp og hvor man skal starte. Overordnet set findes der ikke noget sprog ved navn Lisp. Lisp er en betegnelse for en familie af højniveausprog, der alle arver features og designidéer fra det oprindelige Lisp-sprog, LISP, der blev skabt omkring 1960. I dag er der tre større Lisp-sprog (dialekter):
Jeg foretrækker selv Common Lisp, men har også respekt for Scheme, og bruger det nu og da til mindre programmer, hvor simplicitet er i højsædet. Common Lisp er måske ikke lige så elegant som Scheme, men der er langt mere support og, i mine øjne, nogle mere kraftfulde standard-features, der gør Common Lisp stort set ubegrænset kraftfuldt - det kan praktisk taget emulere ethvert andet sprog uden større problemer og uden at blive til en Turing tar-pit (mere om det senere). Hvad gør Lisp-det-almene-koncept så fantastisk? En del af det er at Lisp er gearet imod funktionsorienteret programmering - en programmeringsstilart, hvor man, i modsætning til imperativ programmering, ikke siger hvordan noget skal udregnes, men hvad der skal udregnes. En del af dette er at undgå side-effekter, altså at variable skifter værdi. En af grundstenene indenfor funktionsorienteret programmering er at en funktion altid skal returnere samme værdi hver gang den kaldes med et unikt sæt elementer. Det betyder at udregningen af funktionens returværdi ikke må afhænge af "globale variabler" eller lignende, da disse kan ændre sig fra kald til kald. Det er naturligvis langt fra alle funktioner der kan skrives således, og det kræver tid at vænne sig til det, men når først man begynder at tænke funktionsorienteret bliver ens kode ofte lettere at forstå og med en markant nedsat mængde bugs. Jo mindre data ændrer sig, desto mere forudsigelig er programkørslen, desto mindre potentiale er der for fejl. Funktionsorienteret programmering er ikke unikt for Lisp, og der findes andre sprog der tager konceptet langt længere end Lisp. En af Lisp's andre features er dog mere eller mindre unik for sproget - at kode og data er så tæt forbundet. Det første indtryk ikke-Lisp-programmøren får, når vedkommende ser et Lisp-program, er den massive mængde parenteser - programmet ser fuldstændigt ulæseligt ud, og læseren får den idé at Lisp-programmører bruger halvdelen af deres tid på at tælle parenteser (dette passer ikke). Parenteserne er ofte blevet fremført som en brist i Lisp's design, men de er måske den absolut vigtigste features - parenteserne beskriver nemlig enkelt-lænkede lister, Lisp's grundlæggende datastruktur. Det betyder at kode kan indlæses som enhver liste, at kode kan ændres og analyseres som enhver liste, af enhver Lisp-programmør. Ethvert gyldigt Lisp-program kan indlæses som en datastruktur eller et objekt af en af Lisp's almindelige I/O-funktioner. I andre sprog ville dette involvere konstruktionen af en kompliceret parser, til at fortolke den måske tvetydige og ofte meget komplekse syntaks, og derefter uhyre forsigtighed fra programmørens side, for at bevare den syntaktiske korrekthed. Betyder dette noget i praksis, giver det nogle reelle fordele? Ja, det betyder at Lisp-programmører har lige så meget kontrol over sproget, som kun compiler-konstruktører har over andre sprog. Og det gør det naturligvis også afsindigt let at skrive værktøjer der foretager analyse af Lisp-kode. Med et enkelt kald til funktionen read kan man læse et komplet parse-træ af et Lisp-program ind i hukommelsen - det vil jeg gerne se gjort for C i under 5000 linjer. I princippet kan man sige, at hvor andre sprog har en kompliceret syntaks, der så af compileren konverteres til et abstrakt syntaks-træ, så skriver man Lisp-kode direkte i det abstrakte syntakstræk, og kan derfor lettere se, og ændre, hvad compileren bliver fodret med. Lisp's simple syntaks betyder naturligvis også at det er let at parse i andre sprog, f.eks. vist i Lisp500, en implementation af Lisp i 500 linjer ANSI C. Den nemhed, hvormed man kan manipulere Lisp-kode, udnyttes primært til at lave runtime-generering af kode ("programmer, der skriver programmer" eller "programmer, der omskriver sig selv, mens de kører") eller til at implementere makroer. Den simple syntaks muliggør også teksteditorer, som f.eks. Emacs, der er i stand til at manipulere med programmets logiske struktur, i stedet for dets tegn. En tredje af Lisp's fordele er at det er garbage-collected, men det er nok et mindre vindende argument i dag, end det var for 30 år siden.
Når Common Lisp skal promoveres er en af de oftest nævnte
fordele makroer, så hvorfor bryde traditionen? Desuden er
det suverænt en af de sejeste features i noget programmeringssprog
jeg endnu har set. Nogle kender måske makroer fra C, og forstår ikke
hvorfor Common Lisp-programmører er så begejstrede for dem. Det er
forståeligt - C's preprocessor-baserede makroer er primitive og
forårsager let fejl. Common Lisp's er i en helt anden klasse, og
drager nytte af at Lisp-kode bare er lister. Et Lisp-makro kan
forstås som en funktion, der kaldes ved compile-time, i stedet for
ved runtime. Når Lisp-compileren gennemgår et stykke
kode, ekspanderer den først alle makroer - det sker ved at
makrofunktionen kaldes med de makro-parametre programmøren har
angivet i sin kode, hvorefter makrofunktionens returværdi indsættes
som kode i stedet for makroen. Makroer giver derved mulighed for at
manipulere og ændre koden efter den er blevet læst af parseren og
før den bliver omdannet til maskinkode af
compileren. Makrofunktionen får sine parametre som Lisp-objekter
(f.eks. lister) og kan ligeså returnere kode som en ganske
almindelig liste, så at skrive en makro er næsten lige så let som at
skrive en funktion - der er nogle få detaljer vedrørende det faktum
at makroekspansion foregår ved compile-time, som man bør være
opmærksom på, men grundlæggende er det at skrive en makro og en
funktion to ret ens processer i Lisp. Makroer giver enhver
programmør mulighed for at ændre sproget præcist som vedkommende
lyster, næsten uden grænse. Det er muligt at skrive en makro, der
som parameter tager {C++, C#, VB, Haskell, SML, Brainfuck,
FORTRAN}-kode og genererer Lisp-kode til compileren, hvis man skulle
have lyster i den retning. Common Lisp-programmører bruger ofte
makroer til at udvide sproget, lave syntaktisk sukker og
implementere domæne-specifikke sprog. Nu og da kan en del af et
Lisp-program udtrykkes klarere i et sprog designet til opgaven, og
via makroer kan man implementere et sådan under-sprog direkte i
Lisp. Makroer bruges ofte til mindre syntaktisk detaljer, der
isoleret set virker lidt banale, men som sammenlagt kan øge
fornøjelsen ved at skrive programmer. Tag f.eks. den klassiske
design pattern singleton - hvis man ønsker at lave en sådan
i C++ involverer det som regel enten at skrive en pæn bid kode og
nedarve fra en anden klasse. Hvis C++ havde makroer som Lisp, ville
man blot kunne udvide sproget til at tillade en syntaks
som
(loop for odd in (list 1 3 5 7 9)
for even in (list 2 4 6 7 8)
collecting odd
collecting even
maximizing odd into max
maximizing even into max)
Dette simple loop itererer over to lister (samtidigt), samler den
til en fælles liste og indsætter den største værdi i
variablen max. Alt hvad makroer kræver er at deres
parametre opfylder grundlæggende Lisp-syntaks-regler, f.eks. et
balanceret antal parenteser, pga. parseren. Man kan dog godt omgå
dette, hvis man er virkelig ivrig, ved at definere
en reader-makro, der ændrer på parserens opførsel, og
tillader en at tage total kontrol over den stream som parsingen
foregår fra. Dette bruges typisk til at implementere nye literal
types, f.eks. kan man få parseren til at omdanne enhver instans af
Lisp-miljøer/runtimes er programmer ganske ulig hvad man normalt ser i dag, og vil være fremmede for de fleste nye Lisp-programmører, ganske som de var det for mig engang (Emacs-brugere vil dog kunne genkende en del af dem). På sin vis minder Lisp-runtimes meget om .NET-runtimes og JRE's - et runtime-miljø kører forskellige programmer og man kan via diverse protokoller snakke med miljøet. I Lisp tager dette som regel form af en REPL - et Read-Eval-Print-Loop. Det er en simpel kommandoprompt, hvor man kan indtaste et Lisp-udtryk, få det evalueret og få udskrevet returværdierne. Ved denne prompt har man som bruger fuld adgang til Lisp-miljøet, inklusive compileren, på samme måde som alle programmer har det ved runtime. På ethvert tidspunkt kan Lisp-miljøet blive bedt om at kompilere et udtryk, en funktion eller ændre værdien af data forskellige steder i systemet. Det er en interaktivitet man hurtigt lærer at værdsætte, hvis man, f.eks. som C-programmør, er vant til at ens miljø sætter store grænser op mellem write-time, compile-time og run-time for ens program. I Lisp er miljøet altid til rådighed og kan altid evaluere ethvert Lisp-udtryk man måtte ønske. Miljøet compiler alle funktionsdefinitioner, man som programmør eller program indlæser, til maskinkode, og der er således ikke et større performancetab end i .NET og Java, og da Lisp er let at optimere, og compilerne desuden har mere end 20 års optimeringsteknologi bag sig, er Lisp nu og da overraskende hurtigt. Dette super-interaktive miljø har været en stor fordel for mig i de seneste par dage, hvor jeg har arbejdet på tekst-editoren Climacs. Mit pt. foretrukne udviklingsmiljø til Common Lisp er GNU Emacs med SLIME, der holder en åben socket-baseret forbindelse til Lisp-miljøet, og derved, med et enkelt tastetryk, kan sende en opdateret funktionsdefinition til Lisp-miljøet, der så vil kompilere og installere funktionen i systemet. Dette udnyttede jeg, da jeg debuggede en række Lisp-funktioner i Climacs - jeg ændrede funktionen i Emacs, skiftede over til Climacs-vinduet, testede den, og vendte tilbage til Emacs for at arbejde videre. At skifte over til Climacs og teste min funktion tog mig vel i gennemsnit under 3 sekunder, da jeg jo ikke behøvede at genstarte programmet - Lisp har ingen problemer med at ændre programmer, der allerede er indlæst - og jeg behøvede heller ikke genindtaste den test-data jeg brugte til at afprøve min kode. I andre sprog ville jeg have været nødt til at genstarte det program jeg testede hver gang jeg ændrede en smule på en funktion, men ikke i Lisp - der kan jeg omskrive mine programmer mens de kører. Den eneste begrænsning er at en ændring af en eksisterende funktion kun vil have effekt på nye funktionskald, hvis programmets control-flow allerede befinder sig i funktionen, vil det ikke blive påvirket før næste gang det træder ind i funktionen. Dette påvirker naturligvis ens udviklingsrytme i massiv grad, og man bliver langt mere villig til at eksperimentere, fordi det går så hurtigt med at få feedback på ens forsøg. Der er naturligvis ingen rose uden torne, og Common Lisp har også sine fejl, hvor den primære er manglen på libraries. Hvis man er vant til den massive mængde libraries der findes i forbindelse med Java, .NET, C eller C++, så kan man let blive skuffet over det noget mere magre udvalg indenfor Common Lisp. De libraries der findes er dog ofte ret gode, og da der igennem årtier konsistent er blevet produceret store programmer i Common Lisp, må det jo være muligt at klare sig. Common Lisp er et sprog i vækst, og selv i det lille år hvor jeg har observeret sproget, er der sket store landvindinger i library-udbuddet og implementations-kvaliteten, så det er så absolut ikke et døende sprog man i givet fald tilslutter sig. Selvom Common Lisp nok aldrig bliver rigtigt mainstream, så har det altid været, og vil nok altid være, et særdeles velegnet sprog til at løse svære opgaver og give ansvarlige programmører ekstremt kraftfulde værktøjer til det. Alt i alt kan jeg kun anbefale nysgerrige programmører at lære Common Lisp - det er en for det første en øjenåbner og for det andet et praktisk værktøj. I disse tider er det rimeligt nemt at installere et frit Common Lisp-miljø og finde den nødvendige dokumentation til at komme i gang, men det skal dog siges med det samme at den optimale oplevelse får man på et GNU/Linux-system (muligvis også OS X). Den letteste måde at komme i gang på, og den jeg også selv fulgte for omkring et år siden, er at læse den gratis tilgængelige bog Practical Common Lisp, der også indeholder anvisninger på at opsætte et udviklingsmiljø (også under Windows). Derefter kan man gå i gang med at læse Planet Lisp og besøge #lisp på Freenode i al den fritid man naturligvis opnår grundet produktivitetsforøgelsen ved at skifte til Lisp. Læs evt. også min Common Lisp-side. Tuesday, April 4. 2006Interaktiv programmeringJeg har efterhånden gjort det i et godt stykke tid, men nu og da stopper jeg alligevel op og tænker - hvor er interaktiv programmering bare fedt! Konceptet med at kunne omskrive et program mens det kører, og evaluere ethvert programudtryk interaktivt, ved en prompt, uden rekompilering eller andet skidt, er en helt fantastisk oplevelse sammenlignet med traditionelle edit-compile-run-loops. Hvor man normalt ændrer nogle ting, rekompilerer, tester og fikser det, der ikke virker, føler man sig når man udfører interaktiv programmering mere som en kunstner, der former en klat ler eller en klump marmor. Man får utroligt hurtigt svar på sine formodninger og man kan på ekstremt kort tid nå at gennemgå mange forskellige iterationer af en given implementation af noget funktionalitet. Det lynhurtige feedback-loop inviterer en til at udføre eksperimenter, og min erfaring er derfor at man har lettere ved at opdage både optimale løsninger på problemer, samt uforudsete konsekvenser af ens ændringer. Interaktiv programmering fjerner mange af de kedelige elementer, og lader en koncentrere sig om det essentielle og det underholdende - at få skrevet noget kode. Og da jeg samtidigt arbejder på en editor/IDE er fornøjelsen naturligvis endnu større - at hacke på et program i selve programmet er så sejt at det næsten er surrealistisk. Saturday, April 1. 2006Comments (10) Trackbacks (0) Jeg er mand for at indrømme når jeg har taget fejlEfter lidt selvransagelse, en del overvejelser og lidt arbejde har jeg indset, at jeg måske har taget fejl mht. programmeringssprog. De seneste dage har jeg hacket på Climacs i et forsøg på at rette nogle fejl og udbygge Lisp-IDE-funktionaliteten. Gang på gang er jeg blevet frustreret over det fuldstændigt absurde abstraktionsniveau - Lisp tillader brug af makroer, closures og sofistikerede objektsystemer til at lave komplekse abstraktionsplaner, men problemet er bare, at det hele bliver så abstrakt, at det er fuldstændigt umuligt at se hvad det er. Det er umuligt at se hvad der foregår, hvordan det virker, hvor hurtigt det vil fungere eller hvordan control-flowet overhovedet vil løbe! Det er som GOTO, men hvor labels først bliver beregnet ved run-time. Det gør det fuldstændigt umuligt at følge programmets flow og komplicerer debugging ekstremt - mange gange har jeg været nødt til simpelthen at indsætte eksplicitte kald til inspectorer og print-funktioner, bare for at få en svag idé om hvad der foregår. Det siger sig selv at det ikke lykkedes for mig. Makroerne gør det svært at læse koden, men den sande ondskab ligger i de generiske multimethods og den gennemgående brug af multiple inheritance - det gør det helt umuligt at se hvad et kald til en generisk funktion vil afvikle af kode. Jeg er ikke meget for at indrømme det, men disse oplevelser står i skarp kontrast til den oplevelse det var at udvikle Deathcubes - i Microsoft's VB.NET, som jeg ellers så tit har tilsvinet. I Deathcubes var det altid simpelt at forstå koden, det var altid tydeligt, hvordan den ville opføre sig, og den statisk typede, objektorienterede fremgangsmåde betød at jeg kun skulle være opmærksom på enkelte, veldefinerede abstraktionsinterfaces. Ydermere var der grænser for hvor fremmed jeg kunne gøre koden - lige meget hvor elegant, eller krøllet, min kode endte op med at være, var det altid stadigvæk VB.NET - opbygget af nogle ganske simple, grundlæggende syntaktiske konstruktioner med veldefineret semantik - ingen uforudsigelige, potentielt buggy, programmør-specificerede sprogudvidelser. Hvorfor skulle det overhovedet være nødvendigt at kunne udvide programmeringssproget på den måde? En god programmør er vel i stand til at læse den simple repræsentation af programmet i form at dets kildekode, og danne sig en ubegrænset kompleks eller elegant struktur i sin egen hjerne? Og tilmed betyder dette også at to dygtige programmører kan fortolke programmet på hver sin måde, i stedet for at den ene af dem påtvinger den anden sit billede af programmet. I lys af disse tanker må den ideelle udviklingsmetode være at bruge et simpelt sprog - en BASIC-dialekt er et godt valg, f.eks. VB.NET - og udelukkende skrive ekstremt simpel, procedural kode, og holde sig væk fra mere komplicerede features (generics, metaprogrammering) og programdesigns (mixins, policy classes, etc). På den måde vil enhver programmør hurtigt kunne forstå den trivielle kode, og danne sig sit eget, ideelle billede af programmet - fuldt ud ligeså elegant som Lisp og C++ templates, men med den fordel at andre end den oprindelige programmør også kan forstå abstraktions-metaforen, og tilmed tilpasse og ændre den, uden at skulle ændre koden. Det er sagt at programmer bør skrives til mennesker, ikke til computere, og denne fremgangsmåde passer perfekt til den opfattelse. Selv er jeg blevet betaget af VB.NET - både sprogets simplicitet, af de årsager jeg nævnte ovenfor, og Visual Studio med dets .NET-framework. Mono virker ikke synderligt interesseret i at understøtte VB.NET (hvis der er nogen, der kender til detaljer angående brug af VB.NET i Mono, så kontakt mig), så i stedet er jeg begyndt at bruge Gambas - det er ikke helt så elegant som VB.NET og, især, Visual Studio, men det er godt på vej. Og hvad der er vigtigst - koden er forståelig. Tuesday, March 21. 2006Homespring!Fremtiden ligger indenfor metaforbaseret programmering - programmeringssprog designet ud fra en samlende metafor der kan genkendes fra det virkelige liv, hvor beregninger og opgaveløsninger er udtrykt ved variationer over metaforen. Derfor blev jeg ganske positivt overrasket over at opdage at Homespring stadigvæk findes, og at der tilmed er en fremragende fortolker til det! Jeg blev i hin tider gjort opmærksom på Homespring af samme fyr som også genopvakte min interesse for Brainfuck (en syg, syg mand), men var noget trist over at den oprindelige Homespring-udvikler åbenbart havde valgt at droppe sproget. Nuvel - Homespring er unikt i sin tilgang til programmering. Som sagt er det et metaforbaseret sprog - i objektorienterede sprog opretter programmøren et hierarki af klasser der tjener til at repræsentere virkeligheden og virkelighedens genstande, og gennem objekterne manipulerer han med sin virtuelle virkelighed. I Homespring er den virtuelle virkelighed et flodløb og man udtrykker sine programmer ved at manipulere med vækstmønstret for en flok laks. Man beskriver i sit program flodløbet (i form af et N-ært træ) og hvilke specielle kvaliteter der er ved hver forgrening (node) i træet - det kan være en bjørn, der fanger laksene, et dambrug, der opdrætter nye laks, et kraftværk, ungdommens kilde, der gør laksene unge, strøm, broer, snevejr, osv. Laksene kæmper sig fra oceanet op ad floden for at gyde ved enden, og når dette er gjort, bevæger de nye, unge laks sig ud mod havet igen - når de når frem til havet er de programmets output (enhver laks har et navn). Sproget er uhyre high-level, hvilket naturligvis betyder at det er velegnet til alt. Homespring's syntaks er også noget helt for sig selv - følgende stykke kode er et Hello World-program taget fra den "officielle" hjemmeside: Universe of bear hatchery says Hello. World!. Det er så sandeligt fremtiden indenfor programmering. Thursday, March 9. 2006Comments (0) Trackbacks (0) Spinning Deathcubes of Doom from MarsI dag er en stor dag, thi i dag bliver VB.NET-programmet, Spinning Deathcubes of Doom from Mars, udviklet i forbindelse med undertegnedes datalogiundervisning, udgivet. Spillet går ud på at navigere musemarkøren igennem en kampzone af stigende letalitet, og overleve så længe som muligt. Der er en central server, der, hvis maskinen har adgang til Internettet, får tilsendt resultater fra spillerne. Målet er, naturligvis, at komme på highscoren med det højeste antal point. Spillet kan hentes her og kræver .NET 2.0 (Windows only). Wednesday, January 11. 2006CLIM-desktopJeg har tidligere skrevet om CLIM, Common Lisp Interface Manager. Her i dag har jeg undersøgt CLIM-desktop, en pakke af programmer, der danner et mere eller mindre integreret CLIM-baseret udviklingsmiljø til Common Lisp. Lad mig sige det med det samme: CLIM-desktop og dets komponenter er som hovedregel ekstremt buggy (især Climacs, teksteditoren er slem), ikke udpræget finpudsede og ganske besværlige at sætte op og bruge. Det er dog ret tydeligt at der er stort potentiale bag et miljø, der er helt og holdent implementeret i CLIM. At stort set alle grafiske elementer er kontekst-sensitive i den rigtige sammenhæng er en fantastisk feature, som virkelig gør interfacet dynamisk og effektivt at arbejde med. CLIM-desktops ideal er hvad Emacs er en primitiv efterligning af. CLIM-desktop gør det meget let at udnytte Lisp's indbyggede introspection-faciliteter, da det pludselig er ganske trivielt at klikke sig rundt i slots, generiske funktioner, subklasser og superklasser i et klassehieraki, samt konstant, selv når man er nok så langt nede i debuggeren, at kunne pille ethvert objekt fra hinanden. Denne gang har jeg screenshots. På dette screenshot vises Climacs, CLIM-listeneren og Beirc (en IRC-klient). I CLIM-listeneren vises resultatet af lidt klikkeri gennem et objekthieraki - derudover er listeren en fuldgyldig REPL, hvor man kan indtaste vilkårlig Lisp-kode. Hvis man, eller programmerne, dummer sig, ender man i debuggeren (nederst til højre), hvor man kan gennemgå en stack trace, se på funktionsparametre, etc. CLIM-desktop har potentiale - nok ikke som desktop til den almene bruger, men som udviklingsmiljø til Common Lisp vil det snildt kunne overgå SLIME, hvis der bliver udviklet en smule mere på det. Friday, December 30. 2005Comment (1) Trackbacks (0) Versionering af softwareSom mange andre aspekter af softwareudviklingsprocessen, er det ikke standardiseret hvordan man skal versionere sine programmer. Nogle benytter versioner som et marketing-trick, og hamrer lystigt major version-tallet opefter (eksempelvis Microsoft og Oracle), andre kører på version 1 i årtier (eksempelvis GNU Emacs) og helt andre lader versionsnummeret gå imod en matematisk konstant, som f.eks. Pi (eksempelvis TeX). I dette blogindlæg vil jeg skrive lidt om mine egne tanker om versionering af software, og hvilken metode jeg selv foretrækker. Et versionsnummer består typisk af en række tal adskilt af punktummer - f.eks. 1.0.2. Jo længere til venstre i rækken et tal står, desto større betydning har det, og jo større ændringer skal der til, før tallet forøges. Det første tal kaldes ofte major version, og det andet for minor version. Hvis major version er 0 betyder det som regel at programmet endnu ikke opfattes som færdigt af sine udviklere, og at det stadigvæk er i en eller anden form for test- eller udviklingsfase. Det er almindelig kotume at forøge major version-nummeret hvis den nye udgave kan opfattes som et nyt program, eller ikke er nativt kompatibelt med filer og data fra en ældre major version (det er dog muligt at nyere versioner har filtre der kan importere ældre filer). Minor version-nummeret bliver forøget, når en ny udgave har nye features (en feature release), men som sådan ikke kan opfattes som et nyt program. Det tredje tal angiver antallet af bugfix releases siden den sidste feature release, altså antallet af releases, der udelukkende retter fejl, og ikke tilføjer nye features. Når et tal inkrementeres, sættes alle tal til højre for det til 0. Som jeg nævnte i starten af indlægget er denne proces ikke standardiseret, og mange projekter har deres egne versioneringsregler, med mere end tre tal. Større, proprietære systemer har også for vane at specificere et build-nummer, og en masse andre værdier i versionsnummeret. Det værste ved denne model er dog major version-reglerne, idet forøgelse af major version-nummeret ofte er marketingbestemt, og ikke relateret til de konkrete teknologiske forbedringer. Meningen med et versionsnummer er at have et fast referencepunkt man kan bruge til at referere til bestemte feature-samlinger i et givent program - på samme måde som man kan checke tidsbestemte udgaver af et program ud af et versionsstyrings-system. Dette mål bliver i mine øjne bedre opfyldt, hvis man har en formel definition på hvad et givent versionsnummer betyder. Jeg foreslår en simplere model, der er delt ind i to faser - udviklingsfasen, der tilsvarer det punkt hvor major version-nummeret er 0 i den traditionelle model, og udgivelsesfasen, der er den fase, hvor man normalt ville bruge et major version-nummer på 1 eller derover.
Dette system har jeg udtænkt i perioder hvor jeg har været træt, fuld eller begge dele. Jeg synes selv at det er ganske elegant, især finder jeg den gradvise gang mod 1 i udviklingsfasen for meningsfuld, men systemet bærer nok præg af at jeg i højere grad har erfaring med distribueret udvikling af fri/open source software, end traditionel udvikling. Jeg ved ikke hvor godt dette system ville fungere i en traditionel udviklingsproces. Friday, December 23. 2005Comment (1) Trackbacks (0) Common Lisp Interface ManagerDe sidste par dage er, for mit vedkommende, gået med at eksperimentere med CLIM - Common Lisp Interface Manager - kort sagt, et widget-framework til Common Lisp. Det er bemærkelsesværdigt at CLIM, der er næsten 20 år gammelt, har features, der endnu ikke er set i nutidens udbredte, grafiske miljøer. Ja, jeg vil nu skrive om grafiske interfaces, ja, jeg vil rose dem. Ja, det er muligvis en gris, der flyver rundt, uden for dit vindue. En af disse features, og måske den mest centrale grundpille i hele CLIM's design, er konceptet omkring presentations. Ethvert grafisk interface præsenterer information til brugeren. Dette sker ved at printe tekst, billeder eller andre grafiske repræsentationer, men i selve udprintningen mister de deres oprindelige datatype - de bliver bare til tekst, et billede, en knap, eller et andet grafisk element. Hvis interface-designeren ønsker at de skal have en speciel betydning, er han nødt til selv at sætte signaler op til at opfange musetryk, drag'n'drop, etc. Sådan er det ikke med CLIM - her præsenteres information til brugeren ved at present'e det, og en CLIM presentation husker sit oprindelige objekt, så man fra ethvert grafisk element kan sige præcist hvilket Lisp-objekt, der står bag. Naturligvis kan et stykke data stadigvæk repræsenteres via tekst eller et grafisk element, men CLIM vil huske forbindelsen mellem brugerinterface-elementet og Lisp-objektet. Dette giver for det første brugerinterfacet en massiv grad af reflection, noget der skulle få OOP-tilhængere til at klappe i deres små hænder. For det andet skaber det også en alternativ inputmetode til diverse programmer. Hvis et program f.eks. beder om et billede som input, så vil enhver repræsentation, der er present'et som typen "billede", kunne bruges som input til programmet, blot ved at trykke på det - også repræsentationer fra andre programmer end det, der beder om input. Et Lisp-objekts interne type, og hvordan det repræsenteres i brugerinterfacet, behøver naturligvis ikke være identisk - et billede kan godt vises for brugeren som en tekststreng, eller et tal - det er ligegyldigt for CLIM, det vil stadigvæk være gyldigt input, når et program beder om et billede. Programmøren kan naturligvis altid oprette nye præsentationstyper og metoder til at indlæse, og præsentere, objekter som den relevante type, og selve systemet, der sørger for at denne mekanisme virker, er næsten uendeligt fleksibelt, og kan tweakes på stort set enhver mulig måde. Men ak, ingen applikation uden en null pointer. Lige så elegant og avanceret som CLIM er, rent specifikationsmæssigt, lige så dårlig er den eneste frie implementation, McCLIM, der ofte er plaget af besynderlige bugs. Ydermere stammer CLIM fra en anden tid, hvor man ikke havde de samme principper omkring GUI-design som man har i dag, derfor kan CLIM-applikationer virke ekstremt arkaiske og gammeldags. Det rører dog ikke ved deres effektivitet - CLIM blev designet som en højere enhed mellem de muligheder hhv. en kommandolinje og en GUI giver, og grundprincipperne er stadigvæk ganske solide. Tuesday, December 20. 2005MetaprogrammeringJeg har netop læst en rant, der disser Java (hvilket altid er godt), og meget velartikuleret beskriver hvorfor metaprogrammering er en god ting. Da jeg for tiden læser om metaprogrammering, og desuden er enig i hans antagelser, vil jeg anbefale alle at læse hans artikel, og prøve et sprog der understøtter metaprogrammering. Hvorfor skal man kun kunne manipulere data? Hvad er der galt i at manipulere kode? Edit: Wow, den rant er skrevet af en meget klog mand. En af hans andre artikler er også obligatorisk læsning. Thursday, December 15. 2005Comment (1) Trackbacks (0) Nye bøger, eksempelprogrammerJeg har endelig fået de bøger som jeg bestilte via Amazon for tre uger siden: The Art of the Metaobject Protocol og Lisp in Small Pieces. Udover at The Art of the Metaobject Protocol har nogle gode pointer (bl.a. at det er bedre at lave et ekstremt fleksibelt sprog, end et sprog der forsøger at dække alles behov out-of-the-box), er der også noget andet ved den der slår mig: I kapitel 1 bliver den komplette implementation af et objektsystem (med både multiple inheritance og multimetoder) gennemgået - som kontrast er det mest avancerede eksempel i The C++ Programming Language implementationen af en stak-baseret lommeregner. Også Structure and Interpretation of Computer Programs gennemgår implementationen af objektsystemer, virtuelle maskiner, compilere og streams. Det har fået mig til at tænke på om man kan vurdere graden af styrke og interaktivitet et givent programmeringssprog besidder, på baggrund af hvilke eksempelprogrammer der bliver implementeret i bøger og artikler skrevet om det. Jo simplere det er at udtrykke komplicerede koncepter i sproget, desto højere vil kompleksitetsniveauet for eksempelprogrammer blive - og omvendt, et sprog der kræver mange formalismer, og store mængder kode, for at opbygge komplicerede programmer, vil have tilsvarende simplere eksempler. Kender I til flere eksempler på dette fænomen? |
Language selectionFoobar (EN)
My name is Troels Henriksen, known as Athas in some parts of the Internet. This is my weblog, where I write about everything and nothing in particular. I am, at the time of this writing, a student of datalogy at the University of Copenhagen. I spend my free time programming, mostly in C++ and Common Lisp. Calendar
ArchivesQuicksearchCategoriesCreative CommonsSyndicate This Blog |
|||||||||||||||||||||||||||||||||||||||||||||||||
All original material on this website is copyright Troels Henriksen, and may only be used in accordance with the license listed in the sidebar.
