F# development process

 
03 september 2009

Je kunt op verschillende manieren een ingewikkeld stukje software uitwerken. a) In één keer en achteraf de fouten eruit halen. b) Iteratief, met tussentijdse controles. Debuggen achteraf is irritant, vervelend en wat dies meer zij dus is de iteratieve vorm vaak te verkiezen. Met F# kan dit heel eenvoudig omdat we ieder willekeurig stukje code kunnen testen met F#-interactive. Dit geeft ons o.a. de mogelijkheid om verschillende benaderingen te testen en de beste te kiezen. Een ander voordeel is dat we kunnen beginnen met de meest basale / eenvoudige functionele constructie(s) en die verder uit kunnen werken tot een robuust stukje code. Zoals Tomas Petricek (2009: 24) het verwoord: “From simplicity to robustness”. Zo is dat Tomas, niets is heerlijker dan een eenvoudige start. Want, ingewikkeld wordt het vanzelf wel.

Hieronder volgt een voorbeeld van een iteratief ontwikkelproces waarbij we het resultaat van een berekening op ieder [corresponderend] element van twee lijsten willen presenteren in een nieuwe lijst.

step one We schrijven de functie niet in één keer maar beginnen we met een simpele functie die door een lijst heen loopt.

01 // 1. Try to iterate through a list of integers
02 let rec aggregate (l:List) =
03 match l with
04 | [] -> “End of list reached!”
05 | h::t -> aggregate t // seperate (h) head and (t) tail
06
07 // test code
08 > aggregate [1;2;3;4;5];;
09 val it : string = “End of list reached!”

step two Daarna nemen we twee lijsten, tellen de corresponderende elementen bij elkaar op en geven een nieuwe lijst met het resultaat terug.

01 // 2. Process two lists and generate a new one
02 let rec aggregate (l1:List) (l2:List) =
03 match (l1, l2) with
04 |([], []) -> []
05 |(h1::t1, h2::t2) -> (h1 + h2) :: aggregate t1 t2 // add the sum of both to the new list
06
07 // test code
08 > aggregate [1;2;3;4;5] [1;2;3;4;5];;
09 val it : int list = [2; 4; 6; 8; 10]

step three Vervolgens maken we de code robuuster door een extra pattern toe te voegen die een exception opgooit wanneer de lijsten ongelijk zijn en maken we de code generiek. Dit laatste bereiken we door de functie te specificeren die op de twee huidige elementen moet worden toegepast. Het type van deze functie geeft aan dat het twee integers accepteert en een integer als resultaat terug geeft. Dit kan een dusdanige functie zijn maar in F# kunnen we ook een operator meegeven die in wezen ook een functie is.

01 // 3. Make robust (#1) and more generic (#2)
02 let rec aggregate (op:int -> int -> int) (l1:List) (l2:List) = // #2
03 match (l1, l2) with
04 |([], []) -> []
05 |(h1::t1, h2::t2) -> (op h1 h2) :: aggregate op t1 t2 // #2
06 |(_, _) -> failwith “Both lists are not equal!” // #1
07
08 // test code (generic 1)
09 > aggregate (+) [1;2;3;4;5] [1;2;3;4;5];;
10 val it : int list = [2; 4; 6; 8; 10]
11
12 // test code (generic 2)
13 > aggregate (*) [1;2;3;4;5] [1;2;3;4;5];;
14 val it : int list = [1; 4; 9; 16; 25]
15
16 // test code (robustness)
17 > aggregate (+) [1;2;3;4;5] [1;2;3;4;5;6];;
18 Microsoft.FSharp.Core.FailureException: Both lists are not equal!
19 (…)
20 stopped due to error

We kunnen onze aggregate functie zelfs nog verder uitbouwen en hem geschikt maken voor meerdere typen zodat ook een lijst van floats en bigInts verwerkt kan worden. Maar het idee is helder. Start simpel en eindig in robuuste code op een eenvoudige en pragmatische manier.


Werken met ?
Kijk dan bij onze mogelijkheden voor zowel starters als ervaren engineers.


Categorieën: Development

Tags: