Door Tweakers Partners

“Zelfs doorgewinterde developers kunnen leren beter te programmeren”

07-06-2022 • 08:00

87

Wat gebeurt er eigenlijk in je brein als je programmeert? Het is een vraag die Felienne Hermans, associate professor aan de Universiteit Leiden, vrijwel dagelijks bezighoudt. In een talk op de Tweakers Developers Summit, die plaatsvindt op 23 juni, vertelt ze hoe je effectiever code kunt lezen en programmeren. Felienne Hermans

Voor software-development verschijnen voortdurend nieuwe talen. Spannend natuurlijk, want ze bevatten steeds veel nieuwe features. Dat motiveert om te leren, maar vaak is het ook ‘hap-snap’, waardoor de echte diepgang ontbreekt. “Om een programmeertaal echt goed te doorgronden, moet je niet alleen naar de syntax kijken, maar ook naar de achterliggende filosofie”, vindt Felienne. “Onlangs maakte ik bijvoorbeeld mee dat een Haskell-developer mijn Python-syntax ging omschrijven. Weliswaar in Python, maar niet volgens de Python-filosofie. Een programmeertaal leren is echt moeilijker dan je denkt. Je kunt alles wel googelen, maar als dat je denkframe is, kom je niet zo diep. Nieuwe gewoonten en routines inbouwen is belangrijk. Ik programmeer al meer dan twee jaar iedere dag in Python, maar daarbij moet ik nog steeds moeite doen om denkpatronen uit C# en C++ van me af te schudden. De kennis is er, maar zij is nog niet echt geïnternaliseerd.”

Hoe leer je nu echt programmeren? En wat gebeurt er daarbij in je brein? Deze vragen omvatten het werkgebied van Felienne Hermans, die er onder meer colleges over geeft, onderzoek aan wijdt en boeken over schrijft. Haar meest recente boek kreeg als titel ‘The Programmer’s Brain - What every programmer needs to know about cognition’. Naast al deze werkzaamheden vindt ze ook nog tijd om zelf te programmeren, waarbij zij zich met name richt op haar project Hedy. Dit is een in Python en TypeScript gebouwde programmeertaal die vooral is bedoeld om kinderen gradueel kennis te laten maken met de soms ingewikkelde syntax van ‘volwassen’ programmeertalen. “De taal is laagdrempelig en je maakt gaandeweg kennis met de syntax van een geschreven taal. Door de mogelijkheid om in je eigen moedertaal - bijvoorbeeld Nederlands of Chinees - te werken, is de leercurve minder steil voor kinderen die het Engels nog niet helemaal machtig zijn.”

Een getraind brein werkt anders

Felienne vertelt over haar werk als onderzoeker aan de Universiteit Leiden: “Deels is dit cognitief onderzoek, dus over de manier waarop je denkt bij het programmeren. Het andere deel is technisch, want als je weet hoe je mensen kunt leren programmeren, merk je dat niet alle programmeertalen daar even geschikt voor zijn.” De conclusies die ze trekt, hebben lang niet alleen betrekking op kinderen; ook professionele programmeurs hebben baat bij haar werk. Het meest recente boek van Felienne geeft dan ook tal van praktische tips en aanbevelingen. “Je bent in je werk constant aan het leren. Als je begint aan een tweede of derde taal gebeuren er al snel heel andere dingen dan je verwacht, juist omdat je ervaring hebt met een andere taal.”

Veel ervaren programmeurs onderschatten het belang van het langetermijngeheugen, constateert Felienne. “Als het gaat om logica en concepten is het idee vaak dat je die niet zo goed hoeft te kennen, omdat je het toch allemaal kunt opzoeken. Dat is een beetje een misvatting. Vergelijk het met het leren van Duits. Als je geen enkel woord kent, kun je wel een woordenboek pakken, maar ga daarmee maar eens lekker een krantenartikel in het Duits lezen. Dat schiet niet op. Als je de helft van de woorden kent, gaat het al beter en met tachtig procent gaat het prima; dan moet je hooguit af en toe eens een woord googelen. Ervaren programmeurs zitten op dat laatste niveau en vanuit hun situatie geredeneerd hoef je inderdaad niet ‘alles’ te weten. Maar als advies aan bijvoorbeeld een junior-collega die een nieuwe taal moet leren, vind ik ‘zoek het gewoon even op’ niet zo geslaagd als benadering. Als er niet genoeg aandacht is voor het leren van de syntax en de concepten van de taal, is het geen goed advies. Als je het zou volgen, zit je de hele dag in je browser van alles uit te zoeken. Daardoor ben je de flow waarin je zou moeten zitten met programmeren ook wel kwijt.”

Leer beter code lezen

Met de lessen uit het boek van Felienne is het mogelijk om makkelijker een programmeertaal aan te leren, maar ook het cognitieve proces van een ander beter te begeleiden. Dit zou moeten zorgen voor software met minder bugs, maar ook voor minder frustraties over en weer. Met de huidige tekorten op de arbeidsmarkt trekt de IT-sector al jaren zij-instromers aan. “Daardoor zit je bijvoorbeeld als ervaren programmeur met een zij-instromer. Als expert is het soms best lastig je te verplaatsen in iemand die dat niet is. Iets wat voor jou heel goed werkt, werkt voor een beginner niet altijd goed. Dan kun je wel zeggen: ‘Alle documentatie staat op Github, stuur me maar een pull request als je klaar bent’, maar dat gaat ’m waarschijnlijk niet worden. Als junior word je er meestal ook heel onzeker door. Dat is frustrerend, voor beide kanten.”

Developers die weten hoe cognitie en leren programmeren met elkaar interacteren, kunnen daar veel plezier van hebben, betoogt Felienne. Niet alleen bij het begeleiden van een ander, maar ook bij hun eigen werk. Zo gaat ze specifiek in op het lezen van code. “Je scrolt er doorheen, maar hoe bepaal je of iets goed of fout is? Wat is het design en van welke filosofie is uitgegaan? Dat laatste is soms vaag, bijvoorbeeld omdat de documentatie ontbreekt of omdat verschillende programmeurs hebben bijgedragen en niemand het antwoord echt weet. In het ene project is optimalisatie belangrijk en mag dat ten koste gaan van leesbaarheid. Voorbeelden daarvan zijn de Marsrover en een complexe game. Bij zulke projecten heeft het niet zoveel zin als iemand caching gaat verwijderen met het oog op leesbaarheid.” Hoewel de achtergrond van het boek wetenschappelijk is, probeert Felienne voor haar doelgroep heel bondig te zijn. “Programmeurs moeten al zoveel lezen, dus ik geef praktische tips over hoe je een tekst kunt scannen, met daarbij variabelen die je er goed bij kunt gebruiken.”

Schrijf alleen de variabelen op

We vragen Felienne of ze alvast een tip kan geven, vooruitlopend op de Tweakers Developers Summit. “Iets dat ik heel leuk vind en dat ook heel eenvoudig te doen is: negeer alle structuur, de klassen en wat er verder nog gebeurt, in een stuk code dat je nog niet kent. Schrijf alleen de namen van de variabelen op. Doe dit vooral als het gaat om een taal die je niet of niet meer gebruikt. Je komt dan snel in een situatie waarbij het zo oncomfortabel wordt dat je denkt: ‘dit heeft geen zin meer’. Je checkt dan mentaal uit, wat echt zonde is. Maak het vervolgens behapbaar. Wat leer je uit de programmeerconcepten en kun je aan de hand daarvan al zien wat er gebeurt? Als je vervolgens een stukje niet begrijpt, kun je dat ook heel concreet aan je eigen team voorleggen om de betekenis in die context te begrijpen. Dat is een manier om je voorkennis te activeren, zonder dat je die akelige, ellendige code in PHP, C++, Rust, Python of wat dan ook moet bekijken. Het enige dat je nodig hebt, is een leeg Word-documentje of een kladblok. Begin met de concepten en probeer ze te begrijpen. De volgende keer kijk je naar de structuur en daarna naar de verschillende technieken die zijn gebruikt. Zo kom je steeds een stap verder.”

Het moment van ‘uitchecken’ herkent Felienne zelf maar al te goed. “Laatst had ik het bijvoorbeeld nog, toen ik een stuk PHP aan het lezen was, een taal die ik al twintig jaar niet meer gebruik. Ik kan het echter ook hebben bij de Hedy-codebase waarvan ik de hoofd-maintainer ben maar waar ook een stuk of twintig anderen aan werken. Soms is het daarbij alsof iemand anders jouw huis heeft opgeruimd en je niets meer kunt vinden. Als je dat niet wil, moet je het natuurlijk niet op GitHub zetten. Toch is het ook belangrijk dat het opensource is en dat andere programmeurs issues oplossen. Je moet toch even over dat moment heen. Bij programmeren hoort het aanleren van nieuwe technieken, maar een ander deel is ook dat je moet leren dealen met discomfort. Hoe vaker je door zo’n moment komt, hoe meer vertrouwen het geeft dat het je de volgende keer ook lukt.”

De Tweakers Developers Summit voelt als thuiskomen (127.0.0.1). Mis het event op 23 juni niet! Lees via de buttons hieronder alles over het programma, tickets en andere belangrijke zaken.

devsummit

devsummit

Kijk hier voor het privacybeleid van Tweakers: https://tweakers.net/info/algemene-voorwaarden/privacy/

* We volgen het COVID-19-beleid vanuit de overheid dat in juni 2022 voor evenementen geldt. Mocht het evenement in juni niet door kunnen gaan vanwege eventuele maatregelen, dan worden de kosten van je ticket gerestitueerd.

