Huller i Java

Jeg skal være den første til at indrømme, at der er ting, som kunne have været smartere i Java. Det skulle også være mærkeligt, hvis ikke der i løbet af godt 20 år er fundet på bedre måder at gøre ting på, og som bagudkompatibiliteten (som jeg sætter stor pris på) gør det svært at introducere. Alt taget i betragtning synes jeg dog, at antallet af egentlige smuttere er begrænset, og der bliver rettet op på dem - introduktionen af java.time til erstatning for java.util.Date og venner er et godt eksempel.

Nedenfor er en håndfuld eksempler på ting, som jeg synes er signifikant mere besværlige end nødvendigt. Folks forskellige baggrunde giver anledning til forskellige præferencer, og det er altid lærerigt at få et nærmere indblik i dem, så hvor synes I især, at skoen trykker?

Som jeg ser det kan manglerne ret beset inddeles i to overordnede kategorier. Den ene er konstruktioner, som også findes i andre sprog, men som fylder mere i Java - i vidt omfang det, mange benævner boilerplate-kode.

Det primære ankepunkt her er nok de basale (bean)klassekonstruktioner, altså settere og gettere, equals og hashCode. De generer mig som nævnt i et tidligere indlæg ikke så meget, fordi jeg ikke bruger meget tid på dem. Ja, de fylder i koden, men man kan i vidt omfang se bort fra dem, og bliver autogenereret af IDE'et. Det hænder endda, at de kan bruges til noget - gettere, som transformerer data, eller bare muligheden for at sætte breakpoints.

  • Collection literals er derimod en mangel.
private static final Set<String> KEYS = ("foo", "bar", "baz");

At kunne erklære en immutable collection på samme måde som man fx kan erklære et array, altså som en konstant i koden uden at skulle konstruere, kalde .add eller .set og pakke ind i Collections.unmodifiable, vil være værdifuldt.

  • Tupler mangler også.
public (int, double) compute() {
  ...
  return (x, y);
}
 
(int a, double b) = compute();

Det er ofte, at man har behov for at kunne returnere mere end en værdi fra en metode. Det kan sagtens laves med en Pair-klasse, men det er mere oplagt, at det bare er overførsel af flere værdier, hvis der ikke eksplicit er en klasse ind over.

  • Defaultværdier for metodeparametre ville være fantastiske.

Det er især constructors, hvor defaultværdier ville give mening. Constructor chaining er fint nok, men så snart der kommer meget mere end to constructors i samme klasse taber man overblikket. Bare det faktum at det bliver meget langt med et hypotetisk kodeeksempel illustrerer fint pointen :-)

  • Konstant (final) by default.

Bortset fra i løkker er det reglen snarere end undtagelsen, at variable er konstante. Det ville forhindre fejl og spare tid, hvis man eksplicit skulle erklære dem som ikke-konstante.

Den anden kategori er ting, som det aktuelt ikke umiddelbart kan lade sig gøre at lave i Java, for eksempel

  • Algebraiske typer. Algebraiske typer er en af de ting, som fik mig til at tænke "okay, det er smart" dengang jeg i en fjern fortid første gang stiftede bekendtskab med ML (*). Andre folks entusiasme for dem taget i betragtning lyder det måske som en dårlig undskyldning, men smart som det er, så sidder jeg bare sjældent og savner dem. Det skyldes måske, at de fleste datastrukturer, som min kode håndterer, er uniforme collections af instanser af samme klasse, så pattern matching på typerne ikke giver så megen værdi.

Man kan i mange tilfælde lave en billig udgave i Java ved at definere felter og metoder i en enum, og hvis det går helt galt er der den verbose løsning med en subclass pr. element i typen.

  • By value.

Flaskehals nummer et i moderne computere er hukommelsen, så hvis man kan undgå at skulle følge en pointer er det en god ting. Der er derfor i nogen tilfælde signifikant overhead forbundet med at alting (bortset fra værdier af primitive typer) tilgås gennem referencer - det er igen ikke noget, som giver mig søvnløse nætter, men i HPC-agtige applikationer er det centralt. JEP 169 arbejder i øvrigt på at få det ind i Java, så der er lys forude.

(*): Jeg burde være fan af et programmeringssprog, hvis navn er mine initialer. Eller omvendt.

Mikkel er CTO i IntraMed, som laver applikationer til håndtering af behandlingsforløb for patienter med kroniske sygdomme.

Kommentarer (33)

Baldur Norddahl

Scala har tupler men det er faktisk lidt af en unode at benytte dem til returværdier. I stedet bør man bruge en case class til formålet.

case class NavnAdresse(navn: String, adresse: String)
def findById(id: Int): NavnAdresse = ???

i stedet for:

def findById(id: Int): (String, String) = ???

Og hermed endnu en pointe - der er intet der stopper dig i at implementere den første findById i Java, men det er så bøvlet at man i stedet ser folk returnere Object[] og andre horrible hacks.

Nikolaj Brinch Jørgensen

Mikkel, har du overvejet at benytte Groovy, evt. med statisk kompilering og typecheck? Stort set alle dine ankepunkter er netop forbedret i Groovy. Og så er Groovy jo Java, og kan mix-kompiles, så man kan flytte lidt ad gangen.

Jeg synes nu ikke Java 8 er så ringe endda. Stream API, functional interfaces, lambdas osv. er ganske glimrende udvidelser til sproget. Ligesom default methods på interfaces (traits i Groovy er dog bedre IMHO).

Torben Mogensen Blogger

Jeg husker ikke hvem, men en eller anden har en gang sagt "Over time, every programming language evolves to look more and more like ML".

Det er ikke helt ved siden af: Tupler, closures, og i mindre grad typeinferens og algebraiske typer er efterhånden blevet tilføjet mange mainstream programmeringssprog, der ikke er født med disse, og ideen om, at pointere per default ikke kan være null er også ved at vinde frem.

Jeg er ikke den store Java-bruger, men jeg synes, at der udover Mikkels ønsker er nogle oplagte ting, man med fordel kunne tilføje:

  1. Typer, der ikke tillader null-pointere.
  2. Typer, der ikke tillader opdatering af felter m.m. efter, at de er initialiserede.
  3. At disse er default.
David Askirk Fotel

Du mener vel lisp?

"Greenspun's Tenth Rule of Programming: any sufficiently complicated C or Fortran program contains an ad hoc informally-specified bug-ridden slow implementation of half of Common Lisp."

  • Philip Greenspun

    "We were not out to win over the Lisp programmers; we were after the C++ programmers. We managed to drag a lot of them about halfway to Lisp."

  • Guy Steele, Java spec co-author

Begge fra: http://www.paulgraham.com/quotes.html

Tobias Tobiasen

"Det primære ankepunkt her er nok de basale (bean)klassekonstruktioner, altså settere og gettere, equals og hashCode. De generer mig som nævnt i et tidligere indlæg ikke så meget, fordi jeg ikke bruger meget tid på dem. Ja, de fylder i koden, men man kan i vidt omfang se bort fra dem, og bliver autogenereret af IDE'et."

Selv om de ikke generer dig så er der en meget effektiv måde slippe for dem. Jeg har bruge lombok (https://projectlombok.org/) i årevis og savner ikke at skrive/læse gettere, settere, equals, hashCode og toString(). Du skrive bare @Getter, @Setter eller måske @Data på din klasse og så er det klaret. Det er nydeligt integreret i IntelliJ og sikkert også andre IDE'er.

Ivan Skytte Jørgensen

Tilbage i 90erne tilvalgte og fravalgte Gosling+Naughton features, ud fra hvad de ønskede at sproget skulle bruges til. Som i alle sprog, så når man koder, savner man nogle gange features som findes i andre sprog. F.eks.:
- funktionspointere
- duck-typing
- operator overloading
- multipel nedarvning
- procedural kode
- comesfrom
Når man så støder ind i et problem, hvor man savner en feature,så er det, at mængden af boilerplatekode stiger. Det må man leve med, eller skifte sprog.

Palle Simonsen

Når man så støder ind i et problem, hvor man savner en feature,så er det, at mængden af boilerplatekode stiger

Et ordentlig programmeringssprog bør kunne udvides med den pågældende feature, med et minimum af arbejde, så man som anvender ikke skal interface, subclasse, typecaste, hækle, strikke, klippe hækken, vaske bilen og lufte hunden, før man kan løse sin opgave :)

Jeg kan tage fejl, men i min optik er boilerplate en masse besværgelser man skal igennem før man (endelig) kommer til at kunne løse sin opgave.

I denne forbindelse kan jeg ikke lade være med at linke til denne ganske humoristiske artikel om Java Frameworks: http://discuss.joelonsoftware.com/default.asp?joel.3.219431.12&cm_mc_uid...

