Torben Mogensen header

Er det altid godt at være dynamisk?

I almindeligt, dagligt sprog er ordet "dynamisk" betragtet som noget positivt. Jobannoncer søger efter "en dynamisk, ung person", og dynamisk ledelse betragtes også som noget positivt. Tilsvarende betragtes "statisk" som negativt -- tilbageskuende og ufleksibelt.

Men hvordan forholder det sig i programmeringssprogenes verden?

Man bruger ofte statist/dynamisk distinktionen til at indikere, om noget sker, før et program afvikles (typisk under oversættelsen), eller først, når programmet afvikles (enten i en fortolker eller når den genererede kode afvikles). Statisk er i denne betydning det, der sker før afvikling, og dynamisk er det, der sker under afvikling.

Eksempel 1: Statiske versus dynamiske typer.

Statiske typer betyder, at man inden et program afvikles finder og rapporterer typefejl, mens dynamiske typer betyder, at de først findes og rapporteres på køretid (hvis programmet rent faktisk når det sted, hvor fejlen sker).

Hvad er så bedst? Statisk typecheck finder mange fejl, som i dynamiske typesystemer først findes under afprøvning, eller i værste fald, når kunden bruger programmet. Til gengæld er statiske typesystemer mere restriktive, f.eks. kræves normalt, at alle elementer i et array har samme type, hvor man i dynamisk typede sprog sagtens kan have et array, hvor hvert andet element er et tal, og hvert andet er en string. Her må man i statisk typede sprog enten bruge en sum-/uniontype, bruge et array af tal/string-par, eller opdele i to arrays. Det er ikke uoverkommeligt, men det er lidt mere besværligt. Jeg hælder selv mest til statisk typede sprog, for jeg ved, at jeg laver fejl, og jeg vil gerne opdage dem så tidligt som muligt. Det kan illustreres med følgende billede:

Illustration: ukendt

Eksempel 2: Statiske versus dynamiske virkefelter (scopes).

Statiske virkefelter (også kaldet lexical scoping) betyder, at synligheden af en variabel- eller funktionserklæring er bestemt af syntaksen, sådan at denne er kendt før afviklingen af programmet. Med dynamiske virkefelter afhænger synligheden af programmets kørsel: Hvis du på køretid er kommet forbi erklæringen, den ikke er overskygget af en anden erklæring, og den ikke er "aferklæret" igen (f.eks. ved udgangen af en funktion), så er den synlig. F.eks. kan en funktion f erklære en variabel x, og kalde en funktion g et helt andet sted i programmet, og g kan nu bruge x. Men hvis du kalder g fra et sted, hvor x ikke er erklæret, så kan g ikke bruge x.

Statiske virkefelter bruges i de fleste moderne sprog, men der nogle scripting sprog, nogle varianter af BASIC, og ældre versioner af LISP, der bruger dynamiske virkefelter. Dynamiske virkefelter er enklere at implementere, så sprog, der er implementeret over en weekend af folk, der enten ikke kender implementeringsmetoder for statiske virkefelter eller ikke gider besværet, har ofte dynamiske virkefelter. Der er dog undtagelser: TeX (og dermed LaTeX) bruger dynamiske virkefelter, og det er på ingen måde lavet af en amatør over en weekend. Knuts begrundelse for dynamiske virkefelter er, at en TeX-makro helst skal bruge samme tegnstørrelse, skriftsnit, osv., som er i brug det sted, hvor makroen bruges, og ikke der, hvor makroen defineres. Hvis der ikke var dynamiske virkefelter, skulle alle den slags attributter overføres eksplicit til makroen. Det kunne i stedet håndteres med en implicit kontekstparameter (lidt ligesom self/this i objektorienterede sprog), men i den kontekst, hvor TeX/LaTeX bruges, er statiske virkefelter ikke nogen stor fordel. Men dynamiske virkefelter er ikke kompatible med statiske typer (undtagen hvis en variabels navn afslører dens type, som f.eks. x, x$ og x% i BASIC, der er hhv. floating-point, string og heltal), så hvis man er tilhænger af statiske typer, er statiske virkefelter (næsten) en nødvendighed. Da statiske virkefelter også gør det nemmere at læse og forstå et program, hælder jeg i høj grad til, at statiske virkefelter er bedre end dynamiske ditto.