Blokkenschema Dev. Summit 2022

Reacties (87)

87
87
61
4
0
17
Wijzig sortering
De ervaring is dat er meer is dan code. Je gaat geen bugs spotten in code waarvan je niet snapt wat het functioneel doet. Je gaat geen zinnig woord kunnen geven over hoe het structureel anders aangepakt kan worden, of hoe performance bottlenecks aangepakt kunnen worden, als je het grote plaatje niet kunt vatten.

En DAT is de ervaring. Een ervaren programmeur gaat niet naar code staren, maar gaat uitzoomen en proberen uit te dokteren wat de code functioneel doet. Pas als je dat weet en in je hoofd een diagram hebt, ben je in staat om een oordeel te vellen over code. Dit is net zo goed waar voor je eigen code, want niemand gaat langer dan een paar maanden onthouden hoe, wat, waarom eigen code ooit geschreven was.

En de ervaring is niet om het te kunnen... iedereen kan dat. De ervaring is om het snel te kunnen. Als je het snel kan dan ben je al driekwart de ladder op om goed de code van anderen (inclusief jezelf uit het verleden) te kunnen begrijpen. En ja om dat te kunnen doen is de lange termijn geheugen wel belangrijk ja. Als je non-stop naar Google moet grijpen tijdens de analyse dan ga je het niet snel voor elkaar krijgen. De overige kwart is je eigen ego die onder bedwang gehouden moet worden. Want uiteindelijk heeft elke programmeur een mening, maar die mening hoeft niet altijd uitgesproken te worden.
En DAT is de ervaring. Een ervaren programmeur gaat niet naar code staren, maar gaat uitzoomen en proberen uit te dokteren wat de code functioneel doet.
Inderdaad. Dit is de crux van het geheel.

Hoe ga je beoordelen of een lap code correct is, als je nog niet weet welk probleem die code op aan het lossen is? En bij gevolg: hoe weet je of dat probleem überhaupt het juiste probleem is? Heeft de auteur van de code bijv. wel rekening gehouden met alle vereisten?
En ja om dat te kunnen doen is de lange termijn geheugen wel belangrijk ja.
Minder belangrijk dan je korte termijn geheugen, eerlijk gezegd.

Je moet gewoon aardig wat 'head space' hebben om tijdens zo'n analyse alle eindjes in je kop te kunnen houden zodat je zo vlot aan elkaar kunt knopen. Het is een beetje alsof je een legpuzzel in je hoofd aan het leggen bent. Of misschien meer relateerbaar: alsof je een LEGO model aan het disassembleren bent en in je hoofd de bouwhandleiding aan het schrijven bent.

Vanuit lange termijn geheugen helpt het dan wel als je snel bepaalde functionele patronen herkent, maar dat heeft minder met exacte recall te maken dan met (no pun intended) patroonherkenning.

[Reactie gewijzigd door R4gnax op 22 juli 2024 17:36]

Felienne Hermans is een held! Luister maar eens naar de aflevering van de Technoloog waar ze te gast is: https://www.bnr.nl/podcas...-naast-wiskunde-en-engels Ze is zó gedreven, het is echt inspirerend om naar haar te luisteren.
Eigenlijk leer je dit bijna direct als je iemand tegenkomt die programmeert in een "wtf dat is fucking impressive" stijl... en je dat ook wilt kunnen xD Instant reminder voor me dat ik mezelf nog kan verbeteren. Een goede tip van zo'n iemand was om design patterns te leren. Beetje werken op een manier dat iedereen zint met onafgesproken methodes die over de jaren gewoon goed bleken te werken (voor elkaar ook). Dat heeft m'n ogen destijds geopend en me een open mind gegeven om nooit uitgeleerd te zijn.
Ach van alles geldt gebruik het met mate waar nodig. Ik heb eens een stuk code van iemand gezien van een factory patern uitgewerkt tot in de puntjes in 26 files met tests en al. Toen ik uiteindelijk de implementatie had gevonden ging het om 10 regels code. Dit was ook de enige implementatie in al die files, de rest was dus plumbing en tests van het design pattern.

Fouten die veel programmeurs maken zijn:
  • Alles op de toekomst willen voorbereiden waardoor ze te veel plumbing code maken.
    Maak alleen wat je nodig bent,
  • Alles in hetzelfde pattern doen.
    Gebruik het geschikte pattern voor de code.
  • Alles volgens het laatste truckje doen (abstract classes, Linq statements, jQuery etc.)
    Gebruik het alleen indien nodig, of als het het overzichtelijker maakt
  • Geen dubbele code willen hebben tot op het punt dat er procedures zijn met meer dan 5 parameters voor if statements in die functie.
    Maak meerdere kleine functies, roep die aan vanuit verschillende functies, dat geeft een beter overzicht van de flow.
Ik programmeer inmiddels zo'n 35 jaar en ook ik maak fouten. Soms kom je later tot betere inzichten. Refactor als het nog kan, als het al ingebruik is of als er meerdere mensen met de code bezig zijn overleg dan eerst voor het refatoren.

Switchen van taal is lastig, maar ook binnen een taal veranderd er veel. Zeker in Javascript, sinds de chrome v8 compiler is dat ook bloedsnel als je de code schrijft zoals je in c zou doen. Iets waar veel javascript programmeurs nog van kunnen leren. Typescript helpt hier bv al een heel eind mee.

Maar vooral het KISS princiepe is belangrijk en wordt veel te vaak genegeerd.

[Reactie gewijzigd door PuzzleSolver op 22 juli 2024 17:36]

die snelheid komt van WebAssembly en is geimplementeerd in de V8 engine.
WebAssembly komt voort uit emscripten, chrome v8 is ouder dan dat. Firefox had eerst webassembly support toen chrome nog volhield dat alle javascript snel moest zijn.

In die tijd heb ik nog WebAssembly zonder compiler gemaakt, dat kwam erg precies voor Firefox die daarna de webassembly even snel uitvoerde als chrome die het toen nog als gewoon javascript behandelde.Dit was ergens in 2015.

Inmiddels is er veel veranderd en support chrome ook webassembly.
Taalspecifieke implementaties en compiler gerelateerde zaken zijn niet hetzelfde. Een interpreter is nou eenmaal niet hetzelfde als een compiler.

Maar goed dat er eindelijk wat bottlenecks uit die browser engines verdwijnen. Ikzelf heb enkel events en een grunt workflow nodig en gebruik alleen typescript in uiterste noodzaak. De Javascript taal blijft een inferieure workflow. Dan kan je tegenwoordig beter Go of Rust pakken of zo. Of Haskell. Of "gewoon" Python, wat lekker academisch is en goed aansluit op veel C++ workflows.

[Reactie gewijzigd door Bulkzooi op 22 juli 2024 17:36]

En javascript is al lang niet een interpreter meer. Sinds de v8 engine wordt javascript met JIT gecompileerd en die compiler optimaliseerd ook best goed. Zelfs zo goed dat image code die ik eerst in de backend had geschreven in C# sneller draaide in javascript. Dat was ook in 2015, wat ik dus wou zeggen is dat sinds die tijd javascript net zo snel kan zijn als de andere talen mits je geen types wijzigt of variabelen runtime aanmaakt zodat de JIT opnieuw afgaat.

De javascript taal blijft de primaire taal van de browser en mits goed gebruikt of b.v. doormiddel van typescript en/of de nieuwere module structuur net zo goed als elke andere taal. Voor 2012 deelde ik je mening nog over de inferieuriteit, voor es6 en de modules was de taal ook niet echt compleet, maar hedendaags is javascript net zo goed of slecht als andere talen.

Verder draait het gebruik van een taal meer om de toepassing die je wilt maken. Python is prima voor AI inference en Raspbery Pi projecten. C++ voor high-speed backend/drivers/embedded werk. Als je Go of Rust pakt ipv javascript in de browser zadel je jezelf direct op met het handicap dat je niet direct bij alle javscript API's kan en altijd indirect met de browser communiceerd. Altijd een extra build step, terwijl je met javascript direct kan debuggen en editten in de omgeving waar de code draait. Vooral in dat laatste is javascript superieur, aanpassingen binnen een functie kunnen zonder herstart uitgevoerd worden, ideaal als je aan het prototypen bent of als je net die ene bug situatie hebt gerepliceerd.

En het feit dat je Haskell noemt geeft mij direct een indicatie op welk nivo je zit, dat wordt altijd genoemd door personen die nog niet erg lang programmeren, ik ben het nog nooit nodig geweest.
@PuzzleSolver
Misschien was het duidelijker als ik had getypt ''een inferieure programmeerervaring".
Javascript is een interpreter
Nee, Javascript is een taal die geïnterpreteerd wordt. tja...

Anders maak een tijdlijn van Javascript, met lancering van features. Ik heb enkel events nodig en de inefficiënte van browsers altijd verafschuwd. Geldt trouwens ook voor latency op de DOM.

Dus ik heb nooit een reden gehad om verder dan PERL te zoeken; daar zat zelfs een superhandige IDE bij. Zowel Javascript en PHP heb ik nooit als volwaardige talen beschouwd, hoewel nieuwe features, Typescript en betere IDE's de situatie om websites te maken steeds iets beter maakte. (ook bv. Frontpage van Microsoft was een zwaar ondermaatse ervaring tov Visual Studio toendertijd)

Vanwege syntactische redenen gebruik ik voor een beetje adhoc scripting eerder MicroPython dan javascript.

Om mezelf te quoten:
Taalspecifieke implementaties en compiler gerelateerde zaken zijn niet hetzelfde. Een interpreter is nou eenmaal niet hetzelfde als een compiler.

[Reactie gewijzigd door Bulkzooi op 22 juli 2024 17:36]

"Nee, Javascript is een taal die geïnterpreteerd wordt. tja..."

Als je dat op die manier stelt dan wordt elke taal geinterpreteerd, een interpreter is geen compiler. Een interpreter leest de code en roept dan routines aan om die code uit te voeren. Veel interpreters zetten al sinds de jaren tachtig code eerst om in bytecode, vervolgens wordt die byte code geinterpreteerd en stap voor stap uitgevoerd alsof het een array van functies is die aangeroepen moet worden.

Byte code (of IL) wordt in talen als C# (eigenlijk MSIL op dat moment) en Java vervolgens via JIT (Just In Time) gecompileerd naar assembly code. WebAssembly doet een soortgelijk iets tegenwoordig en deze IL wordt steeds uitgebreider waardoor het begint af te wijken van het originele javascript en meer voordelen begint te bieden. Helaas heeft het ook nog steeds nadelen tov javascript.

Javascript wordt tegenwoordig gecompileerd volgens het JIT principe. Er wordt dus assembly code voor gegenereerd en die wordt uitgevoerd. Dat maakt het dus niet meer een taal die geinterpreteerd word.

Verder is een taal maar wat je er van maakt in dit geval, Frontpage was idd een draak destijds maar javascript is veel gegroeid sinds die tijd, vooral de laatste 10 jaar. Python heb ik zelf weer weinig ervaring mee, wel eens gebruikt voor een hobby project in combinatie met een C daemon.

Als ik iets moet scripten of testen pak ik wat voorhanden is. Op een Raspberry PI grijp ik al snel naar C omdat ik dat ken en er alles voor te vinden is. Python is daar ook best handig maar zou ik alleen gebruiken als ik een plug-in moet maken voor een Python framework zoals OctoPrint of Mopidy. PHP kan ook handig zijn voor kleine server scriptjes.

Voor kleine snelle tests van algoritmes gebruikte ik vroeger vaak C#, maar tegenwoordig bevalt javascript me goed genoeg. Als ik op Linux zou draaien en veel Python had gedaan had ik dat gebruikt. Het maakt niet de ene omgeving superieur aan het andere, dat is een fout die individuele programmeurs nogal vaak maken.

De tool die je kent is voor jouw op dat moment het beste todat het dat niet meer is omdat dingen veranderen of de tool niet meer toereikend is voor je wensen.
Een JIT oplossing zoals in JS noemen we nog steeds een intrepreter omdat het hele ding gewoon nog steeds source code uitvoerd. Een compiler daarin tegen zet alleen source code om in andere source code en voert deze niet uit. Dus C# naar een DLL is een compiler, Java naar een jar is een compiler, Rust naar webassembly is een compiler, maar JS in je browser (of via node) is nog steeds een interpreter omdat het code uitvoert.

De tussen stap is puur het interne design van de interpreter, en veranderd niets aan wat het doet of waar het voor gemaakt is. Iets wat source code uitvoerd is een interpreter en geen compiler.

[Reactie gewijzigd door RagingPenguin op 22 juli 2024 17:36]

@RagingPenguin Dat jij dat zo noemt betekend niet dat het zo is, als je zoekt op de definitie van interpreter dan is er 1 ding dat steeds terugkomt namelijk stap voor staop uitvoeren, dat gebeurt dus niet meer met javascript.
a program that can analyse and execute a program line by line.
Interpreter. An interpreter translates code into machine code, instruction by instruction - the CPU executes each instruction before the interpreter moves on to translate the next instruction. Interpreted code will show an error as soon as it hits a problem, so it is easier to debug than compiled code.
In computer science, an interpreter is a computer program that directly executes instructions written in a programming or scripting language, without requiring them previously to have been compiled into a machine language program
Jij verander nu de definitie naar of de source code moet worden meegeleverd of niet.

Het maakt mij eigenlijk ook niet uit hoe je het noemt, het punt wat ik wou overbrengen is dat javascript tegenwoordig net zo snel is als gecompileerde talen als je de sourcecode zo opzet dat her geen hercompilatie nodig is. Dit is iets wat WebAssemblies javascript target in het begin ook gebruikte, hierdoor hoefde Google in het begin geen webassembly implementatie te gebruiken, hun javascript was nl. net zo snel als de webassembly implementatie van FirfeFox.

Als je een functie schrijft met variabele definities in het begin, nette code die niet de types veranderd dan compileerd de chrome compiler deze 1 malig naar assembly en voerd dat vervolgens uit, niet stap voor stap. Doe hetzelfde met een class en de hele class wordt 1 malig gecompileerd naar assembly en vervolgens op die manier gebruikt, net als de DLL uit je voorbeeld is de class nu een library in assembly met functies en vind er geen stap voor stap analyse meer plaats.
Beetje flauw om te gaan cherry picken in een bron die je verder niet noemt. Maar goed, hier is de rest over de interpreter:
An interpreter does not create an independent final set of source code - source code is created each time it runs. Interpreted code is slower to execute than compiled code.

Interpreted languages include JavaScript, PHP, Python and Ruby. (...)
Behalve dat JS letterlijk als voorbeeld wordt genoemd, stellen de eerste twee zinnen exact wat ik zei: een interpreter neemt de source code als input en voert deze uit. Hier kunnen stappen tussen zitten, maar de crux zit hem in het uitvoeren van code.

En al pakken we de rest van die tekst over compilers dan wordt het nog duidelijker:
A compiler translates the whole program into machine code before the program is run. It can be difficult to test individual lines of compiled code compared to interpreted languages as all bugs are reported after the program has been compiled.

The machine code is saved and stored separately to the high-level code. Compilation is slow but machine code can be executed quickly.

Java and C++ are compiled programming languages. Java is a high-level programming language which is compiled to produce bytecode which is then interpreted by a virtual machine (VM). Bytecode is code which is compiled and can then be interpreted.
Een compiler maakt vertaald source code naar een ander soort source code en slaat deze op. Een compiler runt deze code niet, daar heb je iets anders nodig.

Je conclusies zijn dan ook behoorlijk scheef. Een computer voert instructies altijd stap-voor-stap uit. Dat heeft verder niks te maken met gecompileerd vs geïnterpreteerd, maar is het gevolg van de Von Neumann-architectuur die praktisch alle moderne computers gebruiken. Dat staat verder ook los van of er een IL is of niet, een IL is ook gewoon een (geïnterpreteerde) programmeertaal, enkel zijn deze op zo'n manier ontworpen dat ze efficiënter zijn om te interpreteren. Voor je dotnet of java runtime is een DDL of een jar gewoon source code, net als webassembly is voor een browser.

JIT en AOT verhaal slaan hier ook maar zijdelings op. Een JIT is een compiler (eigenlijk een transpiler) die contant (IL) code optimaliseert op basis op data van een runtime. Bij bij een JIT heb je altijd over een interpreter, aangezien je je geoptimaliseerde IL ergens ingeladen moet worden en dat ding moet ook data hebben over de code die het runt. Dat kan niet als je een gecompileerde taal hebt zonder controle over wat je excludable uiteindelijk uitvoert.

Een runtime in een interpreter kan een virtuele machine zijn die geheugen emuleert en alles high-level uitvoert, of het kan delen aan machine code genereren en deze door het OS laten uitvoeren. Dat laatste is denk wat je nu voor een compiler wil laten doorgaan, maar dat is fout aangezien er geen executable wordt genereert die je los kan uitvoeren. Je hebt nog steeds de runtime die als enige de source code kan uitvoeren, en de gegenereerde machine code heeft geen betekenis zonder de context van de interpreter.

Toevoeging: kijk ook gewoon naar wat je bron is, de doelgroep van Bytesize zijn kinderen. Hun uitleg is niet zo zeer verkeerd, maar wel versimpeld tot op het moment dat de details niet helemaal correct zijn. En die details ben je nu aan het invullen met je eigen interpretatie om tot een conclusie te komen over iets waar de beste beste wetenschappers in het vakgebied nog geen absolute antwoord op kunnen geven.

[Reactie gewijzigd door RagingPenguin op 22 juli 2024 17:36]

@RagingPenguin En daar hebben we nog een die alles zo goed weet en gaat hameren over de definitie en me gaat aanvallen er over. De rest van het wikipedia artikel legt ook al uit dat het niet meer zo eenduidig is, en je cherrie picked zelf ook.

Dus je geeft een eindeloos lange rant over de definitie en andere definities en gaat niet in op de inhoud. Ik zeg ook al dat het niet uitmaakt, Javascript is tegenwoordig ook snel. Ik heb zelf al compilers geschreven dus ga me alsjeblieft daar niet de les over lezen, lees wat ik schijf en leer ervan ipv op alle slakken zout te leggen.
Sorry, maar dit 1) best wel simpel en 2) je gaat heel erg hard in op een enkel zinnetje in een artikel met een leeftijdscatagorie van 11 tot 16 jaar. Deze zin lijkt het inderdaad met je eens te zijn:
a program that can analyse and execute a program line by line.
Echter al pak je de rest van het artikel dat is het meer in lijn met de gangbare definities. En voor de volledigheid quote ik praktisch het hele artikel, dus ik zie niet echt hoe ik zou kunnen cherry picken?

