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?
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)
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
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.