Så min holdning er, at når det gælder programmeringssprog, så er statisk godt og dynamisk skidt. Det er så uheldigt, at det normalt er omvendt i almindelig sprogbrug. Det kunne jo f.eks. få Dilberts chef og lignende typer til at foretrække dynamiske typer og virkefelter "for dynamisk er jo godt". Så måske skulle vi finde på andre ord en "statisk" og "dynamisk", når det gælder programmeringssprog? Jeg har set betegnelserne "early" og "late" brugt (f.eks. "early/late binding" og "early/late types"). Det er dels mere beskrivende for den faktiske situation, og det betragtes normalt for mere positivt at være tidlig end sen, så det er måske ikke et dårligt ordvalg. Har I bedre bud (på dansk eller engelsk)?

Kommentarer (32)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
#1 Rune Larsen

... så er du kureret for ideen om, at "dynamisk" lyder bedre end "statisk".

Efter at have fejlsøgt kode i mange år, vil jeg til enhver tid foretrække statiske sprog.

Dog kan jvm-sprog stadig drille, da generics' typecheck (desværre) kun sker i compileren og ikke på runtime. I den ideelle verden gør det jo ingen forskel, men meget ofte er kodebasen kompileret til en anden version af et library, end den version som koden faktisk eksekvere med. Og så ser man samme type-fejl på runtime, som når Erlangs pattern matching falder over en ukendt værdi eller python dict eller ValueError.

  • 2
  • 0
#2 Peter Lind

Jeg arbejder primært med dynamiske sprog men har været forbi et par statiske også. Fordelen ved de statiske sprog er for mig til at overse - jeg hverken oplever eller laver selv de fejl en compiler fanger. Det er en anden type fejl, der opstår - og de ville også opstå i statiske sprog.

Eric Elliot har en ret fornuftig post om det: https://medium.com/javascript-scene/the-typescript-tax-132ff4cb175b

Alt er selvfølgelig kontekst-afhængigt - det her er baseret på erfaring med webudvikling.

  • 4
  • 1
#4 Anders Munch

Statiske typer finder fejl, men det er ikke det samme som at statisk typede programmer har færre fejl.

Ganske vist er type-annotering en aktivitet der finder fejl, men det er en blandt mange: Afprøvning, unit tests, granskning, omhu - det er alt sammen aktiviteter som også finder fejl, og som konkurrerer om software-udviklerens tid. Hvad der er den bedste brug af tiden, det finder man ikke ud af uden at måle.

Så hvor er empirien? Hvor er studierne? Jeg håber forskningen på DIKU har andet at tilbyde end anekdoter og karikaturtegninger...

Lad mig åbne ballet med Prechelt(2000). Det samme program tager kortere tid at udvikle i dynamiske sprog, vel og mærke uden at korrektheden lider.

  • 2
  • 2
#10 Mark Klitgaard

Der er meget man kan kritisere JS for, men det har bare ikke noget at gøre med statisk vs. dynamisk. Et statisk typet JS vil producere nøjagtigt den samme værdi eller fejlmeddelelse.

Bare fordi man smækker Typescript på Javascript gør det ikke sproget til et strongly typed sprog, det er meget vigtigt at holde fast i den forskel. Den primære udfordring jeg selv støder ind i når jeg bruger weakly typed sprog som JS er som regel at det kan være svært at gennemskue om en varibel rent faktisk er af den type du forventet, f.eks. et string[] det pludselig er blevet et string[][], den type problematikker er ikke eksisterende i strongly typed sprog som Java, C# osv.

  • 1
  • 1
#11 Torben Mogensen Blogger
  • 3
  • 1
#12 Torben Mogensen Blogger

Stærkt defineret og svagt definerede sprog kunne også være en betegnelse?

Det har en lidt anderledes betydning:

Stærkt typede sprog vil fange alle typefejl, enten før eller under kørsel. De kan altså godt være dynamisk typede. Scheme er et eksempel.

Svagt typede sprog vil ikke fange (alle) typefejl, så nogle operationer udføres på værdier, hvor det ikke giver mening.

Det gælder f.eks. maskinsprog, hvor man rask væk kan gange en stringpointer med pi og bruge resultatet som returadresse. Det gælder også f.eks. unions i C, hvor du kan have en union af double og funktionspointer, lægge pi i double-feltet, hente værdien ud via pointer-feltet og kalde den som en funktion.

  • 1
  • 1
#14 Morten Jensen

