web-dev-qa-db-fra.com

Pourquoi les promesses monades?

J'ai appris sur la programmation fonctionnelle et j'ai rencontré Monads, Functors et Applicatives. 

D'après ma compréhension, les définitions suivantes s'appliquent:

a) (A => B) => C [A] => C [B] | Functor

b) (A => C [B]) => C [A] => C [B] | Monade

c) (C [A => B]) => C [A] => C [B] | Applicatif

(référence: https://thedet.wordpress.com/2012/04/28/functors-monads-applicatives-can-be-so-simple/

De plus, je comprends qu'une Monade est un cas particulier d'un Functor. En tant que, elle applique une fonction qui renvoie une valeur renvoyée à une valeur renvoyée et une valeur renvoyée. 

Lorsque nous utilisons Promise.then(func), nous transmettons à la promesse (c'est-à-dire C [A]) une fonction qui a normalement la signature A => B et retournons une autre promesse (c'est-à-dire C [B]). Donc, je pensais qu'une promesse ne serait qu'un functor et non une monade, car func renvoie B et non C [B].

Cependant, sur Google, j'ai découvert qu'une promesse n'était pas seulement un functor, mais aussi une monade. Je me demande pourquoi, car func ne renvoie pas une valeur enveloppée C [B] mais seulement B. Qu'est-ce qui me manque?

12
Jack Spar

Promise est (un peu comme) une monade parce que then est surchargé.

Lorsque nous utilisons Promise.then (func), nous transmettons à la promesse (c.-à-d. C [A]) une fonction ayant normalement la signature A => B et renvoyons une autre promesse (c.-à-d. C [B]). Je pensais donc qu'une promesse ne serait qu'un functor et non une monade, car func renvoie B et non C [B].

c'est vrai pour then(Promise<A>, Func<A, B>) : Promise<B> (si vous excusez mon pseudo-code pour les types javascript, je vais décrire les fonctions comme si this était le premier argument)

l'API Promise fournit une autre signature pour then cependant, then(Promise<A>, Func<A, Promise<B>>) : Promise<B>. Cette version correspond évidemment à la signature pour un lien monadique (>>=). Essayez vous-même, ça marche.

cependant, apposer la signature pour une monade ne signifie pas que Promise est une monade. il doit également satisfaire aux lois algébriques des monades.

les lois qu'une monade doit satisfaire sont la loi d'associativité

(m >>= f) >>= g ≡ m >>= ( \x -> (f x >>= g) )

et les lois d'identité gauche et droite

(return v) >>= f ≡ f v
m >>= return ≡ m

en JavaScript:

function assertEquivalent(px, py) {
    Promise.all([px, py]).then(([x, y]) => console.log(x === y));
}

var _return = x => Promise.resolve(x)
Promise.prototype.bind = Promise.prototype.then

var p = _return("foo")
var f = x => _return("bar")
var g = y => _return("baz")

assertEquivalent(
    p.bind(f).bind(g),
    p.bind(x => f(x).bind(g))
);

assertEquivalent(
    _return("foo").bind(f),
    f("foo")
);

assertEquivalent(
    p.bind(x => _return(x)),
    p
);

Je pense que toute personne familière avec les promesses peut voir que toutes ces choses devraient être vraies, mais n'hésitez pas à l'essayer vous-même.

parce que Promise est une monade, nous pouvons dériver ap et en tirer une application également, nous donnant une syntaxe très agréable avec un peu de hackery peu judicieux:

Promise.prototype.ap = function (px) {
    return this.then(f => px.then(x => f(x)));
}

Promise.prototype.fmap = function(f) {
    return this.then(x => f(x));
}

// to make things pretty and idiomatic
Function.prototype.doFmap = function(mx) {
    return mx.fmap(this);
}

var h = x => y => x + y

// (h <$> return "hello" <*> return "world") >>= printLn
h.doFmap(_return("hello, ")).ap(_return("world!")).bind(console.log)
4
colinro

Les promesses ne sont pas des monades sur des objets contenant une propriété then

Les promesses traitent les objets contenant une propriété then qui est une fonction comme un cas particulier. Pour cette raison, ils violent la loi de l'identité de gauche comme ci-dessous:

//Law of left identity is violated
// g(v) vs Promise.resolve(v).then(g)

// identity function saved under `then` prop
const v = ({then: x=>x({then: 1})})

// `g` returns the `then` prop from object wrapped in a promise
const g = (obj => Promise.resolve(obj.then))

g(v).then(res =>
          console.log("g(v) returns", res))
// "g(v) returns" x => x({ then: 1 })


Promise.resolve(v).then(g)
  .then(res =>
        console.log("Promise.resolve(v).then(g) returns", res))
// "Promise.resolve(v).then(g) returns" 1

exemple sur codepen

Cela se produit car la résolution traite la fonction située sous la propriété then comme un rappel, en transmettant la continuation de la chaîne then en tant qu'argument plutôt que de créer une promesse la contenant. De cette façon, il ne fonctionne pas comme une unité et provoque une violation des lois de la monade.

Cependant, pour les valeurs ne contenant pas de propriété then, il devrait fonctionner comme une monade.

0
Marty Gentillon