在Monads方面递归加入Promises的含义是什么?

时间:2017-08-19 11:21:43

标签: javascript haskell promise monads es6-promise

我知道Javascript的承诺在技术上既不是函数也不是Haskell意义上的monad,因为(除其他外)

  • 它们包含一个bind操作,当传入纯函数时它会回退到map(因此具有不明确的类型)
  • Promise构造函数和resolve(又名return)以递归方式连接嵌套的承诺

通过始终提供具有正确类型a -> Promise b的函数,可以轻松绕过第一个问题。

第二个问题显然违反了参数多态函数的参数性质,即无法构造m (m a)结构。但是这种结构在承诺/异步计算的上下文中意味着什么呢?我无法想到Promise (Promise a)的有意义的语义,其中Promise是monad。那么我们失去了什么?递归加入的含义是什么?

如果我们非常务实(当我们对Javascript进行编程时,我们应该是什么样的话),如果我们认为Promise是Javascript中的monad,我们就不能这样做照顾边缘案件?

2 个答案:

答案 0 :(得分:2)

  

通过始终提供具有正确类型a -> Promise a的函数,可以轻松绕过第一个问题。

或者不使用then作为monad的bind操作,而是使用某些类型正确的操作。 Creed是一个功能强大的承诺库,提供mapchain方法,为代数类型实现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可以帮助抽象和重用您的代码,而不会引入检查和寻找边缘情况的行。