Maar goed, laten we dan kijken hoe wikipedia dit formuleert:
In computer science, an interpreter is a computer program that directly executes instructions written in a programming or scripting language, without requiring them previously to have been compiled into a machine language program. An interpreter generally uses one of the following strategies for program execution:
  • Parse the source code and perform its behavior directly;
  • Translate source code into some efficient intermediate representation or object code and immediately execute that;
  • Explicitly execute stored precompiled bytecode[1] made by a compiler and matched with the interpreter Virtual Machine.
Lijkt zowel redelijk ondubbelzinnig als duidelijk te stellen dat een interpreter source code uitvoert (“de source code moet worden meegeleverd”) er prima tussen stappen kunnen zijn die efficiënter zijn dan een directe tree walker. Het laatste item stel ook duidelijk dat het een interpreter is die byte code uitvoert. JS is hier een implementatie van strategie 2, iets praktisch elke moderne interpreter is.

En dan een compiler:
In computing, a compiler is a computer program that translates computer code written in one programming language (the source language) into another language (the target language). The name "compiler" is primarily used for programs that translate source code from a high-level programming language to a lower level language (e.g. assembly language, object code, or machine code) to create an executable program.
Stelt ook duidelijk wat ik zei, een compiler zet source code om in een executable die je los kan uitvoeren.

Hierdoor is de scheiding tussen een compiler en een interpreter een harde lijn: al levert het een executable op is het een compiler en al voert het code uit is het een interpreter. Talen zijn niet perse enkel gecompliceerde or interpreted, je kan over het algemeen voor iedere language specification zowel een compiler als een interpreter schrijven.

De reden waarom ik (en waarschijnlijk anderen) hierop zout leggen is dat dit voor mij de core van je claims lijkt:
En javascript is al lang niet een interpreter meer. Sinds de v8 engine wordt javascript met JIT gecompileerd en die compiler optimaliseerd ook best goed. Zelfs zo goed dat image code die ik eerst in de backend had geschreven in C# sneller draaide in javascript. Dat was ook in 2015, wat ik dus wou zeggen is dat sinds die tijd javascript net zo snel kan zijn als de andere talen mits je geen types wijzigt of variabelen runtime aanmaakt zodat de JIT opnieuw afgaat.
Zowel de claim dat JS niet meer geïnterpreteerd is als de claims over een JIT zijn fundamenteel onjuist. Een JIT is niet afhankelijk of je types wijzigt of variable aanmaakt, dat is gewoon het standaard gedrag van je programma. Wat een JIT doet is de IL naar machinecode omzetten op het moment dat de runtime hierom vraagt. Naast dat dit überhaupt niks te maken heeft met de stap van source code naar IL, is er niets in je programma wat de JIT “opnieuw af laat gaan”.

Of de runtime bepaald machinecode uit de cache te gebruiken of opnieuw door de JIT te laten genereren hangt af van de implementatie van de runtime, niet wat de runtime runt. De meeste runtimes met een JIT feature cachen alles tot er of te veel geheugen gebruikt wordt (niet super relevant op moderne computer) of er genoeg profiling data is verzameld om een beter geoptimaliseerde implementatie te genereren.

JavaScript is een stuk sneller dan het was, maar dat ligt hier niet aan. De grootste verbeteringen in V8 waren hidden classes, tagged en gecomprimeerde pointers, IC, een redesign of hoe promises werken en meer runtime types met een betere GC. V8 is vooral een flink moderne runtime dan de meeste JS implementaties waren op dat moment, maar vergeleken met andere interpreters is niet echt iets heel anders dan andere doen.

Verder, deze quote is ook fundamenteel onjuist:
Doe hetzelfde met een class en de hele class wordt 1 malig gecompileerd naar assembly en vervolgens op die manier gebruikt, net als de DLL uit je voorbeeld is de class nu een library in assembly met functies en vind er geen stap voor stap analyse meer plaats.
Webassembly is een taal gebaseerd op S-expressies. Terwijl S-expressies super efficiënt zijn om te interpreteren, moet je ze nog steeds parsen (ook in binary .wasm formaat, dat het allemaal nummertjes zijn veranderd dit niet, het is enkel sneller om te parsen dan tekst). Een S-expressie bevat gewoon nog steeds high-level concepten zoals functies, parameter, return types, if-statements etc. Lisp is bijvoorbeeld een high-level taal waarin je direct in S-expressies programmeren.

[Reactie gewijzigd door RagingPenguin op 22 juli 2024 17:36]

@RagingPenguin

https://en.m.wikipedia.org/wiki/V8_(JavaScript_engine)

V8 compiles ECMAScript directly to native machine code using just-in-time compilation before executing it.[14] The compiled code is additionally optimized (and re-optimized) dynamically at runtime, based on heuristics of the code's execution profile. Optimization techniques used include inlining, elision of expensive runtime properties, and inline caching. The garbage collector is a generational incremental collector.[15]
Als we het over cherry picken hebben... Al zou je die quote beginnen aan het begin de zin ipv in het midden dan staat er opeens iets heel anders.
V8 first generates an abstract syntax tree with its own parser.[12] Then, Ignition generates bytecode from this syntax tree using the internal V8 bytecode format.[13] TurboFan compiles this bytecode into machine code. In other words, V8 compiles ECMAScript directly to native machine code using just-in-time compilation before executing it.[14] The compiled code is additionally optimized (and re-optimized) dynamically at runtime, based on heuristics of the code's execution profile. Optimization techniques used include inlining, elision of expensive runtime properties, and inline caching. The garbage collector is a generational incremental collector.[15]
Dat is een interpreter (met een JIT) die source code omzet in een interne IL, vervolgens zet de JIT delen van de IL om machine code die de runtime uitvoerd. Dat een deel van dit proces iets doet waar je het engelse werkwoord to compile voor gebruikt betekend niet dat het hele ding direct een compiler is (to compile betekend samenstellen of verzamelen en is verder niet een CS specifieke term). Ignition is hier de interpreter die verantwoordelijk is voor het uitvoeren van de source code en TurboFan is wat de JIT van deze interpreter gebruikt om de IL om te zetten in uitvoerbare machine code. TurboFan is niet een compiler, maar een onderdeel van de interpreter.

[Reactie gewijzigd door RagingPenguin op 22 juli 2024 17:36]

Ik heb ook nooit gezecht dat het hele ding een compiler is, alleen dat javscript tegenwoordig gecompileerd wordt. Maar noem jij het maar lekker zoals je zelf wilt, ik houd je niet tegen. Het gaat mij alleen om de techniek. Verder heb je nu C# en Java ook het stempel interpreter opgedrukt. Het is tegenwoordig vaak een combinatie van dingen zoals ik nu al 10 keer probeer te zeggen maar dat komt blijkbaar niet aan of je vind het gewoon leuk om te trollen.
Sorry, maar ik denk dat je een beetje beter moet lezen. Een DLL en een jar worden inderdaad geïnterpreteerd, zoals vrij duidelijk bleek uit het wikipedia artikel:
Explicitly execute stored precompiled bytecode[1] made by a compiler and matched with the interpreter Virtual Machine.
Echter word de stap van C# naar een DLL en van Java naar een jar gedaan door een compiler. Iets wat ik nu volgens mij al 10 keer heb herhaald: een interpreter voert code een uit, een compiler zet het om in andere code. Dus Java is een gecompliceerde taal en Java byte code is geïnterpreteerd. JavaScript in een browser is geïnterpreteerd, want de source code word uitgevoerd. Ik denk niet dat ik het veel makkelijk kan maken.

Verder mag je van mij geloven wat je wilt, maar doe dan niet net alsof je weet waar je het over hebt. En kom dan al helemaal niet aanzetten met elitaire onzin als:
ga me alsjeblieft daar niet de les over lezen, lees wat ik schijf en leer ervan
@RagingPenguin Elitaire onzin? daar begon ik alleen maar mee omdat je me hier continu de les te lezen alsof ik het niet begrijp.

Ik programmer al 35 jaar voor mijn werkt en hobby en ik krijg er ook goed voor betaald omdat ik er goed in ben. Ik weet dat ik het begrijp want ik heb al assembly geschreven voor meerde CPU's, geprogrammeerd in meerdere talen. MSIL instructies via de reflection.emit direct in IL laten schrijven om efficiente serializers te maken. Je lijkt zelf het woord interpreteren belangrijker te vinden dan compileren is de enige conclusie die ik uit je verhaal kan trekken, dus kom niet aan met dat ik het niet begrijp.

Je begint met:
Een JIT oplossing zoals in JS noemen we nog steeds een intrepreter omdat het hele ding gewoon nog steeds source code uitvoerd. Een compiler daarin tegen zet alleen source code om in andere source code en voert deze niet uit. Dus C# naar een DLL is een compiler, Java naar een jar is een compiler, Rust naar webassembly is een compiler, maar JS in je browser (of via node) is nog steeds een interpreter omdat het code uitvoert.
En ik zeg "we" noemen dat niet een interpreter meer, want het zit veel complexer in elkaar dan dat, er wordt in meerdere lagen geinterpreteerd om vervolgens tot een blok op-codes te komen in het geheugen. Dat process van het omzetten van een blok source naar een blok assembly noemen we compileren, het wordt namenlijk samengesteld en niet stap voor stap uitgevoerd. Dit gebeurd dus ook binnen de chrome v8 engine. De linker zit daar ook nog ergens tussen die bijhoud waar alle geheugen addressen staan en op die manier efficiente op-codes kan maken.

