Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 像单子一样处理流_Javascript_Haskell_Rxjs5 - Fatal编程技术网

Javascript 像单子一样处理流

Javascript 像单子一样处理流,javascript,haskell,rxjs5,Javascript,Haskell,Rxjs5,考虑以下流程: const oEmailValid$ = oEmailInput$ .map(_catchInputCtrlValue) .map(_isStringNotEmpty) .map(...) .map(...) .map(...) .map(...) .subscribe((predicate) => console.log(predicate)) 假设_isstringnotetry返

考虑以下流程:

 const oEmailValid$ = oEmailInput$
      .map(_catchInputCtrlValue)
      .map(_isStringNotEmpty) 
      .map(...)
      .map(...)
      .map(...)
      .map(...)
      .subscribe((predicate) => console.log(predicate))
假设_isstringnotetry返回false,因此我不想继续流,但仍然希望在subscribe函数上接收_isstringnotetry的返回值,在本例中为false

如何得到它

为了澄清,我的意思是,考虑下面的Haskell代码:


结果我什么也得不到,因为第四次计算什么也不返回。

你可以尝试这样破解它:

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));

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

compose(arrayOfFunctions);
下面是compose的一些示例:

const NOTHING = {};
//see if x is like a promise (has a "then" method)
const promiseLike =
  x =>
    (x!==undefined && typeof x.then === "function")
;
/**
 * 
 * takes a function fn and returns a function that takes x
 * and calls fn(x) if x does not equal NOTHING
 * if x does equal NOTHING it returns NOTHING 
 */
const wrapMaybe = 
  fn =>
  x =>
      (promiseLike(x))   
        ?x.then(wrapMaybe(fn))
        :(x === NOTHING)?NOTHING:fn(x)
;
/**
 * 
 * takes 2 functions and turns it into:
 * fn2(fn1(x)) when a value x is provided
 * if x is a promse it will turn it into:
 * x.then(x => fn2(fn1(x)))
 * if fn1(x) is a promise it will turn it into:
 * fn1(x).then(x => fn2(x))
 * if both x and fn1(x) are promises:
 * x.then(x => fn1(x)).then(x => fn2(x))
 */
const compose2 =
  fn1=>
  fn2=>
  x => {
    //if the current value is a promise
    if(promiseLike(x)){
      return x.then(x => compose2(fn1)(fn2))
    }
    const res1 = fn1(x);
    if(promiseLike(res1)){
      //result of fn1(x) is a promise
      //  invoke fn2 with the promise resolved value
      return res1.then(x => fn2(x))
    }
    //no promise, invoke fn2 with result of fn1(x)
    return fn2(res1);

  }
;
/**
 * turns an array of functions [fn1,fn2,fn3] into:
 * fn3(fn2(fn3(x)))
 * both x or any of the results of the functions can be a promise
 * If it is a promse then the next function will be called with
 * the resolve value of the promise.
 * If the promse is rejected the next function is not called
 * the handler for reject is called later down the promise chain
 * for example fn2 returns a rejected promise:
 * fn1(x)
 * .then(x => fn2(x))
 * .then(notCalled => fn3(notcalled))
 * .then(undefined,called)
 */
const compose =
  fns =>
    fns.reduce(
      (acc,fn) => compose2(acc)(fn)
      ,x=>x//id function
    )
;
/**
 * Turns an array of functions into compose(arrOfFunctions)
 * but maps the functions to wrapMaybe(function):
 * fn turns into wrapMaybe(fn)
 */
const composeWithMaybe =
    fns =>
      compose(
        fns.map(fn=>wrapMaybe(fn))
      )
;
const source = Rx.Observable.from([1,2,3,4,5]);
const mapHandlers = 
  [
    val => console.log("map:",1,"value:",val) || val + 10
    // you can return a promise in the function(s)
    //  from then on all results will be promises but next
    //  function is not called with the promise but it's resolve value
    // ,val => console.log("map:",2,"value:",val) || Promise.resolve(NOTHING)
    ,val => console.log("map:",2,"value:",val) || NOTHING
    // instead of Some or None you could return a rejected promise
    //  this basically gets you the same thing, none of the other
    //  functions are called, the result is a promise value that
    //  will invoke it's reject handler
    // ,val => console.log("map:",2,"value:",val) || Promise.reject("Rejected reason")
    ,val => console.error("map should not be executed:",3,"value:",val) || val + 10
  ]
;
const example = source
  .map(
    composeWithMaybe(
      mapHandlers
    )
  )
;
//synch example
example.subscribe(val => console.log(val));

// asynch example, you need to return a promise in one of the funcitons
// example.subscribe(
//   val => val.then(
//     val => console.log(val)
//     ,reject => console.warn(reject)
//   )
// );

如果要使用自定义运算符,可以使用编译代码。这些自定义运算符在您的IDE中显示为语法错误,因此如果您想编写函数,那么可以查看或。

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

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

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

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

民间故事有一个很好的Monad。


对于这个案例,我想到了几个解决方案。然而,如果您真的在寻找类似Haskell的maybe then Folktale,正如用户7128475所建议的,或者有maybe实现

第一个需要使用.filter和.defaultIfEmpty,但是这仅在oEmailInput$是0-1流时有效

const oEmailValid$ = oEmailInput$
  .map(_catchInputCtrlValue)
  .map(_isStringNotEmpty)
  .filter(isValid => isValid) // or .filter(Boolean) 
  .map(...)
  .map(...)
  .map(...)
  .map(...)
  .defaultIfEmpty(false)
  .subscribe((predicate) => console.log(predicate))
如果您想将此解决方案应用于0-*流,那么它也可以在flatMap内部工作

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))
一种更通用的解决方案是再次使用flatMap,它也适用于0-*流:

const oEmailValid$ = oEmailInput$
  .map(_catchInputCtrlValue)
  .flatMap(input => _isStringNotEmpty(input)
    ? Observable.of(input)
        .map(...)
        .map(...)
        .map(...)
        .map(...)
    : Observable.of(false)
  )
  .subscribe((predicate) => console.log(predicate))
但就我个人而言,我不认为流是这类问题的最佳代表。Streams best model actions\ behavior具有单个行为目标,目前我不清楚您原始示例的目的,并且似乎有单独但相等的分支。流通常应该建模数据如何通过一系列期望的操作来构成单个行为。这就是我怀疑Kagawa Shuhei想要达到的目的。例如,我使用一个表示表单的流,该表单执行以下操作:

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

{
  isValid: true | false,
  fields: {
    email: string, // original input text
  },
  errors: [
    { name: "email", message: "Please enter a valid email address." },
  ],
}

通过这种方式,像淋浴ROR这样的步骤可以映射到错误数组,并且在它为空或需要条件测试时不会中断。

评论Haskell,它实际上应该只有3>>只有4>>只有7>>没有任何>>。。。等等,因为>>被定义为a>>b=a>>=\\\->b。
onFormSubmit$
  .map(_validateData)
  .tap(console.log) // along the lines of your subscribe function
  .tap(showErrors)
  .tap(updateFormState)
  .filter(form => form.isValid)
  .subscribe(_postData)
{
  isValid: true | false,
  fields: {
    email: string, // original input text
  },
  errors: [
    { name: "email", message: "Please enter a valid email address." },
  ],
}