Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Wzorce FP: funktory i monady

W zaawansowanym laboratorium Parku Jurajskiego nie zawsze mamy pewność, czy próbka DNA istnieje, czy analiza się powiodła, czy dane są kompletne. Zamiast sprawdzać warunki na każdym kroku (if/else), możemy użyć eleganckich wzorców z programowania funkcyjnego - funktorów i monad. To jak inteligentne pojemniki na próbki, które same wiedzą, jak obsłużyć brakujące lub uszkodzone dane.

Funktory - pojemniki z mapowaniem

Funktor to obiekt (pojemnik), który implementuje metodę

map
. Pozwala stosować transformacje do wartości wewnątrz pojemnika, bez konieczności jej wyciągania.

1// Najprostszy funktor - Box (pojemnik)
2class Box {
3  constructor(value) {
4    this.value = value;
5  }
6
7  map(fn) {
8    return new Box(fn(this.value));
9  }
10
11  toString() {
12    return 'Box(' + this.value + ')';
13  }
14}
15
16// Uzycie - lancuch transformacji
17const result = new Box(8000)
18  .map((weight) => weight / 1000)      // Box(8)
19  .map((tons) => tons + ' tons')        // Box('8 tons')
20  .map((label) => 'Weight: ' + label);  // Box('Weight: 8 tons')
21
22console.log(result.value); // 'Weight: 8 tons'

Maybe - obsługa brakujących wartości

Monada Maybe rozwiązuje problem wartości null/undefined. Zamiast wszędzie sprawdzać

if (x !== null)
, owijamy wartość w Maybe i pozwalamy monadzie obsługiwać brakujące dane:

1class Maybe {
2  constructor(value) {
3    this.value = value;
4  }
5
6  static of(value) {
7    return new Maybe(value);
8  }
9
10  isNothing() {
11    return this.value === null || this.value === undefined;
12  }
13
14  map(fn) {
15    return this.isNothing() ? this : Maybe.of(fn(this.value));
16  }
17
18  flatMap(fn) {
19    return this.isNothing() ? this : fn(this.value);
20  }
21
22  getOrElse(defaultValue) {
23    return this.isNothing() ? defaultValue : this.value;
24  }
25}
26
27// Bez Maybe - pelno ifow
28function getDinoZoneOld(park, dinoId) {
29  const dino = park.dinosaurs.find((d) => d.id === dinoId);
30  if (!dino) return 'Unknown';
31  const zone = park.zones.find((z) => z.id === dino.zoneId);
32  if (!zone) return 'Unknown';
33  return zone.name;
34}
35
36// Z Maybe - elegancki lancuch
37function getDinoZone(park, dinoId) {
38  return Maybe.of(park.dinosaurs.find((d) => d.id === dinoId))
39    .map((dino) => dino.zoneId)
40    .flatMap((zoneId) => Maybe.of(park.zones.find((z) => z.id === zoneId)))
41    .map((zone) => zone.name)
42    .getOrElse('Unknown');
43}

Either - obsługa błędów

Either reprezentuje wartość, która może być sukcesem (Right) lub porażką (Left). To funkcyjna alternatywa dla try/catch:

1class Left {
2  constructor(value) { this.value = value; }
3  map(fn) { return this; }           // Ignoruje transformacje
4  flatMap(fn) { return this; }
5  getOrElse(defaultValue) { return defaultValue; }
6  isLeft() { return true; }
7  isRight() { return false; }
8}
9
10class Right {
11  constructor(value) { this.value = value; }
12  map(fn) { return new Right(fn(this.value)); }
13  flatMap(fn) { return fn(this.value); }
14  getOrElse(defaultValue) { return this.value; }
15  isLeft() { return false; }
16  isRight() { return true; }
17}
18
19// Uzycie w walidacji proby DNA
20function validateSample(sample) {
21  if (!sample.id) return new Left('Missing sample ID');
22  if (sample.purity < 0.5) return new Left('Purity too low: ' + sample.purity);
23  if (!sample.species) return new Left('Unknown species');
24  return new Right(sample);
25}
26
27function processSample(sample) {
28  return validateSample(sample)
29    .map((s) => ({ ...s, processed: true }))
30    .map((s) => ({ ...s, label: s.species + '-' + s.id }));
31}
32
33// Sukces
34const good = processSample({ id: 'S001', purity: 0.95, species: 'T-Rex' });
35good.getOrElse('Error'); // { id: 'S001', purity: 0.95, species: 'T-Rex', processed: true, label: 'T-Rex-S001' }
36
37// Porazka
38const bad = processSample({ id: 'S002', purity: 0.3, species: 'Raptor' });
39bad.getOrElse('Error'); // 'Error' (blad: 'Purity too low: 0.3')
Vai a CodeWorlds