我可以在 Purescript 中将 do ... pure ... 替换为 ado ... in ... 吗?

Fil*_*und 0 typeclass do-notation purescript

我可以像这样替换 do-block 吗:

do
  ...
  pure ...
Run Code Online (Sandbox Code Playgroud)

其中最后一行是“纯粹”的东西,带有这样的 ado 块:

ado
  ...
  in ...
Run Code Online (Sandbox Code Playgroud)

我知道do只需要Bind而不是Monad(即Applicative),但我们已经在使用pure,所以我们已经Applicative

Her*_*rku 6

长答案

adoApplicative(以及因此的 applicative do - )和 Monad ( bindor )之间的区别do在于,当组合两个应用计算时,第二个计算不能依赖于第一个计算的结果。这意味着:

do
  x <- getX
  y <- getY
  pure { x, y }
-- Can be turned into
ado
  x <- getX
  y <- getY
  in { x, y }
Run Code Online (Sandbox Code Playgroud)

但如果 y 取决于 x 我们就不能这样做:

do
  x <- getX
  y <- getY x
  pure { x, y }
-- This won't compile
ado
  x <- getX
  y <- getY x
  in { x, y }
Run Code Online (Sandbox Code Playgroud)

这意味着您的问题的答案是“否”。

例子

让我们考虑一个直观的例子。Aff Monad 允许我们运行异步效果,例如从 API 获取数据。如果请求是独立的,我们可以并行运行它们。

ado
  user1 <- get "/user/1"
  user2 <- get "/user/2"
  in [user1, user2]
Run Code Online (Sandbox Code Playgroud)

如果它们相互依赖,我们需要一个又一个地运行请求:

do
  user1 <- get "/user/1"
  bestFriend <- get ("/user/" <> user1.bestFriendId)
  pure { user1, bestFriend }
Run Code Online (Sandbox Code Playgroud)

请注意,Aff无论您是否使用bindor apply,它总是按顺序运行,除非我们使用parallelwhich 将 an 转换Aff为 a ParAff。请注意,缺少 Bind/Monad 实例,ParAff因为两个并行计算不能依赖彼此的结果!https://pursuit.purescript.org/packages/purescript-aff/7.1.0/docs/Effect.Aff#t:ParAff

这意味着第一个示例要真正并行运行,我们实际上必须

ado
  user1 <- parallel $ get "/user/1"
  user2 <- parallel $ get "/user/2"
  in [user1, user2]
Run Code Online (Sandbox Code Playgroud)

我喜欢的另一个例子是解析/验证。想象一下您正在解析用户输入的两个字段:日期字符串和年龄。我们可以并行检查这两个属性,如果这两个属性都无效,则返回两个错误。但要验证日期,我们可能首先必须检查输入值是否是字符串,然后检查该值是否是日期字符串。有关示例库,请查看purescript-foreign

validated = ado
  date <- do
    str <- isString dateInput
    isDate date
  age <- do
    int <- isInt ageInput
    isPositive int
  in { date, age }
Run Code Online (Sandbox Code Playgroud)

我们看到Applicative比Monad弱很多。但在某些情况下,放弃 Monad 的力量可以给我们带来其他有趣的可能性,例如并行化或多个错误。