No offence - respekt for de der mestre deres værktøj: Java, Pascal, ML, C, ...

Jesper Louis Andersen

"Over time, every programming language evolves to look more and more like ML"

Jeg har hørt den udtalt af Bob Harper, men jeg ved ikke om han var den første. Udsagnet passer iøvrigt også fint for Common Lisp. Det var bare en milepæl på vejen mod Standard ML. Vi er nu forbi Common Lisp-positionen og på vej videre da sprog nu efterhånden får algebraiske datatyper og pattern match :)

Iøvrigt er algebraiske datatyper's værd i høj grad bestemt at opgaven og om du har dem til rådighed. Deres styrke er at de er i stand til at eliminere en masse bolsk blindhed fra din kode (boolean blindness), hvilket lader dig skrive langt mere præcise programmer, hvor scope styrer hvad du kan gøre med dine data. Det giver en udviklingsform der af Yaron Minsky er beskrevet som "make illegal representations impossible". Altså, lav dit program sådan at de eneste måder du kan konstruere data på er de for programmet lovlige. Resultatet er at du kan eliminere en hulens mængde defensiv kode og checks.

Ivan Skytte Jørgensen

Jeg kan tage fejl, men i min optik er boilerplate en masse besværgelser man skal igennem før man (endelig) kommer til at kunne løse sin opgave.


Jeg brugte begrebet "boilerplate" lidt upræcist. Det var nærmere: håndkodning af den feature, som har brug for. F.eks. hvis man sidder med C og virkelig kunne bruge lidt klasser med nedarvning og virtuelle funktioner, så kan man kode det selv, men sjovt er det ikke. Eller hvis man sidder med Java og virkelig kunne bruge multipel nedarvning, så kan håndkode det selv, men specielt kønt er det ikke. Eller hvis man sidder med et shellscript og virkelige godt kunne bruge floating-point aritmetik, så kan man humpe det igennem med brug af 'bc'. Osv.

Udfordringen er at når man har et problemdomæne, hvor de fleste delproblemer kan løses elegant vha. sprogets faciliteter, men der er et eller flere delproblemer, hvor mængden af "udenomskode", for at få problemet løst, er høj. Det kan være mindre detaljer, så som at lave 64-bit unsigned udregninger i Java; eller større ting, så som at snakke med en webserver i C. I begge tilfælde ender man med at skrive mere ulegant kode end hvis man brugte et andet sprog.

Palle Simonsen

@Ivan: Så forstår jeg bedre. De problemer du nævner har lidt forskellig skala gående fra at det kan 'sagtens' løses med kald af en biblioteksfunktion skrevet i et andet sprog til at man har det forkerte værktøj til opgaven. I det sidste tilfælde skal man så overveje om man kan omformulerer problemet til det værktøj man har. F.eks. klasser - hvis ønsket bare er struktur, kan man gøre det på anden vis, hvis det er fordi kunden ønsker en objektmodel i koden må man se om man kan sno sig igennem alligevel - ellers må man jo gribe ud efter et OO sprog :)

Troels Henriksen

Mener du ikke "Make illegal states unrepresentable"?

Og det kræver vel nærmest dependent types eller i det mindste GADT.

Det kræver dependent types og en masse bevismaskineri, men det er jo ikke en alt-eller-intet ting: Jo flere invarianter du kan garantere via typesystemet, des bedre, også selvom der stadigvæk kan repræsenteres ugyldige tilstande. Jeg synes sprog som SML og Haskell har en god balance mellem konceptuelt/notationelt overhead og statiske garantier. Der findes sprog med meget stærkere typesystemer - Agda, Coq og Idris har jeg selv prøvet - men man ender ofte med at bruge enormt meget tid på at føre korrekthedsbeviser for relativt trivielle ting. At finde balancen i et typesystem er i virkeligheden mere et spørgsmål om brugergrænsefladedesign, end om matematisk logik.

Jonas Høgh

Tilbage i 90erne tilvalgte og fravalgte Gosling+Naughton features, ud fra hvad de ønskede at sproget skulle bruges til.

Spørgsmålet er så, hvad de havde tænkt sig at Java skulle bruges til. Man kunne argumentere for, at de allerede i 1995 fandtes bedre sprog til de fleste typer opgaver.

Torben Mogensen Blogger

