Torben Mogensen header

Når kæden hopper af

Engang i min ungdom cyklede jeg sammen men en af mine venner. Hans kæde sad løs, og et par gang under turen hoppede den af, og han skulle hver gang bruge et minuts tid på at sætte den på igen. Jeg spurgte min ven, om ikke han skulle se at få strammet kæden. "Nej", sagde han, "for det er så dejligt nemt at sætte kæden på igen, når den er så løs".

Jeg ser lidt på samme med manges tilgang til programmering: Det kan være fristende at bruge et programmeringssprog med svage eller dynamiske typer, for det er så nemt at lave et lokalt fix til de problemer, man hele tiden støder på. Med et stærkt og informationsrigt typesystem skal man i reglen lave mere arbejde up front for at få noget, der kører, men til gengæld undgår man senere at skulle bruge tid på at rette mange små fejl. Så jeg foretrækker selv det sidste -- undtagen til små programmer (op til 50 linjer), hvor 100% korrekthed ikke er vigtig (f.eks. små one-time scripts).

Samme holdning ser man hos Tim Chevalier, en af folkene bag sproget Rust, som Mozilla og Samsung vil bruge til en ny browsermotor. Han erkender, at man ved at bruge Rusts ownership types (en variant af lineære typer) skal tænke sig mere om, når man programmerer, og at man ofte skal bruge tid på at få sit program gennem typecheckeren. Men man gør det mere eksplicit, hvordan ens pointere opfører sig (og det på en måde, så oversætteren kan verificere, at du ikke deallokerer for tidligt), og dermed forhindrer man, at små tanketorsk slipper igennem til testfasen, og samtidigt sikrer man bedre performance end i et sprog med managed pointers (GC).

Man kan gå stort set arbitrært langt med krav om statisk verifikation i sprog, helt op til at en komplet specifikation af in/out relationen skal verificeres statisk. Det er nok at overdrive lidt, så kunsten er at finde en passende balance mellem nyttigheden af de verificerede egenskaber og den ekstra byrde, man pålægger programmøren for, at oversætteren skal kunne verificere disse egenskaber. Traditionel statisk typesikkerhed har (efter min mening) for længst vist sin værdi, men den egenskab, der sikres ved dette, er langt fra tilstrækkelig til at fange alle potentielle køretidsfejl. De resterende fejl kan et sprog så enten ignorere ved at gøre opførslen udefineret (som i C og C++) eller ved at lave køretidscheck, som enten standser programmet med en fejlmeddelelse eller laver en exception, der kan fanges af det kørende program. Nogle potentielle køretidsfejl (såsom overflow af tal) kan være så svære at verificere sig ud af, at køretidscheck er uundgåelige, men for eksempel dereference af nulpegere, pegere til deallokeret lager eller visse former for space leaks er det ikke urimeligt at verificere sig ud af. Det kan (ligesom statisk typesikkerhed) kræve lidt ekstra disciplin, når man programmerer og lidt ekstra tid under oversættelsen, men det betaler sig i reglen alligevel i form af færre fejl og bedre performance (og vedligeholdbarhed).

Kommentarer (15)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
#1 Lars Tørnes Hansen

Det kan være fristende at bruge et programmeringssprog med svage eller dynamiske typer, for det er så nemt at lave et lokalt fix til de problemer, man hele tiden støder på. Med et stærkt og informationsrigt typesystem skal man i reglen lave mere arbejde up front for at få noget, der kører, men til gengæld undgår man senere at skulle bruge tid på at rette mange små fejl.

Jeg må indrømme at jeg fortrækker programmeringssprog med et stærkt typesystem. JavaScript som det der findes i browsere, og som som jeg for nyligt er blevet udsat for kan jeg virkelig ikke lide.

Jeg er flasket op med Pascal, og har så senere brugt C, Java, C#, Delphi, og pt har jeg kig på Ada 2012, som har contracts(*) som forstås af compileren. Jeg har også et godt kig til SPARK programmeringssproget, der så vidt jeg har forstået det kun tillader sprog-elementer fra Ada programmeringssproget, der kan tjekkes statisk af værktøjer.

(*): pre-conditions, post-conditions, type invariants, subtype predicates Kig evt på http://www.ada2012.org/ og http://www.adacore.com/uploads/technical-papers/Ada2012_Rationale_Chp1_c... (PDF) Man kan få gratis adgang til en GPL udgave af et IDE der hedder gnat-gps, og en ada compiler på http://libre.adacore.com/ (klik på den store GNAT GPL download knap - den er til venstre) SPARK GPL værktøjer kan også hentes samme sted fra.

  • 3
  • 0
#6 Allan Ebdrup Blogger

Det er fair nok at du bedst kan lide stærkt typede sprog. Men jeg forstår ikke helt dette statement.

Traditionel statisk typesikkerhed har (efter min mening) for længst vist sin værdi, men den egenskab, der sikres ved dette, er langt fra tilstrækkelig til at fange alle potentielle køretidsfejl.

Hvordan kan de "bevise" sin værdi efter, i parantets, "din mening". Det er nok ikke den videnskabelige metodes "bevis" du taler om, så jeg synes du skylder en uddybning af hvad dine erfaringer er.

Jeg synes man skal vælge programmeringssprog til opgaven. Det er ikke det ene eller det andet.

Når man snakker on "bevis" så må man sige at de dynamiske sprog har haft utrolig meget vind i sejlene når det gælder almindelige webapplikationer og websites. Så de kan sagtens bruges til den slags opgaver med stor success.

Jeg har brugt lang tid med C# som en del af hverdagen, og jeg var vild med dotNet. Men efter jeg har programmeret Node.js (JavaScript på serveren) i små to år hver dag, så er jeg begyndt at bande og svovle over hvor meget ekstra arbejde man skal lave i C# og tænker ofte hvor irreterende det er at bruge så lang tid på at lave noget som jeg kunne have gjort på fem minutter i Node.js.

En gang imellem er et typestærkt sprog dog godt, som for nylig da jeg lavede en datatransformation af 1.2 millioner spørgeskemabesvarelser på mit projekt obsurvey. Der var typestærkhed faktisk en fordel.

Hvis jeg skulle have lavet den tilsvarende transformation i Node.js på arbejdet, ville jeg have skrevet en masse unittest, have kode review af en anden programmør og kørt testtransformationer. Så der ville det dynamiske sprog og skemaløse database have fungeret alligevel. Men til lige denne type opgave været lidt mere besværlig pga det dynamiske sprog.

Samlet set er det dynamiske sprog en fordel 95% af tiden, i det jeg laver, så jeg er ikke i tvivl om at det samlet set er en kæmpe fordel i mit arbejde.

Men afhængig af opgaven kunne jeg godt finde på at vælge et typestærkt sprog. Vælg det rette sprog (og database) til opgaven.

  • 3
  • 1
#8 Palle Simonsen

"Netop. Så med mindre du kan huske alle dine designbeslutninger (selv de mindste, og selv om du ikke har rørt programmet i flere år), bør du bruge et typestærkt sprog. "

Ikke enig.

Ens programmer skal naturligvis være velskrevne, anvende fornuftige variabel navne, funktionsnavne etc. og man skal naturligvis også være opmærksomme på, hvem man overhovedet betror udviklingsopgaver.

Eksempel #1:

(let ((raw-data (get-buffer-from-io current-io-port)))  
  (find-and-register-custom-violators   
      (convert-to-custom-records raw-data))  
;... etc ...  
)

Eksempel #2:

var  
  InFile:text;  
  LineData:string;  
   
begin  
  assign(InFile,'textfile.txt');  
  reset(InFile);  
  readln(InFile,LineData);  
  close(InFile);  
  LineData:=UpperCase(LineData);  
  handle(LineData);  
end.

Gad vide, om de to eksempler gør det samme? og hvad mon #2 program gør?

  • 1
  • 6
#9 Jesper Louis Andersen

Jeg foretrækker normalt statiske typer i sprog. Men efter at have arbejdet så lang tid i et sprog med dynamiske typer, så er jeg begyndt at tvivle på om det altid er en fordel at have statiske typer. Der er en masse muligheder, nogen grimmere end andre, hvis sprog er mere dynamisk i dets tilgang til data.

Der er 3 grunde herfor: Typefejl er uinteressante for den erfarne programmør. De er trivielle at rette og er typisk ikke et problem for programmet. De fejl der slipper gennem typesystemet er som regel de fejl der er interessante fordi de har impact.

Der er som skrevet mere up-front arbejde i at nedskrive typer. For visse problemer kan dette betale sig, specielt hvis problemet har formelt fast grund. Men hvis problemet er af mindre formel karakter, så er det ikke altid sikkert at dette up-front arbejde giver bonus i det lange løb.

Og slutteligt, så er der er en række ting der er svære at lave ordentlige typesystemer for: Dynamisk opgradering af kode over flere distribuerede maskiner er f.eks. svært. Specielt fordi du måske ikke har kontrol over alle maskinerne... Det bedste du kan gøre der er at lave et (dynamisk) check af de data der kommer ind i dit system. Og så er det ikke sikkert at et statisk typesystem er til megen hjælp.

Nuvel, for oversættere og lignende, så vil jeg rigtigt gerne have et statisk typet sprog. Eller hvis ting skal køre hurtigt. Men der er også en stor gruppe af problemer hvor jeg synes det er mere til ulempe end gavn.

  • 1
  • 0
#10 Baldur Norddahl

Jeg er klart med på vognen med de statisk typede sprog. Det skal bare være et sprog der ikke kommer i vejen unødigt. Mange har erfaringer fra ældre sprog som Java, der mangler typeinferens og hvor det er omstændigt at lave nye typer (en type per fil!).

