处理Future中嵌套的Maybe monad



我仍在掌握函数式编程的窍门,并试图找出Monads的一个小问题。我有一种情况,为了论证起见,我有一个future,它会发出HTTP请求并返回一个值列表。然后我想能够检查该列表中是否存在特定的值,我会认为在这里返回一个"也许monad"将是胜利的。但是,如果该值存在,我希望能够根据返回的值发出另一个HTTP请求,但它应该只在该值存在的情况下运行。下面是一个使用Future&大概https://codesandbox.io/s/2xvy3m1qmy

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

  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)

您一定在寻找可以将Maybe<Future<…>>转换为Future<Maybe<…>>sequence操作,然后您可以将其加入到您的外部未来中。

traverse:

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

设法解决了我自己的问题,部分原因是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)

最新更新