Spørgsmålet er så, hvad de havde tænkt sig at Java skulle bruges til.

Sproget var oprindeligt designet til interaktivt TV, altså det vi i dag kalder "Smart TV", men det var for omfattende til at kunne bruges til det formål i starten af 1990'erne. Ifølge Wikipedia var hoveddesignprincipperne:

There were five primary goals in the creation of the Java language:

  1. It must be `"simple, object-oriented, and familiar".
  2. It must be "robust and secure".
  3. It must be "architecture-neutral and portable".
  4. It must execute with "high performance".
  5. It must be "interpreted, threaded, and dynamic".
Flemming G. Jensen

I Java 9 er der tilføjet funktionalitet, der gør det muligt at instanstiere immutable collections og maps med få elementer ved hjælp af simple metoder, se JEP 269: Convenience Factory Methods for Collections.

Dit Set-eksempel vil komme til at se nogenlunde sådan her ud:

-> Set<String> set = Set.of("foo","bar","baz")  
|  Added variable set of type Set<String> with initial value [foo, bar, baz]  
   
-> set.add("bazz")  
|  java.lang.UnsupportedOperationException thrown  
|        at Collections$UnmodifiableCollection.add (Collections.java:1056)  
|        at (#22:1)

Formålet med tilføjelsen er, at lette udviklerens tilværelse en smule uden at ændre implementeringen af sproget med den fulde funktionalitet af Collection Literals. I JEP 269 bliver det yderligere begrundet, hvorfor sprogdesignerne har fravalgt Collection Literals i den næste version af Java.

Jonas Høgh

There were five primary goals in the creation of the Java language:

  1. It must be "simple, object-oriented, and familiar".
  2. It must be "robust and secure".
  3. It must be "architecture-neutral and portable".
  4. It must execute with "high performance".
  5. It must be "interpreted, threaded, and dynamic".


Tak for googling, Torben :)

Jeg synes det er sigende, at disse principper primært omhandler platformen JVM. Rent sprogdesign-mæssigt er det reelt kun punkt 1, der er rigtigt relevant. Og det kogte man så ned til "C++ er velkendt og objektorienteret, hvis nu vi fjerner rigtigt grimme ting, indtil det er simpelt, er der nok ikke nogen, der opdager, at det der er tilbage, heller ikke er ret kønt..."

Torben Mogensen Blogger

eg synes det er sigende, at disse principper primært omhandler platformen JVM. Rent sprogdesign-mæssigt er det reelt kun punkt 1, der er rigtigt relevant.

Sikkert, trådet og dynamisk er vel også sproglige features, selv om de påvirker designet af den underliggende platform. Robust til nød, da f.eks. "Det skal ikke være muligt at følge referencer til lager, der ikke er allokeret og initialiseret" handler om robusthed, men skal reflekteres i sprogdesignet. Men jeg giver dig ret i, at det ikke giver mening at designe et sprog til at være effektivt eller fortolket. Selv meget dynamiske sprog kan implementeres med oversættelse i stedet for fortolkning, selv om fortolkning klart er den nemmeste model for meget dynamiske sprog.

Det er i øvrigt sigende, at selv om JVM var designet til fortolkning, så bruger de fleste moderne implementeringer oversættelse.

Ivan Skytte Jørgensen

Og det kogte man så ned til "C++ er velkendt og objektorienteret, hvis nu vi fjerner rigtigt grimme ting, indtil det er simpelt, er der nok ikke nogen, der opdager, at det der er tilbage, heller ikke er ret kønt..."


Jeg vil ikke umiddelbart sige at Java er ukøn, men de fravalgte features, som fandtes i andre sprog på det tidspunkt. Nogle af dem er senere blevet tilvalgt, f.eks. generics/templates, reflection, private strukturer/inner classes.

Mængden af features i et sprog er endeligt, mens mængden af potentielle men ikke understøttede features er uendelig, så der vil altid være mangler i et sprog. Spørgsmålet er hvad man en gang imellem savner, og er bøvlet at kode selv. For Java vil jeg sige: multipel nedarvning og preprocessor. Begge features kan misbruges,men de kan også bruges effektivt.

Noget som jeg generelt savner i sprog (undtagen yacc) er kompakt og letlæselig implementation af tilstandsmaskiner. Det kunne da være fedt at blot kunne skrive noget i stil med dette for en kaffemaskinekontroller:

fsa kaffemaskine {  
off (on) -> preheating  
  //tænd varmelegemet  
  //tænd 'on' lysdioden  
preheating (goal-temperature-reached) -> ready  
  //tænd 'klar' lysdioden  
ready (brew) -> brewing  
  //åbn for vandet  
  //tænd timer  
ready (off) -> off  
  //sluk varmelegemet  
  //sluk 'klar' lysdioden  
brewing (off) -> off  
  //sluk for vandet  
  //sluk for varmelegemet  
  //skyl restvandet ud  
  //sluk 'klar' lysdioden  
  //sluk 'on' lysdioden  
...

og så blot:

if(gpio[7])  
  kaffemaskine.handle_event(on)

Se dét er en feature jeg gerne så i C/C++/Java/Python/COBOL/shell/...
Ja, man kan sagtens implementere det selv i ovennævnte sprog, eller bruge biblioteker til det, men jeg har aldrig set det direkte understøttet i et sprog (undtagen til dels yacc, men det vil måske være at voldtage det).

Jesper Louis Andersen

Mener du ikke "Make illegal states unrepresentable"?

Og det kræver vel nærmest dependent types eller i det mindste GADT.

Ja, du har ret i den citation.

Som Troels skriver, så kan mange illegale tilstande godt elimineres via et typesystem der er stærkere end det Java har, uden at du kommer over i dependent types (eller GADTs). Så det kræver ikke nødvendigvis at du har adgang til et tungt typemaskineri, bare et der er marginalt stærkere end det Java tilbyder. I MLs tilfælde er systemet endda også simplere i sit grundregelsæt, men sproget er mere abstrakt.

Baldur Norddahl

Noget som jeg generelt savner i sprog (undtagen yacc) er kompakt og letlæselig implementation af tilstandsmaskiner.

Det er da nemt nok i mange sprog?

// Scala  
object Kaffemaskine {  
  trait Event  
  case object On extends Event  
  case object GoalTemperatureReached extends Event  
  case object Brew extends Event  
  case object Off extends Event  
   
  trait State  
  case object OffState extends State  
  case object Preheating extends State  
  case object Ready extends State  
  case object Brewing extends State  
   
  var state: State = OffState  
  def handleEvent(event: Event): Unit =   
    state = (state,event) match {  
      case (OffState,On) => Preheating  
      case (Preheating,GoalTemperatureReached) => Ready  
      case (Ready,Brew) => Brewing  
      case (Ready,Off) => OffState  
      case (Brewing,Off) => OffState  
    }  
  }  
}

ML og Haskell gør det naturligvis endnu mere koncist og elegant.

Jonas Høgh

Sikkert, trådet og dynamisk er vel også sproglige features

Jo, du kan vel sige at pointere er udeladt fra sproget af sikkerhedsårsager.

Mig bekendt er den eneste understøttelse for tråde på sprogniveau i Java synchronized nøgleordet. Det er lidt syntaktisk sukker for at gå ind og ud af en monitor, med en finally block. I øvrigt inviterer det til deadlocks fordi monitoren oftest er objektet selv, hvilket gør det umuligt at kontrollere, hvem der ellers låser på det.

Jeg ved ikke hvad der præcist mentes med dynamic, det er jo et temmeligt overloaded begreb, men eftersom Javas typesystem er statisk, antog jeg at der refereres til en egenskab ved JVM.

Ivan Skytte Jørgensen

[tilstandsmaskiner]

Det er da nemt nok i mange sprog?


[snip: pæn Scala implementering af en tilstandsmaskine]

Der er forskel mellem at let kunne implementere tilstandsmaskiner (med patternmatching i dit eksempel), og at sproget direkte understøtter tilstandsmaskiner. Hvis sproget understøtter det direkte, så vil jeg forvente at oversætteren kan advare om:
- manglende transitioner som gør at tilstandsgrafen er opdelt
- start- og sluttilstande, som ikke er markeret som sådan

Hvis jeg skal være lidt brutal, så vil jeg sige at i dit eksempel så er alt op til nøgleordet "match" boilerplate (nødvendigt fordi Scala er statisk typet), og det kunne skrives mere kompakt i shellscript:

function handle_event() {  
    case ${1}-${2} in  
        off-on) echo "preheating" ;;  
        preheating-goaltemperaturereached) echo "ready" ;;  
        ready-brew) echo "brewing" ;;  
        ready-off) echo "off" ;;  
        brewing-off) echo "off" ;;  
    esac  
}

som også implementerer tilstandsmaskinen vha. patternmatching.

Men jeg tror at vi er nået frem til en feature,somJava (og C/C++/cobol/...) kunne bruge, som vil gøre nogle problemer lettere og mere direkte at formulere: patternmatching

Baldur Norddahl

manglende transitioner som gør at tilstandsgrafen er opdelt
- start- og sluttilstande, som ikke er markeret som sådan

Rent faktisk vil mit program netop give en fejl ved oversættelse fordi der mangler tilstande. Hvis programmet skal være korrekt, skal du enten tilføje de manglende "case" eller lave en wildcard "case _ => foo". Du kan også gøre det delvist med "case ( _ ,Off) => OffState" eller "case (OffState, _ ) => OffState // do nothing". Du kan endvidere gruppere ting ved at lave din states i et nedarvnings hierarki eller ved at implementere flere traits.

Scala fylder ret meget når der skal defineres simple datatyper. I Standard ML og Haskell er det en one liner at definere alle tilstandende. Ja selv Java er faktisk bedre på lige det punkt, idet man kan definere enums på en koncis måde.

Standard ML:

datatype state = OffState | Preheating | Ready | Brewing

Men altså, det er ikke boilerplate netop fordi det er nødvendigt for at oversætteren kan gøre opmærksom på fejl såsom manglende transitioner.

Jesper Louis Andersen

Men jeg tror at vi er nået frem til en feature,somJava (og C/C++/cobol/...) kunne bruge, som vil gøre nogle problemer lettere og mere direkte at formulere: patternmatching

Matching er en eliminationsform i logik. Du skal derfor formentlig også indføre konstruktionsformen som du kan eliminere. Det er de føromtalte algebraiske datatyper. Ellers har du sådan set ikke et konsistent regelsæt. Den klassiske "switch" i C er rimeligt begrænset og en af grundende er at den ikke følges af en tilsvarende form.

Hvis du kigger på transitionsformer og lovlighed, så læner du dig rigtigt hurtigt op af en model-checker for at kunne afgøre om transitionerne er lovlige. Det er interessant, men jeg kender ikke noget mainstreamsprog der udnytter disse ideer. Men er du villig til at approximere lovligheden, så har du masser af muligheder i randomiseret testing, og det er et felt jeg har beskæftiget mig rigtigt meget med i praktiske situationer.

Ivan Skytte Jørgensen

Hvis du kigger på transitionsformer og lovlighed, så læner du dig rigtigt hurtigt op af en model-checker for at kunne afgøre om transitionerne er lovlige. Det er interessant, men jeg kender ikke noget mainstreamsprog der udnytter disse ideer.


Nej, og det er en skam. På laveste niveau er alt bits, bytes, registre, adressegymnastik, og betingede hop. Et programmeringssprog er at tilbyde abstraktioner. 50-60 års erfaring har vist at jo tidligere fejl findes jo billigere er de at rette. Så min holdning er at kompilere skal finde alle de fejl de overhovedet kan i brug af abstraktionerne som de tilbyder. De fleste kompilere vil detekte:

int x = 1/0;

og nægte at oversætte. Nogle vil detekte:

int a[20];  
a[42] = 17;

men hovedparten vil blot advare og lystigt fortsætte med oversættelsen. Jeg synes at det er dumt, for programmet er ikke gyldigt.
For at kompilere kan detekte fejl, så kræver det at sproget hæver sit niveau over bits og bytes, for at ikke blot hævet programmøres produktivitet direkte, men også for at kunne finde ugyldig og tvivlsom brug på oversættelsestidspunktet. Og nogle gange fravælge understøttelse af lavniveauskonstruktioner. F.eks. understøttter java ikke goto, men tilbyder højere abstraktioner (do,while, for, labeled-break). Hvad med f.eks. trådsynkronisering? Et sprog som blot siger "du kan bare selv implementere Dekkes algoritme" lyder dumt. Java tilbyder 'synchronized', men måske var et højere niveau bedre? Hvad med f.eks. Adas rendevouz eller Erlangs message-passing? Måske var de bedre end det, somJava tilbyder nu.

At overlade abstaktioner til libraries og frameworks udskyder en del af fejldetekteringen til kørselstidspunkten, hvilket gør fejlfinding dyrere. På den anden side holder det også sproget lille og lettere at implementere.

Log ind eller opret en konto for at skrive kommentarer