224 lines
8.7 KiB
Markdown
224 lines
8.7 KiB
Markdown
---
|
|
name: racket-programmeer-skill
|
|
description: Hiermee wordt mijn voorkeur racket programmeerstijl aangegeven.
|
|
---
|
|
|
|
---
|
|
name: racket-programmeerstijl
|
|
description: Gebruik deze skill wanneer je Racket-code voor Hans schrijft, wijzigt, refactort of beoordeelt. Pas de bestaande, eenvoudige en procedurele programmeerstijl toe; voorkom over-engineering en onnodige abstracties. Gebruik deze skill niet voor algemene uitleg over Racket waarbij geen code voor zijn projecten wordt gemaakt of aangepast.
|
|
---
|
|
|
|
# Racket-programmeerstijl
|
|
|
|
Gebruik deze stijl wanneer je Racket-code voor Hans schrijft of aanpast.
|
|
|
|
## Uitgangspunt
|
|
|
|
Het *allerbelangrijkste* uitgangspunt is dat je de programmerstijl van aangeleverde code volgt.
|
|
Als je een zip met een package aangeleverd krijgt via de prompt dan volg je de programmeerstijl die je in de aangeleverde code vindt.
|
|
Wanneer bestaande broncode beschikbaar is, heeft de stijl van die broncode voorrang. Sluit daar zo nauw mogelijk op aan.
|
|
|
|
Schrijf eenvoudige, directe en goed leesbare Racket-code.
|
|
Kies de kleinste oplossing die het huidige probleem netjes oplost.
|
|
Bouw geen abstraheringslaag voor mogelijk toekomstig gebruik.
|
|
En maak geen helpers die alleen maar in de weg staan.
|
|
|
|
## Structuur
|
|
|
|
- Houd modules klein en doelgericht.
|
|
- Splits functionaliteit alleen af naar een private module wanneer die een duidelijk eigen doel heeft.
|
|
- Gebruik voor duidelijke secties bij voorkeur commentaar in deze vorm:
|
|
|
|
```racket
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Supporting functions
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
```
|
|
|
|
of, wanneer dat beter bij de module past:
|
|
|
|
```racket
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Internal state / functions
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
```
|
|
|
|
Voor publieke functies:
|
|
|
|
```racket
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Provided functions
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
```
|
|
|
|
- Houd `provide` en `require` eenvoudig en overzichtelijk.
|
|
- Voeg geen extra framework, wrapperlaag of generieke infrastructuur toe zonder concrete noodzaak.
|
|
|
|
## pre/postcondities
|
|
|
|
Geëxporteerde functies/procedures/classes of functies/procedures/classes die daarvoor duidelijk in
|
|
aanmerking komen, d.w.z. die die provided zijn of naar verwachting zullen worden, moeten gedocumenteerd worden.
|
|
Zowel in een module scribble als in de code zelf. In het engels.
|
|
|
|
In de code zelf: minimaal:
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; goal : <doel>
|
|
; pre : <preconditie(s)>
|
|
; post : <postconditie(s)>
|
|
; [result:] <resultaat en onder welke conditie>
|
|
|
|
Over het algemeen wil je de internals van een functie weten. Hoe werkt het en waarom werkt het zo.
|
|
|
|
; [internals:]
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
Als een functie/procedure/class overduidelijk in aanmerking komt voor 'provide' en hij staat er nog niet in.
|
|
Verzamel dan de lijst en vraag of je ze moet toevoegen.
|
|
|
|
## Procedures en control flow
|
|
|
|
- Geef de voorkeur aan gewone procedures met een direct leesbare control flow.
|
|
- Gebruik `let`, `if`, `when`, `unless`, `begin` en `cond` expliciet wanneer dat de code duidelijker maakt.
|
|
- Maak niet voor iedere kleine stap een aparte helperprocedure.
|
|
- Introduceer geen hogere-orde of functionele constructies alleen omdat dat compacter kan.
|
|
- Gebruik recursie of een named `let` wanneer dat de meest directe oplossing is.
|
|
- Gebruik bij `cond` bij voorkeur deze vorm:
|
|
|
|
```racket
|
|
(cond
|
|
([condition] korte body)
|
|
([other-condition]
|
|
langere body))
|
|
(else ... alleen als het nodig is)
|
|
```
|
|
|
|
## Mate van abstractie vs leesbaarheid
|
|
|
|
Liever concreet dan abstract.
|
|
|
|
Geef expliciete, goed leesbare constructies de voorkeur boven compacte abstracte idiomen.
|
|
|
|
Bijvoorbeeld combinaties als (filter values (list (and condition 'symbol) ...)).
|
|
Schrijf dan liever expliciet (filter (lambda (x) x) (list (if condition 'symbol #f) ...)).
|
|
Vermijd vooral het stapelen van meerdere impliciete idiomen wanneer dat de leesbaarheid vermindert.
|
|
|
|
## Gebruik lambda.
|
|
|
|
Geef de voorkeur aan λ boven lambda.
|
|
|
|
## Waarden en state
|
|
|
|
- Gebruik `#f` als normale waarde voor "niet gevonden", "niet beschikbaar" of "nog niet geïnitialiseerd" wanneer dat natuurlijk past.
|
|
- Expliciete vergelijkingen zoals `(eq? value #f)` zijn prima wanneer dat de bedoeling duidelijk maakt.
|
|
- Houd state eenvoudig. Een gewone modulevariabele zoals `cached-git-exe` is prima wanneer daarvoor geen zwaarder mechanisme nodig is.
|
|
- Gebruik geen parameters, structs, classes of objectlagen wanneer een gewone variabele of procedure voldoende is.
|
|
|
|
## Publieke API
|
|
|
|
- Gebruik `define/contract` voor publieke procedures wanneer een contract nuttige documentatie en controle geeft.
|
|
- Houd publieke procedures klein en voorspelbaar.
|
|
- Verander een bestaande publieke API niet zonder noodzaak.
|
|
- Voeg geen extra publieke functies toe voor hypothetische toekomstige behoeften.
|
|
|
|
## Fouten en interactie
|
|
|
|
- Geef duidelijke en concrete foutmeldingen.
|
|
- Los eenvoudige interactieve invoer lokaal en procedureel op.
|
|
- Maak foutafhandeling niet generieker dan nodig.
|
|
- Als een externe executable of voorziening ontbreekt, meld precies wat ontbreekt en wat de gebruiker kan doen.
|
|
|
|
## Configuratie
|
|
|
|
- Bewaar lokale configuratie in een kleine, afzonderlijke private module wanneer dat de hoofdmodule eenvoudiger maakt.
|
|
- Gebruik bestaande projectvoorzieningen, zoals `simple-ini`, rechtstreeks in plaats van er een extra abstractielaag omheen te bouwen.
|
|
- Dupliceer geen configuratie die al door een extern programma zelf wordt beheerd.
|
|
|
|
## Naamgeving en leesbaarheid
|
|
|
|
- Kies concrete, korte namen die passen bij de bestaande code.
|
|
- Gebruik Engels voor identifiers en technische namen wanneer de bestaande code dat doet.
|
|
- Schrijf comments alleen wanneer ze iets toevoegen dat niet al vanzelf uit de code blijkt.
|
|
- Geef de voorkeur aan een paar duidelijke regels boven een compacte maar moeilijker leesbare expressie.
|
|
|
|
## Vermijd
|
|
|
|
Vermijd zonder concrete noodzaak:
|
|
|
|
- over-engineering;
|
|
- generieke wrappers;
|
|
- extra abstraheringslagen;
|
|
- dynamische `require`-constructies;
|
|
- classes wanneer procedures volstaan;
|
|
- structs wanneer een eenvoudige waarde volstaat;
|
|
- configuratie-objecten of dependency-injectionpatronen;
|
|
- veel kleine helperprocedures die de control flow versnipperen;
|
|
- refactors die alleen bedoeld zijn om code "slimmer" of abstracter te maken.
|
|
|
|
## Werkwijze bij aanpassen van bestaande code
|
|
|
|
1. Lees eerst de omliggende module(s).
|
|
2. Neem naamgeving, inspringing, control-flow-stijl en module-indeling over.
|
|
3. Wijzig alleen wat voor de gevraagde stap nodig is.
|
|
4. Houd bestaande werkende code intact als er geen reden is die te veranderen.
|
|
5. Voeg geen volgende architectuurstappen alvast toe.
|
|
6. Controleer of de oplossing eenvoudiger is dan het probleem; zo niet, vereenvoudig.
|
|
|
|
## Referentiestijl
|
|
|
|
Deze vorm is representatief:
|
|
|
|
```racket
|
|
(define cached-value #f)
|
|
|
|
(define/contract (get-value)
|
|
(-> (or/c path? #f))
|
|
(if (eq? cached-value #f)
|
|
(let ((value (find-value)))
|
|
(set! cached-value value)
|
|
value)
|
|
cached-value))
|
|
```
|
|
|
|
Een wat langere maar direct leesbare implementatie heeft de voorkeur boven een kortere oplossing met meerdere nieuwe abstracties.
|
|
|
|
# Schrijven van testgevallen voor modules/packages
|
|
|
|
Een test die alleen werkt vanuit de development directory, op het development-OS of met de lokale shell/environment is geen geldige package-test.
|
|
|
|
## Racket Package Index / build-service tests
|
|
|
|
Behandel de Racket Package Index/build service als een aparte, strikte
|
|
en onbekende testomgeving.
|
|
|
|
Bij packagecode en tests gelden daarom altijd de volgende regels:
|
|
|
|
- Maak nooit aannames over `current-directory` of de directory van waaruit
|
|
code of tests worden uitgevoerd. Bepaal testdata en paden expliciet en
|
|
relocatable, bijvoorbeeld met runtime paths en tijdelijke directories.
|
|
|
|
- Maak nooit impliciete aannames over het besturingssysteem. Vermijd
|
|
OS-specifieke paden, shells, executables en gedrag, of handel verschillen
|
|
expliciet per platform af.
|
|
|
|
- Maak tests onafhankelijk van lokale environment state. Benodigde
|
|
environment variables moeten expliciet en bij voorkeur geïsoleerd worden
|
|
ingesteld.
|
|
|
|
- Een succesvolle test moet stil en ondubbelzinnig succesvol zijn.
|
|
Laat geen verwachte foutmeldingen naar de echte stdout/stderr lekken,
|
|
omdat `raco test --drdr` en de Package Index dergelijke output als een
|
|
mogelijke test failure kunnen classificeren.
|
|
|
|
- Verwachte foutoutput moet worden gecaptureerd en geassert.
|
|
|
|
- Tests moeten hun eigen tijdelijke state en testbestanden aanmaken en
|
|
mogen geen bestanden, processen, environment changes of andere state
|
|
achterlaten.
|
|
|
|
- Test packagewijzigingen waar mogelijk ook in een omgeving die lijkt op:
|
|
`raco setup --check-pkg-deps` en
|
|
`raco test --drdr --package <package>`.
|
|
|
|
|