Bare fordi man smækker Typescript på Javascript gør det ikke sproget til et strongly typed sprog, det er meget vigtigt at holde fast i den forskel. Den primære udfordring jeg selv støder ind i når jeg bruger weakly typed sprog som JS er som regel at det kan være svært at gennemskue om en varibel rent faktisk er af den type du forventet, f.eks. et string[] det pludselig er blevet et string[][], den type problematikker er ikke eksisterende i strongly typed sprog som Java, C# osv.

Enig. Typescript er ikke et særlig godt eksempel på et sprog med statiske typer. Der er jeg enig med Torben, Standard ML eller lignede er et langt bedre eksempel. Hindley-Milner typesystem, inferens, totalitets-check osv.

medium.com-blogindlægget ender i øvrigt med at konkludere at Typescript som modkandidat til vanilla JS ikke giver meget merværdi. Bl.a. pga. veludviklede værktøjer til vanilla JS såsom JSlint - altså et eksternt program til at foretage statiske checks, bl.a. af typerne. Det nuancerer for mig diskussionen gevaldigt :)

Min egen erfaring er, at nogle programmer med meget dynamisk opførsel er nemmere at kode i dynamisk typed sprog som Scheme, Lisp og Python.

I større programmer foretrækker jeg statiske typer fremfor dynamiske. Jeg mister overblikket over typerne hvis ikke de står skrevet i koden, når projektet runder nogle tusinde liniers kode og bliver spredt over flere filer.

  • 2
  • 0
#15 Torben Mogensen Blogger

var a = 1; var b = 1;

Og hvad mon så a+b giver -- tadaaaah : 11.

Som nævnt andetsteds er svaret 2, men hvad nu hvis vi laver nogle små varianter:

'1' + 1 + 1 giver '111' 1 + 1 + '1' giver '21' 2 + '2' giver '22' 2 * '2' giver 4 {2} giver 2 {2} + 2 giver 2 2 + {2} giver uendelig løkke ({2}) giver uendelig løkke {{2}} giver 2 {{}} giver undefined {({})} giver {} {({2})} giver uendelig løkke 1 + {} giver '1[object Object]'

Det giver alt sammen god mening. Eller hva'?

  • 8
  • 1
#16 Christian Nobel

Det giver 2, ganske som forventet.

Det var så en simplificering, men konkret lavede jeg dette:

function calsum(firstval,secondval,endsum)   
{  
    var fval = document.getElementById(firstval).value.replace(',','.');  
    var sval = document.getElementById(secondval).value.replace(',','.');  
    document.getElementById(endsum).value = (1*fval+1*sval).toFixed(2).replace('.',',');  
}   

Funktionen skal summere to inputfelter og returnere resultatet til et tredje og tage højde for at værdierne er indtastet med decimalkomma og ikke tåbeligt amerikansk decimalpunktum.

Læg mærke til, at fordi jeg ikke kan definere fval som en real så er jeg nødt til at lave cowboytricket med at gange fval med 1, for ellers betragtes det som en streng.

Og det er i min optik udtryk for et programmeringssprog som tangerer det sublime lort.

Det er det der er kernen med Javascript, man kan ikke altid forudsige hvad der sker, pga. den manglende typing.

  • 1
  • 3
#18 Peter Lind

Vi du dermed sige, at når du koder i et statisk typet sprog, så har du aldrig nogensinde fået en typefejlmeddelelse fra compileren?

Så er du enten den bedste programmør i verden, eller du har aldrig programmeret noget mere kompliceret end "Hello World" i statisk typede sprog.

Nej. I det omfang de fejl sniger sig ind bliver de fanget før koden kommer i decideret brug. Det er irrelevant om compileren eller programmøren fanger dem.

Til gengæld har jeg spildt vanvittig meget tid på statisk typede sprog der enten kræver ekstremt meget upfront arbejde eller tager lang tid om at fange de fejl, der alligevel vil blive fanget ved kørsel af programmet.

  • 1
  • 6
#19 Peter Lind

Læg mærke til, at fordi jeg ikke kan definere fval som en real så er jeg nødt til at lave cowboytricket med at gange fval med 1, for ellers betragtes det som en streng.

Du mener, du har ikke styr på javascript i browsere, og det er sprogets skyld? Input felter er strings, så du vil altid få en string ud. For at omdanne til den korrekte type bruger du Number(fval) - Number("2.1") giver som forventet 2.1.

  • 1
  • 2
#20 Michael Cederberg

Til gengæld har jeg spildt vanvittig meget tid på statisk typede sprog der enten kræver ekstremt meget upfront arbejde eller tager lang tid om at fange de fejl, der alligevel vil blive fanget ved kørsel af programmet.

