处理流可能像monad

时间:2017-10-09 15:28:55

标签: javascript haskell rxjs5

考虑以下流:

{
    "query": {
        "regexp":{
            "product": "*card*"
        }
    }
}

假设 const oEmailValid$ = oEmailInput$ .map(_catchInputCtrlValue) .map(_isStringNotEmpty) .map(...) .map(...) .map(...) .map(...) .subscribe((predicate) => console.log(predicate)) 返回_isStringNotEmpty,所以我不想继续使用流,但仍希望在订阅函数上接收false的返回值,在这种情况下是_isStringNotEmpty

如何获得它?

为了澄清,我的意思是,请考虑以下false代码:

Haskell

结果我得到(Just 3) >>= (\_ -> Just 4) >>= (\_ -> Just 7) >>= (\_ -> Nothing) >>= (\_ -> Just 10) ,因为第4次计算返回Nothing

5 个答案:

答案 0 :(得分:1)

JavaScript没有管道或撰写运算符,因此组合函数的语法可以是:

update: (store, { data: { createLink } }) => {
    const data = store.readQuery({
        query: ALL_LINKS_QUERY
    })
    data.allLinks.push(createLink)
    store.writeQuery({ query: ALL_LINKS_QUERY, data })
}

以下是一些例子的组合:

compose(arrayOfFunctions);

如果您想使用自定义运算符,可以使用sweet.js编译代码。这些自定义运算符在IDE中显示为语法错误,因此如果您想要编程功能,那么可以查看ELMFable

答案 1 :(得分:1)

Folktale有一个很好的Maybe monad。 https://github.com/folktale/data.maybe

function find(collection, predicate) {

  for (var i = 0; i < collection.length; ++i) {
    var item = collection[i]
    if (predicate(item))  return Maybe.Just(item)
  }
  return Maybe.Nothing()
}

var numbers = [-2, -1, 0, 1, 2]
var a = find(numbers, function(a){ return a > 5 })
var b = find(numbers, function(a){ return a === 0 })

a.map(function(x){ return x + 1 })
// => Maybe.Nothing
b.map(function(x){ return x + 1 })
// => Maybe.Just(1)

答案 2 :(得分:0)

你可以试着像这样破解:

const NOTHING = {};
const wrapMaybe = 
  fn =>
  x =>
    (x!==undefined && typeof x.then === "function")
      ?x.then(wrapMaybe(fn))
      :(x === NOTHING)?NOTHING:fn(x)

const source = Rx.Observable.from([1,2,3,4,5]);
//wrap each map function in a maybe
const example = source
  .map(wrapMaybe(val => console.log("map:",1,val) || val + 10))
  .map(wrapMaybe(val => console.log("map:",2,val) || Promise.resolve(NOTHING)))
  .map(wrapMaybe(val => console.log("map:",3,val) || val + 10))
;
//wrap the source in an object that has mm (maybe map) function
const wrapSource =
  source => {
    source.mm = 
      function(fn){
        var ret = this.map(wrapMaybe(fn));
        ret.mm = this.mm;
        return ret;
      }
    ;
    return source;
  }
;
const example2 = wrapSource(source)
  .mm(val => console.log("mm:",1,val) || val + 20)
  .mm(val => console.log("mm:",2,val) || NOTHING)
  .mm(val => console.log("mm:",3,val) || val + 20)
;
example.subscribe(val => console.log(val));
example2.subscribe(val => console.log(val));

答案 3 :(得分:0)

对于这种情况,我想到了几种解决方案。但是,如果你真的在寻找像Haskell那样的东西,那么Folktale(如用户7128475建议)或Sanctuary有可能的实现。

第一个需要使用<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>.filter,但这只适用于.defaultIfEmpty是0 - 1流的情况。

oEmailInput$

如果你想将它应用于0 - *流,这个解决方案也可以在flatMap内部工作。

const oEmailValid$ = oEmailInput$
  .map(_catchInputCtrlValue)
  .map(_isStringNotEmpty)
  .filter(isValid => isValid) // or .filter(Boolean) 
  .map(...)
  .map(...)
  .map(...)
  .map(...)
  .defaultIfEmpty(false)
  .subscribe((predicate) => console.log(predicate))

更一般的解决方案是再次使用flatMap,它也适用于0 - *流:

const oEmailValid$ = oEmailInput$
  .map(_catchInputCtrlValue)
  .map(_isStringNotEmpty)
  .flatMap(isValid => Observable.of(isValid)
    .filter(Boolean) 
    .map(...)
    .map(...)
    .map(...)
    .map(...)
    .defaultIfEmpty(isValid)
  )
  .subscribe((predicate) => console.log(predicate))

就个人而言,我认为流不是这类问题的最佳代表。 Streams最好的模型动作\行为与单一的行为目标,现在你原来的例子的目的不清楚,似乎有独立但相等的分支。流通常应该模拟数据如何通过一系列期望的操作来组成单个行为。这就是我怀疑Shuhei Kagawa试图进入的地方。例如,我使用表示执行以下操作的表单的流:

const oEmailValid$ = oEmailInput$
  .map(_catchInputCtrlValue)
  .flatMap(input => _isStringNotEmpty(input)
    ? Observable.of(input)
        .map(...)
        .map(...)
        .map(...)
        .map(...)
    : Observable.of(false)
  )
  .subscribe((predicate) => console.log(predicate))

onFormSubmit$ .map(_validateData) .tap(console.log) // along the lines of your subscribe function .tap(showErrors) .tap(updateFormState) .filter(form => form.isValid) .subscribe(_postData) 的结果将是一般形状,如:

_validateData

这样,像showErrors这样的步骤可以映射错误数组,如果它是空的或需要条件测试则不会中断。

答案 4 :(得分:-1)

因为您只使用map,这意味着所有验证都是同步进行的,所以我只想编写一个函数来同时处理所有验证。

function validate(validators) {
  return input =>
    validators.every(validator => validator(input));
}

const oEmailValid$ = oEmailInput$
  .map(validate([
    _catchInputCtrlValue,
    _isStringNotEmpty,
    ...
  ]))
  .subscribe((predicate) => console.log(predicate));

我相信尽可能多地编写纯函数并使用Stream或类似Monad的东西,只有当你真正需要它时才会很好,就像在Haskell中一样。