Je loopt me continu aan te vallen over definities die al jaren niet meer zo vast liggen als dat, er is namenlijk nogal wat gebeurd na het originele gecompileerde vs geinterpreteerde talen debat van de vorige eeuw. Er wordt geinterpreteed en gecompileerd in heel veel talen tegenwoordig en het eindresultaat is dat veel vroeger ge-interpreteerde talen even snel zijn als ge-compileerde talen. Ik heb ook DLL's en EXE's gemaakt, onder dos al in de jaren 90 met behulp van DPMI en Pascal. DLL's bevatten meestal op-codes, maar sinds .net kan het ook IL zijn. Die IL wordt dan via JIT in op-codes omgezet door de .net CLR.

Ik wil nog wel een poging doen om mijn originele punt uit te leggen:

Javascript wordt eerst ge-interpreteerd naar een symbol tree ook wel een code dom genoemd. Deze wordt vervolgens omgezet naar byte-code die uiteindelijk gecompileerd wordt naar op-codes voor de CPU die daarna wordt uitgevoerd zonder herinterpretatie. Er worden ook address tabellen aangelegd die in een normale build slag voor de linker zijn, deze houden bij waar de symbols(variabelen en functies) in het geheugen staan.

Bij wijzigingen aan de velden in een class (eigenlijk een function met een prototype in javascript) kloppen geheugenaddressen niet meer en wordt de class (of onderdelen ervan die de symbols gebruiken) opnieuw gecompileerd. Dus als je later ergens in een javascript file mijnobject.foo = 'barr' doet en foo was nog geen onderdeel van die class dan trigger je een rebuild van die class.

Uiteraard kan ik het nog uitgebreider gaan beschrijven maar het gaat compleet voorbij aan mijn punt. Javascript kan net zo snel zijn als andere talen mits goed geschreven. Je hebt alleen geen toegang tot bepaalde types of SIMD instructies om verder te optimaliseren, maar ook dat geld voor een hoop andere talen. Er is overigens ooit wel een voorstel voor SIMD voor javascript geweest, maar die is nooit verder gekomen dan experimenteel. WebAssembly schijnt wel SIMD te supporten, maar daar moet ik nog eens opnieuw mee aan de slag aangezien er veel veranderd is sinds 2015 toen ik het voor de laatste keer heb gebruikt.

[Reactie gewijzigd door PuzzleSolver op 22 juli 2024 17:36]

En ik zeg "we" noemen dat niet een interpreter meer, want het zit veel complexer in elkaar dan dat, er wordt in meerdere lagen geinterpreteerd om vervolgens tot een blok op-codes te komen in het geheugen. Dat process van het omzetten van een blok source naar een blok assembly noemen we compileren, het wordt namenlijk samengesteld en niet stap voor stap uitgevoerd. Dit gebeurd dus ook binnen de chrome v8 engine. De linker zit daar ook nog ergens tussen die bijhoud waar alle geheugen addressen staan en op die manier efficiente op-codes kan maken.
De complexiteit van de implementatie van een interpreter maakt niet opeens een compiler. De definities gaan over wat het functioneel doet, niet over of het optimalisatie x of y bevat. En of een omschrijving van iets het werkwoord to compile bevat veranderd hier ook weinig aan. Als ik mijn JS wil compilen dan heb ik niks aan node, ongeacht hoeveel features ze toevoegen. Echter, als ik mijn JS wil runnen dan heb ik wat minder aan bijvoorbeeld js2c. Ter illustratie, de onderstaande zinnen zijn voorbeelden van ‘to compile’ uit de oxford dictionary:
We are trying to compile a list of suitable people for the job.
The album was compiled from live recordings from last year's tour.
The figures were compiled from a survey of 2  000 schoolchildren.
Ik ga er eigenlijk vanuit dat je de onderwerpen hier ook geen compiler noemt, ook al compilen ze dingen.
Je loopt me continu aan te vallen over definities die al jaren niet meer zo vast liggen als dat, er is namenlijk nogal wat gebeurd na het originele gecompileerde vs geinterpreteerde talen debat van de vorige eeuw.
De definitie van een compiler en een interpreter liggen nog steeds even vast als vroeger, aangezien ze enkel een behaald doel omschrijven niet de implementatie. Meer implementatie gerichte termen zoals JIT en AOT hebben inderdaad wel meerdere gangbare definities.
Er wordt geinterpreteed en gecompileerd in heel veel talen tegenwoordig en het eindresultaat is dat veel vroeger ge-interpreteerde talen even snel zijn als ge-compileerde talen.
Ik heb ook DLL's en EXE's gemaakt, onder dos al in de jaren 90 met behulp van DPMI en Pascal. DLL's bevatten meestal op-codes, maar sinds .net kan het ook IL zijn. Die IL wordt dan via JIT in op-codes omgezet door de .net CLR.
En dat wordt dan alsnog geïnterpreteerd. Probeer maar eens een DDL te runnen op een niet-windows platform zonder de dotnet of mono runtime. Is niet mogelijk omdat de IL een geïnterpreteerde taal is waarvoor je de interpreter nodig hebt (iit tot C# wat een gecompileerde taal is, met de IL als output).
Javascript wordt eerst ge-interpreteerd naar een symbol tree ook wel een code dom genoemd. Deze wordt vervolgens omgezet naar byte-code die uiteindelijk gecompileerd wordt naar op-codes voor de CPU die daarna wordt uitgevoerd zonder herinterpretatie. Er worden ook address tabellen aangelegd die in een normale build slag voor de linker zijn, deze houden bij waar de symbols(variabelen en functies) in het geheugen staan.
Een perfecte omschrijven van wat Wikipedia als tweede soort interpreter noemde, en exact in lijn met wat ik al de hele tijd zeg. Niet elke interpreter is een tree of list walker.
Bij wijzigingen aan de velden in een class (eigenlijk een function met een prototype in javascript) kloppen geheugenaddressen niet meer en wordt de class (of onderdelen ervan die de symbols gebruiken) opnieuw gecompileerd. Dus als je later ergens in een javascript file mijnobject.foo = 'barr' doet en foo was nog geen onderdeel van die class dan trigger je een rebuild van die class.
Sorry, maar dit is net als veel van de rest van je reacties een hoop juiste woorden om fundamenteel foutieve dingen te beschrijven. JavaScript heeft inderdaad hidden classes die gebruikt worden o.a. property acces. Echter heeft dit cruciaal niets te maken met het interpreteren van de source code. In de woorden van de V8 developers:
However, in a prototype-based language such as JavaScript it is generally not possible to know classes upfront. Hence, in this case V8, HiddenClasses are created on the fly and updated dynamically as objects change.
Hidden classes zijn niet aanwezig in de bytecode, maar zijn iets wat de runtime genereert. Bij een wijziging aan een object dan wordt er niets gerebuild, er is überhaupt niets in de source code waar die class van afgeleid zou kunnen worden. De hidden class zelf wordt ook niet eens opnieuw opgebouwd, V8 gebruikt een transition tree met de hele historie van een specifieke hidden class. Tot slot, hidden classes houden niet alle geheugenadressen bij maar enkel de zogenaamde fast properties. De andere twee soorten (in-object en slow properties) zitten überhaupt niet op de hidden class.
Every time a new property is added, the object's HiddenClass is changed. In the background V8 creates a transition tree that links the HiddenClasses together. V8 knows which HiddenClass to take when you add, for instance, the property "a" to an empty object. This transition tree makes sure you end up with the same final HiddenClass if you add the same properties in the same order.

(...)

There are three different named property types: in-object, fast and slow/dictionary.
  • In-object properties are stored directly on the object itself and provide the fastest access.
  • Fast properties live in the properties store, all the meta information is stored in the descriptor array on the HiddenClass.
  • Slow properties live in a self-contained properties dictionary, meta information is no longer shared through the HiddenClass.
Wat hier belangrijk is dat hoewel er OP code is, er geen executable wordt gebouwd. Alles wat er wordt gegenereerd heeft enkel betekenis in de context van de runtime die alles managed, en zonder deze runtime kan je niets met deze code. Dit is waarom het een interpreter is niet een compiler, en waarom alle extra stappen en optimalisaties hier niet aan veranderen. Zelfs met een hidden class kan je alsnog niks zonder de runtimes kennis over de slow en in-object properties.

Maar tot slot, je claim die mij het meest stoorde:
vroeger ge-interpreteerde talen even snel zijn als ge-compileerde talen
Dit is een conclusie die niet zo hard kan stellen, er zijn meer dan genoeg voorbeelden van benchmarks die allebij de types als sneller bestempelen. Wat we wel redelijk algemeen kunnen stellen is dat JS behoorlijk slecht is op het gebied van geheugen gebruik, zie bijvoorbeeld deze benchmarks.
De complexiteit van de implementatie van een interpreter maakt niet opeens een compiler.
Maar als er een ingebouwde compiler in de runtime zit is het ook niet meer een interpreter in mijn ogen.

Over de hidden classes heb je gelijk, maar die beheren de memory offsets wat ik een geheugen addres noemde, die hidden class krijgt een extra entry op het moment dat je mijnobject.foo = 'barr' uit mijn voorbeeld uitvoerd. Dit betekend dat sommige addressen in de gegenereerde op-codes niet meer kloppen en herberekend moeten worden. Oftewel de gegenreerde op-codes moeten aangepast worden en dat leverd een behoorlijke performance penalty op.
En dat wordt dan alsnog geïnterpreteerd. Probeer maar eens een DDL te runnen op een niet-windows platform zonder de dotnet of mono runtime.
En dat wordt niet meer geinterpreteerd, ouderwetse DLL's bevatten op-codes, een tabel met alle addresen die aangepast moeten worden aan de plek waar de DLL geladen is en een lijst met functies die aangeroepen kan worden. Deze zijn zelfs zo ver gecompileerd dat het vroeger problemen kon vormen omdat C++ en Pascal anders met de stack omgingen. Dat je ze niet op linux kan laden komt omdat het binary formaat anders is en Linux ook geen system functies op dezelfde addresen heeft als windows. Maar onder Wine kan je ze wel laden.

.Net DLL's bevatten IL en zonder CLR waar de compiler in zit kan je die niet uitvoeren inderdaad. Als je het nu over Java had dan had je nog enigsinds gelijk als je zou zeggen dat de Jar via een type 3 Interpreter (van de wikipedia page over interpreter) wordt uitgevoerd. Java gebruikt idd een VM om de instructies stap voor stap uit te voeren. .Net daarentegen zet de IL om in op-codes. De IL wordt dus gecompileerd naar op-codes.

Of er nu een binary wordt gebouwd heeft niets te maken met compileren of interpreteren. De binary wordt gebouwd door een linker in een ouderwets build process.

Over geheugengebruik heb je gelijk, maar dat heeft meer te maken met dat het een managed taal is met garbage collector. Java gebruikt b.v. ook veel geheugen, .net is iets beter maar ook veel. Overigens kan een goede garbage collector soms ook helpen snellere code te krijgen als er veel kleine objecten zijn.

[Reactie gewijzigd door PuzzleSolver op 22 juli 2024 17:36]

lol, natuurlijk wordt taal geïnterpreteerd. Dat geldt voor computertalen en ook voor menselijke talen. Altijd.
...
De tool die je kent is voor jouw op dat moment het beste todat het dat niet meer is omdat dingen veranderen of de tool niet meer toereikend is voor je wensen.
Klopt. Dus we hebben als tools een interpreter, een compiler en een web dat vele talen kent en dat zo lek is als een mandje. Je kan ook alles in de browser stoppen. Als je uit vendor frameworks komt dan kan het zijn dat je geen keus hebt, maar voor bijvoorbeeld m2m latex workflows was dit geen vooruitgang.

Nogmaals, het is hartstikke leuk dat browsers sneller worden. Niks mis mee en nog goed voor het milieu ook. Overigens is het verhaal van antialiasing en shaders en zo iets complexer. Die features hebben ook een andere historie.

En daarom zei ik: Maak maar een timeline van de ontwikkeling van features van Javascript en noem dan één reden of één moment in tijd op waarom iemand die al PERL kon ook nog Javascript zou leren, terwijl er amper een fatsoenlijke IDE voor was...

[Reactie gewijzigd door Bulkzooi op 22 juli 2024 17:36]

Overigens is het verhaal van antialiasing en shaders en zo iets complexer.
Vanwaar dat we dat erbij halen? Ik schrijf zelf ook al shaders voor WebGL sinds 2012 dus daar weet ik ook genoeg over, idem over antialiasing, maar dat heb ik hier nog niet genoemd.
Het ging over webassembly en interpreteren.
jij haalt er een hele IR laag bij en zegt dat javascript een interpreter is. Dus ik benoem 2 mainstream features benodigt in de browser om bijvoorbeeld text te renderen, aansluitend op mijn eerdere voorbeeld met m2m Latex workflows.

Bovendien maken we geen websites meer maar webapps.

[Reactie gewijzigd door Bulkzooi op 22 juli 2024 17:36]

Het ging helemaal niet over webassembly en interpreteren. Jij noemt javascript een geinterpreteerde taal en dat is al lang niet meer zo. Verder begin je het woord "inferieur" te noemen wat volslagen onzin is, alles heeft gewoon zijn plaats.

Text renderen doet de browser prima voor je, daar heb je geen shaders of AA kennis voor nodig dat handeld het onderliggende systeem voor je af, tenzij je zelf alles op en canvas wil renderen zonder de canvas text functie te gebruiken. Maar dan moet je je ook nog druk gaan maken over RGB of BGR subpixels om cleartype goed te krijgen en die informatie heb je niet in javascript. Het voordeel is dat je juist een browser hebt (en ook OS) die al die dingen al voor je afhandeld.

En de browser is geschreven in C++ wat een prima taal is voor dat doel. Net als dat javascript een prima tool is om applicaties in de browser te schrijven en daar helemaal niet "inferieur" is. Wijs de chrome brouwser naar je dev folder en je hebt zelfs een ontwikkelomgeving, ook niet "inferieur". Ik snap niet waar al die javascript haat vandaan komt.
Het ging helemaal niet over webassembly en interpreteren.
huh? Lees nog eens opnieuw. Deze discussie ontstaat uit mijn initiële toevoeging op jouw proza over programmeren:
die snelheid komt van WebAssembly en is geïmplementeerd in de V8 engine.
Die latex workflow, gebruikelijk in vertaalsystemen, betreft dan dus ook eerder G++, alhoewel er ook meerdere C++ varianten en dialecten ingezet kunnen worden.

En ik zeg niet dat Javascript niet zijn plaats heeft; lol, Google en het web zijn er groot mee geworden. Ik gaf aan dat ik die EcmaScript syntax niet zo prettig vindt. Ook integratie met IDE's is beter geworden, met intellisense en linting en zo. Dus het betreft geen haat maar een functionele afweging. Maar ik kan echt niet meer. Jij wint.

[Reactie gewijzigd door Bulkzooi op 22 juli 2024 17:36]

Agree to disagree zeggen ze dan toch ;-)

