Hvordan punktfri komposition vil gøre dig til en bedre funktionel programmør

Find funktionel JavaScript blev udnævnt til en af ​​de bedste nye funktionelle programmeringsbøger af BookAuthority!

Foto af Hans-Peter Gauster på Unsplash
Punktfri stil - sigter mod at reducere noget af det visuelle rod ved at fjerne unødvendig kortlægning af parameter-argument.
Kyle Simpson i JavaScript-funktion

Overvej den flydende kode:

lad newBooks = books.filter (point => isTechnology (point))

Se nu på den samme kode efter at have fjernet point (parametre / argumenter):

lad nye bøger = books.filter (isTechnology)

Punktfri i listefunktioner

Lad os udføre operationer i en punktfri stil.

Lad os sige, at vi er nødt til at finde teknologititlerne på en liste over bøger, forberede bogobjektet med al information til visningen og sortere bøgerne efter forfatterens navn.

Sådan ser koden ud:

funktion getBooks () {
  return books.filter (isTechnology)
              .map (toBookView)
              .sort (ascByAuthor);
}
// Små funktioner med punkter
funktion isTechnology (bog) {
   return book.type === "T";
}
funktion til BookView (bog) {
  return Object.freeze ({
    titel: book.title,
    forfatter: forfattere [book.authorID] .navn
  });
}
  
funktion ascByAuthor (bog1, bog2) {
  hvis (book1.author  book2.author) returnerer 1;
  retur 0;
}

Tilbagekaldene erTechnology (), toBookView (), ascByAuthor () er små funktioner med hensigtsmæssigt afslørende navne. De er ikke bygget i en punktfri stil.

Koden, der samler alle disse funktioner i getBooks (), er pointfri.

Nedbrydning og sammensætning

Vores naturlige måde at tackle et problem på er at opdele det i mindre stykker og derefter sætte alt sammen igen.

Vi opdeler den større opgave i flere funktioner, der udfører mindre opgaver. Derefter kombinerer vi disse mindre funktioner for at løse det oprindelige problem.

Lad os læse kravene igen:

Vi er nødt til at finde teknologititlerne på en liste over bøger, forberede bogobjektet med al information til visningen og sortere bøgerne efter forfatterens navn.

Vi oprettede:

  • isTechnology () predicate for at kontrollere, om det er en teknologibog
  • toViewBook ​​() for at oprette et objekt med alle oplysninger til visningen
  • ascByAuthorname () for at sortere to bøger stigende med forfatterens navn
  • getBooks () for at kombinere alle disse små funktioner sammen i en punktfri stil
funktion getBooks () {
  return books.filter (isTechnology)
              .map (toBookView)
              .sort (ascByAuthor);
}

Trin mod punktfri sammensætning

Der er ingen yderligere anonym tilbagekald, når du udfører punktfri komposition. Intet funktionsnøgleord, ingen pilsyntaks =>. Alt, hvad vi ser, er funktionsnavne.

  • I de fleste tilfælde trækkes det tilbage i de navngivne funktioner.
  • I enkle tilfælde skal du bare bruge en hjælpefunktion fra værktøjskassen til at oprette tilbagekald på farten. Se f.eks. Funktionen prop ().
  • Skriv koordinatorfunktionen i en punktfri stil.

Små funktioner

Konsekvensen af ​​at skrive kode på denne måde er mange små funktioner, der har til hensigt at afsløre navne. Det kræver tid at navngive disse små funktioner, men hvis det er gjort godt, vil det gøre koden lettere at læse.

Der vil være to slags funktioner:

  • Funktioner, der udfører en opgave: de er rene eller lukningsfunktioner. Normalt er de ikke bygget i en punktfri stil, men har i stedet gode navne.
  • Funktioner, der koordinerer en masse opgaver: At forbinde disse små opgaver i en punktfri stil gør det lettere at læse.

Ikke alt er pointfrit

Jeg sigter ikke mod at have alt pointfrit. Jeg sigter mod point-free på bestemte steder, især når jeg komponerer funktioner.