Jeg er 800% på holdet for statisk typede sprog men du har ret her. Det er almindelig best practice at alt kode skal køre i automatisk test inden det kommer i produktion og såfremt man har ordentlig coverage så fanges den slags fejl. Personligt er jeg hurtigere til at finde fejl i statisk typede sprog, men det er nok forskelligt fra person til person.

Men der hvor kæden falder af er når en anden skal overtage koden. Han får meget lidt guidance når typerne mangler. Når han kalder metoden "getFirstElement" på et objekt, så er det ikke vil at sige hvad der sker fordi det i princippet kan være en vilkårlig metode med navn "getFirstElement" der kaldes.

På samme måde når det handler om refactorying. Hvis jeg vil rename metoden "f" så er det ikke givet hvilke af metoderne "f" der er "in scope" for renaming. I praksis mister man en række muligheder for auto refactoring når typerne forsvinder.

  • 2
  • 1
#21 Baldur Norddahl

Det er væsentligt nemmere at lave en editor eller IDE der kan hjælpe programmøren når der er tale om et statisk typet sprog. Du får med det samme at vide at den der går ikke, ligesom at editoren giver forslag til hvad det er, du vil skrive. Editoren kan også hjælpe med at refactorere hvis du eksempelvis vil omdøbe en funktion, så kan den automatisk finde alle kald til denne funktion og også rette der.

Med moderne type inferens er der iøvrigt ikke den store forskel på hvor meget der skal skrives. De fleste type erklæringer kan undlades.

Hvis man må være lidt fræk, så er den største styrke ved dynamisk typede sprog at man skal kunne mindre og have mindre forståelse for koncepter som eksempelvis typer. Man kan prøve sig frem. Men til gengæld mister man hjælpen fra udviklingsmiljøet, så det bliver hurtigt til en ulempe når nybegynderen avancerer.

  • 8
  • 1
#22 Morten Jensen

Med moderne type inferens er der iøvrigt ikke den store forskel på hvor meget der skal skrives. De fleste type erklæringer kan undlades.

Ja man kan hurtigt gøre kål på typesystemet hvis der er inferens ;) Jeg er generelt enig med dig, men jeg synes det er et tveægget sværd.

C# tillader at du strør om dig med "var", og C++ har tilsvarende "auto".

Hvis det bruges for flittigt, så kan meget af det smarte ved statiske typer forsvinde efter min mening, idet typerne bliver "usynlige".

Bevares, typesystemet bliver kontrolleret og skal stadig stemme overens - så du får ikke flere typefejl ved at bruge inferens.

Analogi: Jeg undlader ofte typerne i fx. Haskell og SML mens jeg udvikler/itererer over et design, og så tilføjer jeg dem når jeg er ved at være færdig, og nu ved hvilke specifikke typer jeg gerne vil have og ikke bare de allermest generelle der kan bruges (som jo er resultatet hvis du bruger Hindley-Milner inferens).

  • 2
  • 0
#23 Anders Munch

http://evanfarrer.blogspot.com/2012/06/unit-testing-isnt-enough-you-need

Interessant udviklingsstrategi. Først skriver man et programbibliotek i et dynamisk typet sprog, og så oversætter man det linie for linie til et statisk typet sprog.

Jeg ville have foreslået ham at bruge Common Lisp i stedet for Python og Haskell, for så kunne han bare have tilføjet typer i stedet for at skrive noget om. Det er en idé i hvert fald, jeg har ikke selv prøvet at skrive CL med typeannotering, så det kan godt være jeg overvurderer deres praktiske anvendelighed.

Han får mere eller mindre gendrevet påstand 2 og 3, hvilke ikke kan overraske. Det kan godt være han har hørt folk sige de ting mange gange, men derfor behøver jeg ikke være enig i dem. Påstand 1 derimod er jeg enig i, med det forbehold at unit testing er ikke den eneste måde at teste på. Men den påstand kan hans metode hverken be- eller afkræfte.

Men altså, nej, det er ikke et svar på Prechelt, for det studerer noget helt andet.

Prøv i stedet ML, Haskell eller F#.

Jeg er ikke forsker, hvordan vil du have jeg skal gøre det?

  • 0
  • 2
#24 Troels Henriksen

Jeg er ikke forsker, hvordan vil du have jeg skal gøre det?

Du kan downloade oversættere her:

Mig bekendt er der ikke krav om en forskeransættelse for at kunne hente og bruge dem.

Det er en idé i hvert fald, jeg har ikke selv prøvet at skrive CL med typeannotering, så det kan godt være jeg overvurderer deres praktiske anvendelighed.

Jeg har prøvet CL med typeannoteringer, og du overvurderer deres praktiske anvendelighed. Statisk er de primært nyttige som lavniveau-optimering ("x er altid et fixnum"), hvor visse oversættere så kan advare hvis de kan se at du ikke overholder annoteringen. De er ikke specielt nyttige til at udtrykke f.eks. sum- og produkt-typer på en statisk verificérbar måde.

  • 3
  • 0
#26 Stig Christensen

Funktionen skal summere to inputfelter og returnere resultatet til et tredje og tage højde for at værdierne er indtastet med decimalkomma og ikke tåbeligt amerikansk decimalpunktum.

Læg mærke til, at fordi jeg ikke kan definere fval som en real så er jeg nødt til at lave cowboytricket med at gange fval med 1, for ellers betragtes det som en streng.

Det er igen i dårligt eksempel, det helt samme vil fx ske i C#.

var a = ... streng fra UI var b = ... anden streng fra UI var c = a + b

Så i begge sprog er du jo nødt til at konvertere til en numerisk type. I stedet for "cowboytricket " * 1, hvorfor bruger du ikke parseFloat eller number lige som du ville gøre i fx C#?

og er amerikansk separator tåbeligt fordi du skal krudt på at konvertere eller hvordam skal det forstås?

  • 0
  • 0
#28 Michael Cederberg

Jeg kan slet ikke se hvordan jeg vil komme i mål med et dynamisk sprog, når man laver store refaktoreringer i store projekter. Her er compileren guld værd.

Og det er et sted hvor man kan se at ikke alle typesystemer og dermed ikke alle statisk typede sprog er skabt ens. Hvis man sammenligner refactoring i C# og Java, så er det min erfaring at i C# er man sikker på at det refaktorerede program virker på samme måde som det oprindelige. I Java sker det at der bliver forskel. Det er fordi type-systemet i Java i nogle tilfælde (generics) blot er syntaktisk sukker og at programmer der egenligt er syntaktisk forkerte alligevel virker.

Og derfor mener jeg i øvrigt at Java bør få reified generics og custom valuetypes ASAP.

  • 0
  • 0
#29 Christian Nobel

Så i begge sprog er du jo nødt til at konvertere til en numerisk type. I stedet for "cowboytricket " * 1, hvorfor bruger du ikke parseFloat eller number lige som du ville gøre i fx C#?

I rest my case.

og er amerikansk separator tåbeligt fordi du skal krudt på at konvertere eller hvordam skal det forstås?

Det var mere et generelt rant i forhold til amerikanerne der ikke vil vedgå sig standarder som f.eks. decimalkomma (og datoformat, og længdemål, og vægtenheder, og, og, og....).

  • 0
  • 1
#31 Anders Munch

Nemt nok: Du finder programmer i Haskell og oversætter dem så direkte som muligt (linje per line) til Python, og ser om du kommer til at introducere fejl, som statisk typecheck ville have fanget.

Jeg ved ikke hvad det skal forestille at teste. Selv hvis jeg ikke introducerede en eneste fejl af nogen slags, så ville jeg jo ikke kunne påstå at jeg havde bevist noget. Der er ingen analogi til Prechelts studie. Transskribering er en meget atypisk måde at udvikle software på.

Derimod ville jeg vældigt gerne se et komparativt studie af produktivitet i programmeringssprog som inkluderer mere moderne statisk typede sprog; en opdatering af Prechelt. Men jeg havde sådan set forventet at universitetsverdenen studerede den slags.

  • 0
  • 0
#32 Torben Mogensen Blogger

Men jeg havde sådan set forventet at universitetsverdenen studerede den slags.

Universitetsverdenen studerer primært det, den kan få forskningsmidler til, og den slags komparative studier er det svært at få penge til -- og de er ret dyre, hvis resultaterne skal være nogenlunde troværdige. For det kræver, at man finder programmører, der har præcist den samme mængde af erfaring med Haskell og Python. Og det vil ofte betyde, at man træner ikke-programmører fra bunden i de to sprog med lige lang tid til hvert sprog.

Der har været lavet komparative studier, men dem, jeg kender til, er ret gamle, og lavet med forholdsvis små populationer.

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