In 5 Minuten erklärt: Monaden in einfachem JavaScript

Dec 22 2022
Trotz des einschüchternden Titelbilds werde ich versuchen, Monaden mit so wenig Mathematik wie möglich zu erklären. Keine Erwähnung der Kategorientheorie, kein Kleisli, nicht einmal Curry.
Foto von Dan Cristian Pădureț auf Unsplash

Trotz des einschüchternden Titelbilds werde ich versuchen, Monaden mit so wenig Mathematik wie möglich zu erklären. Keine Erwähnung der Kategorientheorie, kein Kleisli, nicht einmal Curry. Es wird zwar eine geben NaN.

Mit den Worten des großen Douglas Crockford:

Abgesehen davon, dass es gut und nützlich ist, ist es auch verflucht, und der Fluch der Monade besteht darin, dass Sie, sobald Sie die Epiphanie erhalten haben, sobald Sie verstehen – „oh, das ist es!“ – die Fähigkeit verlieren, es jemand anderem zu erklären.

Nun, ich habe eine Theorie – das liegt daran, dass unter all den hervorragenden Artikeln zu diesem Thema, die eine großartige Erklärung der mathematischen Theorie hinter Monaden liefern und wie sie implementiert werden sollten (zusammen mit exquisiten Beispielen!), Eines nicht stimmt:

Wozu brauche ich die eigentlich?

Bevor wir versuchen, es herauszufinden, eine Bemerkung. Vor einiger Zeit habe ich gelesen, dass Sie wirklich nicht versuchen sollten, Monaden auf der Grundlage von irgendetwas zu verstehen, das nicht fest auf der Erklärung der dahinter stehenden Mathematik und ihres Platzes in der Kategorientheorie basiert. Für diejenigen, die daran interessiert sind: Es gibt einige ausgezeichnete Artikel, die dieses Thema behandeln, wie diesen oder diesen oder den, der Monaden in die Programmierung von 1992 einführte . Da ich diese hier wirklich nicht wiederholen möchte, lesen Sie sie bitte jetzt (oder scannen Sie sie zumindest ein) und kommen Sie danach hierher zurück (obwohl ich glaube, dass es nicht unbedingt notwendig ist, um zu verstehen, was als nächstes passiert).

OK, jetzt haben wir das aus dem Weg geräumt – was ist eine Monade? Nun, der einfachste Weg, es auszudrücken, ist – es ist ein Wrapper um einen bestimmten Wert. In Bezug auf die funktionale Programmierung ist eine Monade ein Funktor , eine Struktur, die es ermöglicht, eine Funktion darauf anzuwenden . Es ist eine zusätzliche Abstraktionsebene zwischen dem Wert und der Verarbeitung, der wir den Wert unterziehen wollen. Aber das sagt uns nichts.

Werfen wir einen Blick auf die einfachste Monade, die Sie sich vorstellen können: eine Implementierung der IdentityMonade:

const Identity = (value) => ({
  value: () => value,
  map: (fn) => Identity(fn(value)),
});

Schauen wir uns das einfachstmögliche Beispiel an:

const addTax = (value) => value * 1.1;

const cartValue = Identity(100);

const cartValueWithTax = cartValue
  .map(addTax) // At this point this is Identity(110)
  .value(); // Equal to 110 as a number

Mit freundlicher Genehmigung von https://makeameme.org/meme/please-bear-with-tmby8v

Die IdentityMonade hat – genau wie die identityFunktion in vielen funktionalen Programmierbibliotheken – eine ziemlich transparente Logik. Es ist eine Platzhaltermonade, genauso wie identityeine Platzhalterfunktion. Die Anwendung addTaxauf unseren Wert funktioniert gut für das oben angegebene Szenario, aber denken wir darüber nach, was passieren würde, wenn der Wert unseres Einkaufswagens gleich wäre undefined. Dank der wunderbaren Funktionsweise von JavaScript erhalten wir eine NaN.

Um dies zu umgehen, wäre es normalerweise am intuitivsten, der Funktion eine Prüfung auf den Wert hinzuzufügen addTax. Das ist ein falscher Ansatz. Es verschleiert den Zweck der Funktion und macht sie weniger rein. Die addTaxFunktion sollte nur eine Sache und nur eine Sache tun: adddie Tax.

OK, Schlaumeier – könnte man sagen – wie sollen wir den undefinedWert berücksichtigen? Nun, dafür haben wir Monaden. Wir wollen die Funktion rein halten. Wir möchten auch, dass sich unsere Logik basierend auf dem Wert, den wir an die Funktion übergeben, unterschiedlich verhält. Der beste Ort, um dieses Verhalten zu definieren, ist der Funktor !

Schauen wir uns die wahrscheinlich beliebteste Monade an, die es gibt: Maybe. Dies könnte eine grundlegende Implementierung sein (ich lasse die üblichen Methoden, denen Sie in einer Monade begegnen könnten, ofaus Gründen der Klarheit):

const Maybe = (value) => ({
  value: () => value,
  map: (fn) => value === undefined || value === null 
    ? Maybe(null) 
    : Maybe(fn(value)),
});

const primitiveCart = 100;
const undefinedCart = undefined;
const nullishCart = null;

const maybeCart = Maybe(100);
const maybeUndefinedCart = Maybe(undefined);
const maybeNullishCart = Maybe(null);


addTax(primitiveCart); // boring, 110
addTax(undefinedCart); // NaN
addTax(nullishCart); // 0 - thanks, JavaScript!

maybeCart.map(addTax).value(); // 110
maybeUndefinedCart.map(addTax).value(); // null
maybeNullishCart.map(addTax).value(); // null

Dies ist natürlich nur ein Bruchteil der enormen Fülle an Möglichkeiten, die die Verwendung von Monaden mit sich bringt. Nehmen wir an, wir möchten jeden Gast begrüßen, der unser Hotel betritt:

const greet = (name) => `Hello, ${name}!`;

Schauen wir uns nur ein weiteres elementares Beispiel an – die OptionalMonade:

const Optional = (value) => ({
  value: () => value,
  orElse: (alternativeValue) => value 
    ? Optional(value) 
    : Optional(alternativeValue),
  map: (fn) => Optional(fn(value)),
});

const firstGuest = "John";
const anonymousGuest = undefined;

greet(firstGuest); // "Hello, John!"
greet(anonymousGuest); // "Hello, undefined" - this is a bit embarrasing

const OptionalWithStrangerFallback = (name) => Optional(name)
  .orElse("stranger");

const firstGuestOptional = OptionalWithStrangerFallback(firstGuest);
const anonymousGuestOptional = OptionalWithStrangerFallback(anonymousGuest);

// I really hope you know where this is going at this point

firstGuestOptional.map(greet).value(); "Hello, John!"
anonymousGuestOptional.map(greet).value(); "Hello, stranger!"

Ich hoffe wirklich, dass dies die Dinge mit Monaden klärt. Wenn nicht, lass es mich bitte in den Kommentaren wissen! Wenn Sie weitere Artikel über JavaScript, funktionale Programmierung, Computer Vision, Architektur, Geometrie, Meme, technische Führung oder eine Mischung aus dem oben Genannten lesen möchten, folgen Sie mir unbedingt hier oder auf LinkedIn !