Je gaf aan dat de snelheid van Chrome's v8 door webassembly kwam. dat is dus niet zo wou ik zeggen omdat de chrome JIT compiler al sinds 2012 voor die snelheid zorgt door javascript te compileren als het kan en te hercompileren bij wijziging van types etc. Dat heeft dus niets met webassembly te maken, dat haalde jij erbij.

Volgens mij zouden we in het echt een prima discussie met elkaar kunnen hebben, maar via reacties en blijkbaar teveel aannames van bijde kanten is het een beetje ontspoort :).

Overigens was NetScape de uitvinder van Javascript en is Google groot geworden met een search engine die in eerste instantie in Java en Python was geschreven maar geport werd naar C++ behalve de crawlers die bleven eerst in Python, geen idee wat ze nu gebruiken op de backend, waarschijnlijk vanalles.

[Reactie gewijzigd door PuzzleSolver op 22 juli 2024 17:36]

Voor mij hetzelfde, ook voornamelijk hobbyprogrammeur, maar dat werkt bij mij toch anders.Alles C op een *nix-CLI. Multithreaded doe ik alleen aan als je background jobs meetelt. Een eigen "projectmanagement", is een veredelde to-do list, eigen codebase is een directory met functies en structs
In de basis is het alles opzetten met "het moet eerst werken"als uitgangspunt. Dat wordt een shell-script met een loop die alles overeind houdt.
Vervolgens gaan we alles wat beter kan met C vervangen. Achteraf heb ik een checklist voor standaard dingen die mis kunnen gaan maar niet direct te zien zijn: missende bestanden, cpu-belasting moet normaal zijn, memory-leak controle, e.d.
Nadeel hiervan is dat beroepsmatige programmeurs er waarschijnlijk niet veel mee zouden kunnen. Ooit enigzins samengewerkt met iemand om met een Filemaker-systeem te kunnen communiceren, die snapte er helemaal niks van.
De meeste hobbyprogrammeurs zijn vaak sterker dan veel professionele programmeurs. Mensen die het vanuit de hobby doen hebben vaak een doel en dagen hunzelf continu uit om meer te leren. Mensen die het ALLEEN voor hun werk doen doen uiteindelijk vaak hetzelfde truukje. Overigens kan je beide gebruiken in een bedrijf, gezien je ook mensen nodig hebt die zaken afmaken, iets waar de hobbyist vaak minder goed in is.

Ik ben ook vanuit de hobby begonnen en gaan werken op mijn 18e in 1989. Ik deed eerst een elektronica opleiding maar vond computers veel leuker. Praktijk opleiding in IT gedaan (was niet veel toen, ik was daar zelfs les aan het geven) en bij mijn stagebedrijf direct werk gekregen. Dat bedrijf werd succesvol, in 2000 verkocht en toen kwamen de nieuwe managers alles verprutsen.

Veel school stagieres van hogere opleidingen langs gehad waar ik eerst nog tegen opkeek, maar later kwam ik er al achter dat als ze het niet vanuit de hobby deden ze een heel stuk achterliepen op de gemiddelde hobby-ist.

[Reactie gewijzigd door PuzzleSolver op 22 juli 2024 17:36]

Ik zie ook wel hobbyisten dingen doen die ik amper kan voorstellen. Hier in de buurt woont iemand die een bijna-CMS heeft gemaakt in ASP.net op een Windows ME laptop. :+
Maar vooral het KISS princiepe is belangrijk en wordt veel te vaak genegeerd.
Maar ook daarvoor geldt dat je zoiets beter niet direct kan zeggen tegen iemand die het nog moet leren.

Want KISS roepen kan dan als gevolg hebben dat iemand juist 20 totaal verschillende functies schrijft die in principe allemaal hetzelfde doen, want dat is lekker simpel, terwijl het onderliggende idee prima kan worden afgehandeld met 1 functie zonder 5 parameters of andere hacks door het principe iets anders en op een hoger niveau te benaderen.

In mijn ervaring kun je daarom (qua kwaliteit van code) beter focussen op het punt dat 5 if-else statements (of meer) binnen 1 functie snel voor slecht onderhoudbare spaghetti zorgt. Dan ziet men direct waarom het niet verstandig is en kan er ook niet omheen worden gewerkt door bv. alle parameters in 1 object aan de functie door te geven en het alsnog met een enorme if-else of switch-case af te handelen.

