1

chain MethodeWarum benötigt die Fantasy-Landespezifikation, dass die Kette einen Wert derselben Kette zurückgeben muss?

Ein Wert, die eine Kette hat, muß ein chain Verfahren bereitzustellen. Die Kette Verfahren ein Argument nimmt:

m.chain(f)

  1. f muss eine Funktion sein, die
    • einen Wert zurückgibt Wenn f nicht eine Funktion ist, ist das Verhalten von chain unspezifiziert.
    • f muss einen Wert von derselben Kette zurückgeben
  2. chain muss einen Wert von derselben Kette zurückgeben

GitHub - fantasyland

Gegeben ist eine einfache Implementierung der Option Monade:

// prototypes: 
const someProto = { 
    of(x) { return some(x) }, 
    map(f) { return some(f(this.x)) }, 
    ap(ftor) { return ftor.map(this.x) }, 
    join() { return this.x }, 
    chain(mf) { return this.map(mf).join() } 
}; 

const noneProto = { 
    of() { return this }, 
    map() { return this }, 
    ap() { return this }, 
    join() { return this }, 
    chain() { return this } 
}; 

// factories: 
function some(x) { 
    return Object.assign(Object.create(someProto), {x: x}); 
} 

function none() { 
    return Object.assign(Object.create(noneProto), {x: null}); 
} 

Um zu garantieren, dass chain immer eine Option monad zurückgibt, müsste ich sicherstellen, dass (monadische Funktion) immer eins zurückgibt. Dies ist nicht möglich, da nicht Teil der Implementierung ist. Vielmehr ist es definiert, wenn das monadisch verwendet:

// auxiliary function: 
const sub = y => x => x - y; 

let a = some(2); 
let b = some(3); 

a.chain(x => b.chain(y => some(sub(x)(y)))); // {x: 1} 
a.chain(x => b.chain(y => sub(x)(y))); // 1 - ouch! 

Bei der zweiten Methode übergab Anwendung die Funktion gibt keinen monadisch, die zu einem ausgepackten Ergebnis der monadischen Berechnung führt. Ich könnte einen Typcheck zu chain oder join vielleicht durch Ente eingeben, um das Problem zu lösen - das wäre aber ziemlich hässlich.

Warum benötigt die Spezifikation an dieser Stelle Typensicherheit? Javascript wird dynamisch typisiert, und ich würde lieber geeignete Komponententests schreiben, anstatt Typprüfungen zur Laufzeit durchzuführen. Würde ich dann die Spezifikation verletzen?

Antwort

1

In Ihrem zweiten Beispiel sollten Sie .map() verwenden:

a.chain(x => b.map(y => sub(x)(y))); 

und dann folgt alles die Regeln.

Zum Vergleich hier sind die entsprechenden Signaturen Haskell:

fmap :: m a -> (a -> b) -> m b -- same as .map() 
(>>=) :: m a -> (a -> m b) -> m b -- same as .chain() 

Also, wenn Ihre Funktion einen monadischen Wert zurückgibt, .chain() verwenden. Ansonsten verwenden Sie .map().

+0

Das Problem ist, wenn jemand diese Option Implementierung in einer Weise missbraucht, die ich in meinem zweiten Beispiel tat, verletzt es die Spezifikation. Daher könnte meine Implementierung nicht mit Fantasy-Land konform sein. Die einzige Möglichkeit, dies zu verhindern, besteht offenbar darin, zur Laufzeit eine Typprüfung hinzuzufügen. Ich glaube, das ist irgendwie hässlich. – ftor

+0

Ihre Implementierung einer Option monad ist kompatibel mit der Spezifikation. Ihre Verwendung von '.chain() 'ist nicht. Von Benutzern Ihrer Chainable wird erwartet, dass sie die entsprechende Methode verwenden - entweder '.chain()' oder '.map()', je nachdem, welche Funktion sie bereitstellen. – ErikR

+0

OK, ich bin nicht ganz überzeugt, aber akzeptiere die Antwort, danke! – ftor