Delvis anvendelse

Delvis ansøgning henviser til processen med at rette et antal argumenter for en funktion. - kilde

Lad os tage sagen om en funktion isOfType (), der kan filtrere efter enhver bogtype. Se koden nedenfor:

lad newBooks = books.filter (bog => isBookOfType ("T", bog));

For punktfri sammensætning refaktorerer jeg ud til en navngivet funktion og komponerer derefter det hele tilbage.

funktion isTechnology (bog) {
  return isBookOfType ("T", bog);
}
lad nye bøger = books.filter (isTechnology);

I dette tilfælde kan vi forbedre os mere og oprette den nye predikatfunktion i en punktfri stil ved hjælp af delvis anvendelse.

let isTechnology = isBookOfType.bind (null, "T");
lad nye bøger = books.filter (isTechnology);

En punktfri funktion pr. Definition bruger ikke funktionens nøgleord eller symbolet =>.

En predikatfunktion er en funktion, der tager et element som input og returnerer sandt / falsk baseret på, om emnet opfylder en betingelse. - kilde

Punktfri med løfter

Lad os tage sagen om at hente et sæt todos fra serveren, vælge det vigtigste og derefter gengive dem på skærmen.

Nedenfor er koden:

fetchTodos (). derefter ((todos) => {
   lad topTodos = getTopPriority (todos);
   gøre (topTodos);
 }). fangst ((fejl) => {
   console.error ("Der var en fejl:" + error.status);
 });
funktion getTopPriority (todos) {}
funktion gengivelse (todos) {}

Lad os refaktorere det til en punktfri stil. Der er noget andet denne gang. Som du kan se, bruges resultatet af getTopPriority () -funktionen som input til render () -funktionen. Vi kan bruge løftekædningssystemet til at kombinere funktionerne getTopPriority () og render ().

Som vi gjorde før refaktorer vi fangstopkaldet til en navngivet funktion.

fetchTodos ()
  Ringing (getTopPriority)
  Ringing (gengive)
  .catch (handleError);
funktion handleError (fejl) {
  console.error ("Der var en fejl:" + error.status);
}

Hver derefter () kan returnere en værdi, den har brugt som inputværdien for det næste derefter () opkald.

Værktøjskasse

I punktfri stil har vi brug for en værktøjskasse, et sæt funktioner, der skal bruges igen og igen i fælles scenarier.

rør()

Jeg tager sagen om at sende et stykke data gennem flere transformationer.

Et personobjekt med fornavn og efternavn vil først have begge navne aktiveret. Derefter beregnes fuldnavnet. I slutningen forkortes det fulde navn, så det passer til skærmbegrænsningerne.

Nedenfor er koden, der viser alle disse trin:

funktion tilFullNameForView (person) {
  lad newPerson = kapitalisere navn (person);
  lad fuldnavn = beregneFullnavn (newPerson);
  returner kortere navn (fuldnavn);
}
funktion capitalizeNames (person) {
  Vend tilbage {
   fornavn: aktiverer (person.firstnavn),
   efternavn: aktivere (person.lastnavn)
  }
}
funktion aktiverer (navn) {
  return name.charAt (0) .toUpperCase () + name.slice (1);
}
funktion computeFullName (person) {
  return person.firstName + "" + person.lastName;
}
funktion kortere navn (navn) {
  return name.substring (0, 8);
}

capitalizeNames (), computeFullName (), kortere navn () er de små rene funktioner. toFullNameForView () er koordinatorfunktionen, der komponerer dem sammen.

Definition af toFullNameForView () i punktfri stil kræver en ny hjælpefunktion: pipe ().

funktionsrør (... funktioner) {
  returfunktion (værdi) {
   return functions.reduce (composeLeftToRight, værdi);
  }
}
funktion composeLeftToRight (computedValue, fn) {
  return fn (computeValue);
}

pipe () kæder fungerer sammen, så output fra hver funktion bruges som input til den næste.