Meer waarom, minder hoe.
Ik vermoed dat jij dat met je 35 jaar ervaring wel begrijpt :) maar ik heb het vaak op die manier alsnog zien misgaan.

[Reactie gewijzigd door Stukfruit op 22 juli 2024 17:36]

Klopt, andere programmeurs aansturen is een van de lastigste dingen die er is. Je heb er bij die alles beweren te snappen en het niet snappen en je hebt erbij die honderden vragen stellen terwijl ze het prima snappen. Ik heb liever de laatste soort, maar ze laten beginnen met een afgekaderd stuk code met duidelijke omschrijving, daarna dit goed reviewen en vertellen wat anders kan en aangeven wat ze goed gedaan hebben en voral waarom. Ook dit blijft niet hangen na de 1e keer, dus je zal het process vaker door moeten.

KISS was mer bedoel voor mensen die het al kunnen maar minder ervaring hebben, die schieten nog wel eens door in overstructureren.

Ik heb ooit 1 programmeur in het buitenland aan moeten sturen. Ik was alleen over op een project en ons bedrijf dacht dat dat een goede constructie was. Dit was gewoon echt niet te doen, als ik eindelijk beschreven had wat hij moest maken had ik het zelf al 5 keer kunnen schrijven. Vooral taalbariere en management laag aan hun kant maakte het onmogelijk. Ik heb zelfs uiteindelijk dingen zelf geschreven en opgestuurd met dit was de bedoeling en mijn manager gevraagd om er mee op te houden aangezien het alleen maar tijd koste ipv opleverde.
Klinkt allemaal enorm herkenbaar :Y

En sommige partijen bestaan helaas alleen om geld te innen en niet om zelf te werken.
Herkenbaar hier. Senioriteit komt ook door het open staan voor nieuwe/andere inzichten. Helaas is ego nogal een dingetje in ons vak waardoor mensen die telkens hetzelfde truckje uithalen denken dat ze op het goede pad zitten en het voor het zeggen hebben.
Klopt, dat zie je vaak in bedrijven. Mensen die minder kunnen programmeren maar wel betere social skills hebben en daardoor hoger op de ladder komen. Of nieuw management dat met nieuw personeel aan komt zetten die de boel aan moeten sturen. Dit heeft bij mij tot 2 baanwissels geleid ;)
Mooi geschreven. Eensch!
Enterprise class code is niet altijd beter. Zie deze: https://github.com/Enterp...FizzBuzzEnterpriseEdition

[Reactie gewijzigd door vandijk op 22 juli 2024 17:36]

Helemaal mee eens, zo heb ik een bedrijf compleet stuk zien gaan omdat de manager vooral de code-coverage van tests belangrijker vondt dan de rest. Er wordt binnen enterpises vaak teveel gefocussed op het proces in plaatst van het doel. Het process is een middel om een doel te bereiken, maar ze verheffen het middel tot doel omdat dat makkelijker te managen en begrijpen is voor ze. Overal willen ze het liefst een KPI (Key Performance Indicator) aan hangen zodat ze daarop kunnen sturen.

Dit leid vooral tot een onevenredig aantal tests voor code, onzinnige tests voor code omdat men de coverage KPI moet halen. Deze moeten vervolgens wel onderhouden worden waardoor refactoring van een verkeerd opgezet design (of verkeerd omdat het doel gewijzigd is) enorm veel werk wordt en niet gebeurd. Vervolgens onstaat er lavaflow programming met systeem om systeem heen geprogrammeerd en wordt de code-base tig keer groter dan een simpel design.

Bij een te grote wijziging (bv. origineel framework is niet meer geschikt voor de markt) is het beter om op nieuw te beginnen met een 2e systeem ernaast. Ook dit is er binnen veel enterprise omgevingen niet door heen te krijgen waardoor ze vaak achter blijven. Microsoft heeft het in de jaren 90 wel goed aangepakt door NT naast windows te ontwikkelen, meer enterprises (en ook kleinere bedrijven) zouden daar een voorbeeld aan moeten nemen. Het gaat overigens niet altijd goed, tijdens de Mobile race begon microsoft te laat met een nieuw systeem (naast Windows CE) om het vervolgens 2 a 3 keer in haast te veranderen waardoor ze in mijn optiek veel developers en het mobile platform verloren zijn.
Als hobby programmer in mijn uppie heb ik die mogelijkheid niet om van ervaren of niveau guru experts te leren. Maar je komt online dan al snel die go4 design paterns tegen. En later kom je ook de anti-paterns tegen. Dan ook guidlines en wat je moet mijden.
Dan is software architectuur en solid principes wat dan ook opduiken.
En dan heb gedrocht als C++ waar je te maken hebt met multi paradigma programmeer taal. Waar de syntax voor geen optimaal is en door legacy ook zwaar bloated is.
Bij toepassing van API zoals Directx en Vulkan merk je de programmeer stylen van corporaties en instanties, die aanzienlijk verschillen en dat dat ook gewenning vergt.
Nu ben ik ook aan verdiepen in Swift en Metal een taal die cleaner is maar ook anders aanvoelt.
Naast dat je leert dat inheritance in oop niet defacto standaard oplossing is maar composition aggregation. Pure function zonder side effects. Heden ook programmer methoden je grotere rekening houd met multithreading dus mijden van muteren van shared data en paradigma programmeer stylen guidline toepast die daar rekening mee houden. Naast OOP dus ook functional en data oriented programmeren. In heden is als je ooit van plan bent om ook zwaardere programma te maken is goede correcte start van multithread aware programmeren.

Naast dat zelf ontwikkeling ook bad praktice binnen sluipt en je grotere kans dat slechte methoden aanleert die later weer afleren moet. En je leert uiteraard door intensief te doen. Ik programmeer niet regelmatig en dus vorder ik nogal slecht , met grote gaten periodes van er niet mee bezig te zijn.

Ik pleur in begin ook hoop data in global scope. Lekker makkelijk. Bad. Voor kleine progsels niet zo probleem.

Naast dat veel demo code verre van productie code en nogal versimpeld en geen architectuur.
En dan is de kennis en ervaring om c++ ook goed te kunnen refactoren.

Ik heb niet echt een style. Daar programmeer nog te weinig voor.
Maar voor variabelen naming gebruik ik meerdere talen zoals nederlands duits engels etc wat lekker korts is. Zorgt ook voor minder name conflicts.
Niet goed voor shared of opensource gebruik.

Mijn beperkte ervaring.
GWbasic Z80 machine code 68000 assembler TurboPascal C C++ C# javascript HTML Swift. Beetje kennis van de oudere is reeds vervlogen. Recente C++ en Swift.
Web frontend javascript html php.
Grappig, dat noemt zichzelf hobbieprogrammeur ;-).

Je weet beter hoe het moet dan de meeste "professionele" programmeurs waar ik mee gewerkt heb. Ik ben begonnen met MSX-Basic en Z80 machine code, Turbo Pascal, Delphi, C#, javascript etc. Ook C en C++ gedaan, ben het met je eens C++ heeft veel te veel macro en template systemen die misbruikt kunnen worden en C++ code is vaak een rommeltje daardoor. Ook de reden dat Linus Torvalds het liever op C houd. Overigens ook hele mooie C++ code gezien met templates, o.a. voor de ledstrings in arduino code, daar is het echt op zijn plaats om efficiente resource efficiente code te maken voor verschillende platformen en hardware.

Multi threading programmeren blijft altijd lastig, voor je het weet heb je deadlocks. Vaak wordt een lijst gelocked en dan het item er in ook. Ergens anders wordt het item eerst gelocked en dan de lijst en je hebt al een probleem. Meest belangrijke daar is goede afspraken en eventueel blackboxen in funcie die niet fout kunnen gaan.
Beetje afhankelijk van wat je programmeert, maar ik programmeer al ruim tien jaar professioneel, en ik heb die hele GoF of design patterns in het algemeen nog nooit gehad. Het is dus maar net wat je doet. :-)
C++ is een interessant voorbeeld, want die taal heeft zichzelf zowaar opnieuw uitgevonden sinds de C++11 standaard. Ik kijk er naar vanuit een embedded firmware invalshoek. In dat vakgebied zijn er nog echte puristen te vinden die alleen C willen schrijven, en dan bijvoorbeeld verwijzen naar quotes van Linus Torvalds van voor C++11 waarom C de taal is van de Linux kernel (ondanks dat men in C "handmatig" de features van C++ hebben geimplementeerd, zoals classes).

Nu is dat de (non-)causaliteit van argumenten niet een reden om ze af te zwaaien. Echter argumenten als memory allocation, teveel features/bloatware, etc. zijn m.i. enorm acterhaalt. C kent ook genoeg undefined behaviour waar je jezelf mee kan verwonden (om nog maar te zwijgen van uitzonderlijke situaties waarin iets in C prima mag, maar in C++ undefined behaviour is -- for good reason). Echter sinds C++11 en eigenlijk de laatste standaarden nog meer, komen er steeds meer zaken naar voren die m.i. juiste verrekte handig zijn voor hogere kwaliteitscontrole en/of performance (zekerheid) voor je code.