Man taler om at nyere statisk typede sprog, som for eksempel Scala, føles som dynamiske. Man skal ikke unødigt angive typer hele tiden, den slags klarer oversætteren med typeinferens. Man kan oprette nye typer næsten ligeså koncist som i funktionssprog som Haskell.

Statiske typer er ikke kun programverifikation. Jeg bruger det i høj grad som dokumentation. Det kan være en stor hjælp for brugeren af et framework, når typesystemet hjælper med at sikre de rigtige parametre.

  • 6
  • 0
#11 Torben Mogensen Blogger

Jep. Mange menneskers modvilje mod statiske typer grundes i omstændelige eksplicitte erklæringer over det hele i programmet. Men med en god typeinferens kan dette reduceres til et minimum.

Min filosofi for typesystemer er:

  1. Hvis det med rimelighed kan lade sig gøre at inferere en mest generel type, så lad oversætteren gøre det.

  2. Hvor dette ikke kan lade sig gøre, skal programmøren give den minimale mulige information til, at #1 kan lade sig gøre.

  3. Sproget skal designes, så #1 gælder for det meste.

  4. Men det skal være muligt for programmøren at indsætte ellers unødvendige eksplicitte typeangivelser, som checkes af compileren.

Dette gælder også, hvis et typesystem er udvidet til at indeholde information, der går udover sædvanlige typer (f.eks. ejerskab eller lokalitet). Da den slags egenskaber ikke altid lader sig inferere, kan de kræve eksplicitte annoteringer (som tilfældet er i Rust), men antallet af annoteringer skal holdes så lav som mulig, og informationen for resten af programmet skal infereres ud fra ganske få annoteringer.

  • 3
  • 0
#13 Kai Birger Nielsen

Det er sikkert udenfor emnet, men jeg kiggede lige på noget databasekobling i python og kæden hoppede af to gange, men der var faktisk "udenfor" python i en eller anden grad. Første variant: udskrift fra databaseentries vha print gik fint, indtil der kom noget med æøå i, det gav en meget underlig fejlmeddelelse, som skyldtes at python's sys modul er ascii-centrisk. En meget dårlig måde at fikse det på er at lave det om og så reloade sys: import sys reload(sys) sys.setdefaultencoding("utf-8") Under aftestning pipede jeg output over i head for kun at se de første 10 linier og bingo, det gav så en io-error. Men det fikser man jo "nemt": from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE,SIG_DFL)

Min pointe, hvis jeg har en, er at ovenstående problemer jo egentlig ikke har noget med python at gøre, men alligevel? Jeg mindes i alt fald et foredrag af Bjarne Stroustrup om hvilke kolosal indflydelse det har på modtagelsen af et nyt sprog om standardbibliotekerne er af høj kvalitet eller bare noget, der også lige skulle med.

  • 0
  • 0
#14 Fredrik Wendelboe Løkke

Jeg ser type sikkerhed som en af de fordele der er ved et typesystem. Et mindst ligeså vigtigt aspekt er tooling. Dvs. features såsom refaktorisering, intellisense (kontekst afhængig hjælp, såsom visning af tilgængelige metoder i en klasse eller funktioner i et modul) og navigering, eksempelvis 'goto definition' eller 'find usages'.

  • 3
  • 0
#15 Benjamin Balder

"Strong typing is for people with weak memories"

Helt korrekt, selvom jeg også er enig med Torben Mogensen :) Vi skal bare give mere kontekst til eksemplet. Hvis man er ved at cykle til en aftale, man er for sent til, begynder man ikke at finde værktøjskasse frem og stramme koden... øh kæden.. op... så skynder man sig at sætte kæden på og kører videre, og så venter man på en anledning, hvor man kan sætte kæden rigtigt på. Og hvis kæden ikke bare kan strammes, kan det være, man skal skifte koden... øh kæden... helt ud..... analogi schmanalogi :)

Der findes et redskab til det hele. Jeg ville helt sikkert bruge et type-stærkt sprog til at skrive en oversætter; men en applikation, som er under konstant forandring, ville jeg vælge Python til, så man kan generalisere over iterérbare objekter osv... virkeligt vigtige redskaber for programmører, der skal ekspedere masser af ligegyldig samlebåndsproduceret kode for den utålmodige kunde, som alligevel ikke har nogen overordnet strategi for sin applikation og bikser og bakser med en kortsigtet tilgang til it.

Det fantastiske er også, at der faktisk findes en masse projekter relaterede til statisk type-checkning af f.eks. Python-kode. Dette eksempel viser, at selv med et dynamisk typet sprog, kan man sagtens rode de to verdener sammen og få lidt godt fra begge.

  • 0
  • 0
Log ind eller Opret konto for at kommentere