Lad os definere koordinatorfunktionen i punktfri stil.

let toFullNameForView = pipe (
  capitalizeNames,
  computeFullName,
  shorterName,
);

rekvisit()

Lad os tage en simpel sag som følgende:

books.map ((bog) => book.title);

En punktfri mulighed er at refaktorere tilbagekaldet til en navngivet funktion:

lad titler = books.map (toBookTitle);
funktion til BookTitle (bog) {
  return book.title;
}

En anden mulighed er at bruge en generel funktion, der kan hente en egenskab: prop ().

lad titler = books.map (prop ("titel"));
funktions prop (propName) {
  returfunktion getProp (obj) {
    return obj [propName];
  }
}

unary ()

unary () er en funktionsdekoratør, der tager en funktion og returnerer en funktion, der tager en parameter. Det bruges normalt til at løse problemer, når funktionen kaldes med flere argumenter end nødvendigt.

funktion unary (fn) {
  return funktion unaryDecorator (først) {
    return fn.call (dette, først);
  }
}

Lad os bruge en console.log () i en punktfri stil til at logge alle numre fra en matrix.

lad tal = [1,2,3,4,5,6];
numbers.forEach (console.log);
// 1 0 (6) [1, 2, 3, 4, 5, 6]
// 2 1 (6) [1, 2, 3, 4, 5, 6]
// ...

Som du kan se, er der et problem. console.log () modtager flere argumenter, den har brug for. Vi er nødt til at sikre, at det kaldes med ét argument.

Her er koden ved hjælp af funktionen unary ().

numbers.forEach (unær (console.log));
// 1 2 3 4 5 6

Nedenfor er et andet eksempel, der bruger parseInt () i en punktfri stil:

lad tal = [1,2,3,4,5,6];
numbers.map (parseInt); // [1, NaN, NaN, NaN, NaN, NaN]
numbers.map (unær (parseInt)); // [1, 2, 3, 4, 5, 6]

Punktfri med metoder

Lad os nu bruge metoder i en punktfri stil. Jeg opretter et objekt ved hjælp af en fabriksfunktion og bruger derefter objektets metode i en punktfri stil.

Her er eksemplet:

funktionstjeneste () {
  lad url = "http: //";
 
  funktion reload () {
    console.log (URL);
  }
  
  return Object.freeze ({
    reload
  });
}
var service = ny service ();
setTimeout (service.reload); // http:

Brug af metoden i en punktfri stil fungerer godt.

Derefter skal jeg gøre det samme, men denne gang bygger jeg objektet ved hjælp af klasse.

klasseservice {
 constructor () {
    this.url = "http:";
  }
  
  genindlæse () {
    console.log (this.url);
  }
}
var service = ny service ();
setTimeout (() => service.reload (), 0); // http:
setTimeout (service.reload, 0); // undefined

Som du kan se, denne mistede kontekst.

Problemet kan løses ved hjælp af f.eks. Bind ().

setTimeout (service.reload.bind (service), 0);

Dette er en af ​​grundene til, at jeg foretrækker fabriksfunktioner frem for klasser. For en mere dybtgående sammenligning, se på funktionen Class vs Factory: udforske vejen frem.

Konklusion

Punktfri sammensætning forbedrer kodens klarhed og læsbarhed.

Mere end det favoriserer det praksis med at nedbryde alt i mindre stykker, der derefter kan sammensættes sammen på en meget udtryksfuld måde.

Punktfri stil går hånd i hånd med praksisen med at give intention-afslørende navne. At tage sig tid til at skrive navne på gode funktioner gør punktfri komposition meget lettere at læse.

Find funktionel JavaScript blev udnævnt til en af ​​de bedste nye funktionelle programmeringsbøger af BookAuthority!

For mere om anvendelse af funktionelle programmeringsteknikker i React, se på Funktionel reaktion.

Læs mere om Vue og Vuex i en hurtig introduktion til Vue.js-komponenter.

Lær, hvordan du anvender principperne i designmønstre.

Du kan finde mig også på Twitter.