Daarbij is de metaprogramming wel echt enorm toegenomen. Sommige stukken code of eigenschappen wil je het liefst zoveel mogelijk compile-time genereren ipv run-time, zodat de code die daadwerkelijk op jouw device draait zo min mogelijk overhead heeft. In C worden macro's daarvoor gebruikt, omdat de werking daarvan redelijk stabiel is over verschillende compiler varianten. , Echter aangezien macro's niets meer zijn dan een search-replace text tool, ontbreekt daar enige debugbaarheid en typesysteem informatie van. Vreselijk.
Templates zijn daarintegen weer een draak om mee bezig te zijn: wie met Boost wel eens geprogrammeerd heeft kan zich waarschijnlijk wel GCC foutmeldingen van 2 FullHD monitoren breed/lang voorstellen omdat je ergens een type in een templated functie stopt die niet ondersteunt wordt. Zoiets doet mij terugdenken aan de eerste stappen die ik in Haskell heb gezet.. het vereist ook echt z'n eigen mindset om er goed/vlot resultaat mee te behalen.

Maar het resultaat mag er zijn. Je hoeft niet ver te zoeken, of je kan wel libraries op github vinden van Regex parsers of JSON libraries die compile-time worden gedraaid, zodat jouw uiteindelijke code alleen doet wat er echt moet gebeuren.
Of een spel waarbij de procedural level generation compleet compile-time is geimplementeerd, inclusief RNG. Wil je een nieuw level? Hercompileer je code dan even. :+
Maar uitgeleerd raken over/met programmeren, volgens mij duurt dat nog wel even :)

[Reactie gewijzigd door Hans1990 op 22 juli 2024 17:36]

In het algemeen maakt de taal voor puristen weinig uit; het gaat om afspraken.Maar het is nou eenmaal zo dat C een rijkere historie, vooral zonder object-geörienteerdheid, heeft dan C++. Da's ook de discussie geweest die door IT-land waaide tentijde van LLVM en clang. Hierdoor hebben de meeste projecten hun style-guides aangepast.

[Reactie gewijzigd door Bulkzooi op 22 juli 2024 17:36]

@SG Alle taalproblemen die je benoemd, zijn zo ongeveer de geschreven geschiedenis op een computer, veelal gerelateerd aan object-geörienteerdheid (OOP) en al minstens actueel sinds Edgar Dijkstra zijn rant tegen het goto-statement (1968) publiceerde.
Dat was nogal een precieze en een groot voorstander van lineair programeren, danwel gestructureerd ontwikkelen. De motivering is met de Cloud, IoT & M2M enkel maar actueler geworden hoewel de academische publicatie dus nog in de tijd van het mainframe betrof, dus voor personal en home computing.

Hierbij maakt Dijkstra een reference naar ALGOL en ik ben het wel met hem eens, zeker qua veiligheid. Ondertussen zijn de computertalen zo doorgegroeid, dat er voldoende expressiviteit en creativiteit mee te bereiken is. Maar toen was dat een antwoord van Dijkstra op Amerikaanse linguïst Choam Nomsky, over filosofische discussies, dus academisch en industriebreed, over syntax, grammar en useability en zo. En dan mn. een discussie in de context van personal computers en gericht op object-geörienteerdheid van computertalen.

En qua het aanspreken van Graphics API's kan je bv. ook eens kijken naar pygame. Da's wel een moderne, leerzame suite en omvat ook de gehele grafische scoop.

[Reactie gewijzigd door Bulkzooi op 22 juli 2024 17:36]

Ik heb maar 1 vraag, hoe kan het, als ik tot in de late uurtjes aan het programmeren ben. De volgende ochtend, ik mijn code niet terug kan vinden.
Weet je zeker dat je niet op een ramdrive zat te werken?
Achter af, weet ik zeker. Dat ik in mijn geheugen zat te werken
Tijd voor een zen-moment. Alles weer terughalen via astrale projectie. :+
Schrijfbeveiliging van de floppy?
vi /dev/null? :)
Dan is version controle software handig, en zo ver ben ik nog niet.
Zelf zet comment blok
// mijn code volgt
Edit code // edit gegevens
Of /* orginele code */ of blok
Bedoel je jouw vraag als een werkelijk probleem dat de code weg is? Of filosofisch dat je 's ochtends erachter komt dat de stukken code van gisterenavond op een -zoals je gedachten er nu bij staan- onlogische plek liggen?

Voor het eerste zul je toch echt meer context moeten geven (OS, IDE, programmeertaal, framework, versiebeheer, etc). Want dat kunnen we niet ruiken. En zo'n vraag is meer iets voor in GoT dan op de FP.

Voor het tweede; geestelijke gesteldheid speelt inderdaad een rol bij programmeren. Net als een machine besturen, wat je eigenlijk bij programmeren aan het doen bent.
Relevante XKCD. Ik heb 's morgens ook altijd twee prangende vragen na een avondje beer-to-code-conversion:
- Waar was ik in godsnaam mee bezig toen ik dit schreef?
- Hoe kan het dat deze code naar verwachting werkt?
Vooral dat laatste is altijd een mooie, helemaal als je het met 'echte' code niet meer werkend krijgt en je onleesbare dronken code gewoon perfect werkt :+
Oh god. Dit is herkenbaar.
Nee, drugs hebben er niets mee te maken. Ik denk dat ik niet in slaap moet vallen en door programmeren in mijn dromen. Wat op zich best jammer is, want die code doet het altijd :) (in mijn dromen)
Ik word al gek van mijn eigen code. Laat staan die van andere van een github.
Het helpt om je programma eerst in het grote geheel te bekijken. Wat is het hoofddoel en welke subdoelen helpen daarbij.

Jammer dat de prijzen absurd hoog zijn. Is een rib uit mijn lijf.
Op zich valt 300 euro voor een berg informatie toch best wel mee?
Het is een berg informatie ja, maar je moet rekening houden met hoeveel informatie kan jij opslaan in zo'n korte tijd terwijl je van hot naar her loopt en vele afleidingen je informatie toename beperken.
Daarom maak je aantekeningen tijdens de lezing :-)
Je hoeft niet alles in 'e'en keer op te slaan.
Je kunt ook wachten op de (tweak)blogs van mensen die er al geweest zijn en graag hun bevindingen (en eigen visie erop) delen.
Blah 300 euro!?

Een paar tientjes is prima, netwerken is natuurlijk ook altijd waardevol, maar dit is echt zo'n prijs waarbij je al vanuit je werkgever op pad gestuurd moet worden. Krijg ik echt niet aan mijn vrouw verkocht :+
Ik krijg dit dan wel aan mijn vrouw verkocht, maar vind het zelf gewoon te duur.
Ik verwacht dat mijn werkgever mijn dan liever naar een training voor gevorderden stuurt, ook al is dat duurder, dan naar een seminar voor dit geld.
Wat een vreemde opvattingen: ik hoef niet te leren want Google? Is het echt zo diep gezakt met onze informaticastudenten?

If anything, moet je door Google juist meer leren en begrijpen van de systemen achter de kennis en feitjes.

Als/zodra je in het gegeven voorbeeld, je de duitse grammatica en zinsbouw een beetje snapt dan is woordkennis idd minder belangrijk want die kun je opzoeken

Dan hoef je niet meer zoals vroeger (jaren 60)
Weet u de weg naar de bakker los te zien van de vraag of iemand weet waar de slager is of de kruidenier, het politiebureau of de huisarts.

Maar om nu concepten te gaan googelen 🤯
HBO-ICT leeraren zijn ontzettend blij met de ontwikkeling zoek het zelf maar uit op google :). Volgens mij heb ik in de laatste 3 jaar alleen maar opdrachten gekregen en de rest zelf zitten google. Ik snap dat je als HBO student zelfstanding moet zijn maar, of ik de best practice heb toegepast is een andere verhaal....
Dan hebben we het nog niet eens over de gigantische berg aan desinformatie in artikelen en Youtube filmpjes. Eigenlijk hetzelfde probleem als social media over het algemeen.
Je moet in staat zijn het juiste eruit te pikken, echter als de basisconcepten je niet geleerd zijn ja dat lastig.
Dan krijg he weer die “lollige” grapjes dat men code kopieert van StackOverflow.
Uhh, wat is dat? Ticket garantie van €24.60?
Dat wil zeggen dat ze je bij je enkels ondersteboven willen houden en schudden tot er nog meer geld uit valt.
Anoniem: 428562 7 juni 2022 09:47
Youtube: "Clean Code - Uncle Bob / Lesson 1" vanaf 40 minuten
Juist als ervaren developer weet je toch dat maar alles Googlen en kopieren + plakken geen goede code onderhoudbare code oplevert..
Ik geef toch nog vaak het devies "Zoek het maar op" of "vraag maar als je er niet uitkomt" bij het leren van een taal. Ja, in het begin ben je constant aan het opzoeken en dat verstoort je flow enorm, maar na een tijdje hoef je minder en minder op te zoeken. De andere optie is om eerst een taal (en/of framework, bibliotheek, API) meester te worden door alle documentatie te lezen. Kans is groot dat je op de helft al niet meer weet wat je aan het lezen bent omdat je er niet actief mee bezig bent.

Voor mij is vaak een korte tutorial en dan gewoon in het diepe springen het beste. En dan veel googlen. Dat hoeft natuurlijk niet voor iedereen zo te zijn, en ik heb het voordeel dat ik al vrij veel programmeertalen ken en dus makkelijk de overeenkomsten en verschillen zie.

Een heel nieuw paradigma leren is andere koek trouwens. Python leent wel een beetje van Haskell (sowieso is er een verschuiving richting functioneel gaande in veel talen die ik gebruik), maar het is toch een ander beestje.

Op dit item kan niet meer gereageerd worden.