我知道Javascript的承诺在技术上既不是函数也不是Haskell意义上的monad,因为(除其他外)
bind
操作,当传入纯函数时它会回退到map
(因此具有不明确的类型)Promise
构造函数和resolve
(又名return
)以递归方式连接嵌套的承诺通过始终提供具有正确类型a -> Promise b
的函数,可以轻松绕过第一个问题。
第二个问题显然违反了参数多态函数的参数性质,即无法构造m (m a)
结构。但是这种结构在承诺/异步计算的上下文中意味着什么呢?我无法想到Promise (Promise a)
的有意义的语义,其中Promise
是monad。那么我们失去了什么?递归加入的含义是什么?
如果我们非常务实(当我们对Javascript进行编程时,我们应该是什么样的话),如果我们认为Promise
是Javascript中的monad,我们就不能这样做照顾边缘案件?
答案 0 :(得分:2)
通过始终提供具有正确类型
a -> Promise a
的函数,可以轻松绕过第一个问题。
或者不使用then
作为monad的bind
操作,而是使用某些类型正确的操作。 Creed是一个功能强大的承诺库,提供map
和chain
方法,为代数类型实现Fantasy-land spec。
第二个问题也可以使用相同的方法绕过,不使用resolve
而是使用fulfill
,而使用静态of
方法作为单位函数。
但是这种结构在承诺/异步计算的上下文中意味着什么呢?
这是对价值承诺的承诺。不是每个可构造的类型都需要有意义的#34;或"有用" : - )
然而,Fetch API提供了一个类似类型的好例子:它返回一个解析为Response
对象的promise,该对象再次包含"一个承诺,解决了回应的主体。
所以Promise (Promise a)
可能只有一个成功结果值,也可以通过Promise a
访问,但是两个级别的承诺
请注意,Promise
类型应该有拒绝原因的第二个类型变量,类似于Either
。两级Promise err1 (Promise err2 a)
与Promise err a
完全不同。
我知道Javascript的承诺在技术上既不是Haskell意义上的仿函数也不是monad
然而,你还没有提到最大的问题:它们是可变的。从挂起状态到已解决状态的转换是一种副作用,如果我们考虑执行顺序就会破坏引用透明度,当然我们通常的承诺用例涉及许多IO,而这些IO根本没有由promise类型建模。
Promise.delay(50).then(() => Promise.delay(50))
// does something different than
const a = Promise.delay(50); a.then(() => a)
应用monad定律很有趣,偶尔也很有用,但我们确实需要很多实用主义。
答案 1 :(得分:0)
我知道,就Haskell而言,从技术上讲Javascript的承诺既不是函子也不是单子。
Not only in the sense of Haskell,也可以通过其他任何方式。
- 它们包括一个绑定操作,当传入一个纯函数(因此类型不明确)时,该绑定操作会回退到地图上
JS本机promise没有提供bind
运算符
- Promise构造函数和解析(aka return)都递归地加入嵌套的Promise
我想您的意思是解开“ theneables”,即只要有这样的功能,便调用存储在then
prop下的功能。
始终提供正确类型
a -> Promise b
的函数可以轻松地绕过第一个问题。
这与map
类似,例如当map(f)
用于
f = x => {then: a => a}
。
第二个问题显然违反了参数多态函数的参数性特征,即不能构造
m (m a)
结构。
确实。
但是,这种结构在承诺/异步计算的上下文中意味着什么?我无法想到
Promise (Promise a)
是有意义的语义,其中Promise
是单子。那我们输了什么呢?递归联接的含义是什么?
您需要允许存储任意值。不允许存储必需品(不进行包装),这就是问题所在。因此,您需要同时更改对象和方法的语义。允许对象无需更改即可存储需求,并实现.bind
或.chain
,该需求恰好能一次包装(或联接)需求-无需递归。
creed
对类似promise的对象进行操作,cpsfy
对基于回调的(又称为连续传递样式)函数进行操作。
前提是我们非常务实(这就是我们编写Java脚本时应该做到的),如果我们照顾到边缘情况,难道我们不能声称Promise是Java语言中的monad吗?
编写安全,简洁和可组合的代码非常实用。冒险通过泄漏抽象引入细微的错误,可能会使关键软件崩溃并带来长期后果。 每个极端情况都是造成这种风险的潜在原因。
在这方面,声称Promise
是单子,除了不正确之外,弊大于利。这确实有害,因为您不能安全地将单子转换应用于Promise。例如。使用符合承诺的monadic接口的任何代码,就像它们是monad一样,是不安全的。如果使用得当,monad可以帮助抽象和重用您的代码,而不会引入检查和寻找边缘情况的行。