【问题标题】:Handling nested Maybe monad inside Future在 Future 中处理嵌套的 Maybe monad
【发布时间】:2019-04-15 13:53:55
【问题描述】:

我仍然掌握函数式编程的窍门,并试图找出 Monads 的一个小问题。我有一种情况,我有一个未来会发出一个 HTTP 请求并返回一个值列表,为了参数的缘故。然后我希望能够检查该列表中是否存在特定值,我会认为在这里返回一个 Maybe monad 会是胜利。但是,如果该值存在,我希望能够基于该返回值发出另一个 HTTP 请求,但它应该仅在该值存在时运行。这是一个使用 Future & Maybe https://codesandbox.io/s/2xvy3m1qmy 的 ramda-fantasy 实现的示例

我很可能把这整件事弄错了,所以如果您需要更多信息,或者需要更改上述任何内容,那就去做吧。只是为了澄清这将是我在伪代码中的工作流程:

  1. 请求所有值
  2. 过滤值并可能找到匹配的值
  3. 如果找到,请求获取其余数据

这是一个初步的、带注释的尝试,如上链接所示:

import R from "ramda";
import Fantasy from "ramda-fantasy";
const Future = Fantasy.Future;
const Maybe = Fantasy.Maybe;

// make a fake HTTP request and return a list of values
// response :: Future Array String
const response = Future.of(['one', 'two', 'three'])

const maybeOrNothing = val => val ? Maybe.Just(val) : Maybe.Nothing()

// Maybe return our given value
// getVal :: String -> Maybe String
const getVal = input => response.map(R.find(R.equals(input))).map(maybeOrNothing)

// make another fake request
// let's pretend that this takes an ID and makes a fake ajax request to get teh data
// getValueData :: String -> Future
const getValueData = id => Future((reject, resolve) => {
  // fake HTTP request
  setTimeout(() => {
    resolve({
      id: id,
      foo: 'bar'
    })
  }, 100)
})

// 'one' should then run getValueData
// something isn't right here, do I need to map then chain?
getVal('one')
.chain(getValueData)
.fork(console.error, console.log)

// 'five' isn't there, so shouldn't run getValueData
// something isn't right here as getValueData still runs
// map(R.chain) works to not run getValueData but then it causes issues later on
getVal('five')
.chain(getValueData)
.fork(console.error, console.log)

【问题讨论】:

  • 请在您的问题中发布代码本身,而不仅仅是指向它的链接。
  • 已更新以在帖子中包含代码
  • getVal 的类型应该是String -> Future (Maybe String),对吧?

标签: javascript functional-programming monads


【解决方案1】:

您一定在寻找可以将Maybe<Future<…>> 变成Future<Maybe<…>>sequence operations,然后您可以加入您的外部未来。

traverse:

getVal('one')
.chain(traverse(Future.of, getValueData))
.fork(console.error, console.log)

【讨论】:

  • 嘿,Bergi,感谢您的回复,该示例不太奏效。需要进一步详细说明吗?
  • 它肯定可以工作,但是traverse 似乎总是假设一个列表类型而不是Maybe。我不知道为什么,你可能想提交一个 Ramda 错误。
【解决方案2】:

设法解决了我自己的问题,部分原因是 Bergi 的建议。 Bergi 上面的答案非常接近,但实施并不完全正确。问题是我试图处理 Future 然后将其更改为 Maybe,所以我处理了以下内容:

import R from "ramda";
import Fantasy from "ramda-fantasy";
const Future = Fantasy.Future;
const Maybe = Fantasy.Maybe;

// make a fake HTTP request and return a list of values
// response :: Future Array String
const response = Future.of(['one', 'two', 'three'])

const maybeOrNothing = val => val ? Maybe.Just(val) : Maybe.Nothing()
const maybeToFuture = m => Maybe.isNothing(m) ? Future.reject() : Future.of(m.value)

// Maybe return our given value
// getVal :: String -> Maybe String
const getVal = input => response.map(R.find(R.equals(input))).map(maybeOrNothing)

// make another fake request
// let's pretend that this takes an ID and makes a fake ajax request to get teh data
// getValueData :: String -> Future
const getValueData = id => Future((reject, resolve) => {
  // fake HTTP request
  setTimeout(() => {
    resolve({
      id: id,
      foo: 'bar'
    })
  }, 100)
})

// 'one' should then run getValueData
getVal('one')
  .chain(maybeToFuture)
  .chain(getValueData)
  .fork(console.error, console.log)

// five isn't there so getValueData doesn't run
getVal('five')
  .chain(maybeToFuture)
  .chain(getValueData)
  .fork(console.error, console.log)

【讨论】:

  • 这看起来不对,你根本没有使用Maybe。现在你的类型注释也错了,应该是getVal :: String -> Future (Future String)
  • 添加了 Maybe 实现
  • 它仍然将Maybe 转换为Future(并拒绝它!),而不是创建一个Future (Maybe String),这是我理解你最初正在寻找的?
  • 也许有右手边。要么有右手边和左手边。这个 future 的实现有一个右手边和一个左手边——这意味着当你有一个 future 时,你也有可能(和 any)已经可用的行为!而不是使用maybe.some或maybe.none,你可以只使用future.reject和future.of并避免在这些类型之间进行任何转换。如果您依赖于已经返回可能的函数(例如 MaybeFromNothing),它可能会有一些用处,但是由于您自己在这里实现它,所以使用可能没有意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-09
  • 2020-12-13
  • 2014-10-16
  • 1970-01-01
  • 2011-08-02
  • 1970-01-01
相关资源
最近更新 更多