Let's say we are going to read some files, return the first file which pass the prediction method, this prediction method can be just check whether the file content contains more than 50 chars.

For reading the file, it has tow requirements, first we should have the right to read the file, then read file content, we can use Node.js method:

fs.access
fs.readFile

We won't directly using those methods, we are going to wrap those functions into Async functor:

const {Async, curry} = require('crocks');
const {fromNode} = Async; const access = fromNode(fs.access);
const readFile = fromNode(fs.readFile); const accessAsync = curry((mode, path) =>
access(path, mode)
.map(constant(path))); // readFileAsync :: Option -> a -> Async Error b
const readFileAsync = curry((option, path) =>
readFile(path, option));

By using 'fromNode', we are able to conver the Node's method into Async functor.

Here, we also put 'path' to the last params and apply 'curry', this is because we want to partially apply the params in the future.

Now 'accessAsync' & 'readFileAsync' both return 'Async' type, we can compose them:

const {Async, constant, composeK, curry} = require('crocks');
... // loadTextFile :: String -> Async Error String
const loadTextFile = composeK(
readTextFile,
checkRead
);

'loadTextFile' is the only method we want to be exported.

We also create a helper method to fork Async functor:

const fork = a => a.fork(
console.log.bind(null, 'rej'),
console.log.bind(null, 'res')
);

Full Code for funs.js:

const fs = require('fs');
const {Async, constant, composeK, curry} = require('crocks');
const {fromNode} = Async; const access = fromNode(fs.access);
const readFile = fromNode(fs.readFile); const accessAsync = curry((mode, path) =>
access(path, mode)
.map(constant(path))); // readFileAsync :: Option -> a -> Async Error b
const readFileAsync = curry((option, path) =>
readFile(path, option)); const checkRead = accessAsync(fs.constants.F_OK);
const readTextFile = readFileAsync('utf-8'); // loadTextFile :: String -> Async Error String
const loadTextFile = composeK(
readTextFile,
checkRead
); const fork = a => a.fork(
console.log.bind(null, 'rej'),
console.log.bind(null, 'res')
); module.exports = {
loadTextFile,
fork
}

Then let's continue to build our main.js file:

Let's say we have an array of filenames:

const data = [
'text.txt',
'text.big.txt',
'notfound.txt'
];

'text.txt' & 'text.big.txt' are existing files, and only 'text.big.txt' can pass the predicate function:

const isValid = x => x.length > ;

So with those in mind, let's define what we want to do:

1. We want to map over each filename in the 'data' array, read file content

2. For each content, we want to check against our 'isValid' method.

3. If the checking pass, it's done! output the content

4. If not pass the checking, we continue with next filename, repeat step No.1.

5. If all the filenames have gone though, no matching found, throw error.

6. If the list is empty, throw error.

7. If list is not empty but no matching file, and there is a not found filename, also throw error.

Step1-4 is a the main logic, step 5-7 is just some house keeping, throw some errors...

Step1-4 is prefect case for using 'mapReduce'

'mapReduce' here means, we first mapping over each case, then we do 'reduce' or let's say 'concat'; 'mapReduce' require a "empty" case, since our is Async functor, then the empty case will be a rejected async functor.

const {fork, loadTextFile} = require('./funs.js');
const {Async, curry, safe, mapReduce, maybeToAsync} = require('crocks'); const data = [
'text.txt',
'notfound.txt',
'text.big.txt',
]; const isValid = x => x.length > ; const concatAlt = pred =>
(acc, curr) =>
acc.alt(curr)
.chain(maybeToAsync(new Error('not good!'), safe(pred))) const flow = curry(pred => mapReduce(
loadTextFile, //map
concatAlt(pred), // reduce
Async.Rejected(new Error('list is empty')) //Seed
)); fork(flow(isValid, data));

Let's have a look 'concatAlt' in more details:

const concatAlt = pred =>
(acc, curr) =>
acc.alt(curr) // If acc async is rejected, then check curr, otherwise continue to next step 'chain' with the value of acc
.chain(maybeToAsync(new Error('not good!'), safe(pred))) // Async(Error String) --safe(pred)--> Async(Maybe(Error String)) --maybeToAsync--> Async(Async(Error String)) --chain--> Async(Error String)

'alt': works as fallback option, only has effect when 'acc' is falsy. Which means, if first two files cannot pass 'isValid' checking, but third passing, then we are still good! Also means, if the first one is passing the check, then we are not going to continue with next two files.

Here we are also using natural transform, maybeToAsync, more detail check my another post.

[Functional Programming] mapReduce over Async operations with first success prediction (fromNode, alt, mapReduce, maybeToAsync)的更多相关文章

  1. [Functional Programming] mapReduce over Async operations and fanout results in Pair(rejected, resolved) (fanout, flip, mapReduce)

    This post is similar to previous post. The difference is in this post, we are going to see how to ha ...

  2. [Functional Programming] Use Task/Async for Asynchronous Actions

    We refactor a standard node callback style workflow into a composed task-based workflow. Original Co ...

  3. [Functional Programming] Reader with Async ADT

    ReaderT is a Monad Transformer that wraps a given Monad with a Reader. This allows the interface of ...

  4. Functional Programming without Lambda - Part 2 Lifting, Functor, Monad

    Lifting Now, let's review map from another perspective. map :: (T -> R) -> [T] -> [R] accep ...

  5. Monad (functional programming)

    In functional programming, a monad is a design pattern that defines how functions, actions, inputs, ...

  6. JavaScript Functional Programming

    JavaScript Functional Programming JavaScript 函数式编程 anonymous function https://en.wikipedia.org/wiki/ ...

  7. Beginning Scala study note(4) Functional Programming in Scala

    1. Functional programming treats computation as the evaluation of mathematical and avoids state and ...

  8. Functional Programming without Lambda - Part 1 Functional Composition

    Functions in Java Prior to the introduction of Lambda Expressions feature in version 8, Java had lon ...

  9. a primary example for Functional programming in javascript

    background In pursuit of a real-world application, let’s say we need an e-commerce web applicationfo ...

随机推荐

  1. [BZOJ5306][HAOI2018]染色(容斥+FFT)

    https://www.cnblogs.com/zhoushuyu/p/9138251.html 注意如果一开始F(i)中内层式子中j枚举的是除前i种颜色之外还有几种出现S次的颜色,那么后面式子就会难 ...

  2. [CF1053C]Putting Boxes Together(线段树)

    http://codeforces.com/blog/entry/62013 两个结论: 1.一定有一个箱子不用动. 2.不动的箱子一定是加权前缀和为S/2的那个. 1显然,2由1易得. 于是问题变为 ...

  3. java设计模式(三)模板模式

    抽象类中公开定义了执行它的方法的方式,子类可以按需求重写方法实现,但调用将以抽象类中定义的方式进行,典型应用如银行办理业务流程.冲泡饮料流程.下面给出简单例子,用沸水冲泡饮料,分为四步:将水煮沸.泡制 ...

  4. LeetCode:整数反转(Reserve Integer)

    public class ReserveInteger { public int reverse(int x) { //用于接收个位数(10的余数) int remainder; //是否负数 int ...

  5. python开发_tkinter_获取单选菜单值

    在之前的blog中有提到python的tkinter中的菜单操作 python开发_tkinter_窗口控件_自己制作的Python IDEL_博主推荐 python开发_tkinter_窗口控件_自 ...

  6. URAL 1880 Psych Up's Eigenvalues

    1880. Psych Up's Eigenvalues Time limit: 0.5 secondMemory limit: 64 MB At one of the contests at the ...

  7. linux—文件目录简单介绍

    1.Linux系统以文件目录的方式来组织和管理系统中的所有文件.所谓文件目录就是将所有文件的说明信息采用树型结构组织起来,即我们常说的目录:整个文件系统有一个“根”(root),然后在根上分“杈”(d ...

  8. Qt线程外使用Sleep

    一:方法1 QTime t; t.start(); while(t.elapsed()<1000){     QCoreApplication::processEvents();} 二:方法2 ...

  9. [置顶] iOS中让省略号垂直居中

    在显示等待框时,一般要求在提示信息后面加个省略号,但中文输入法下输入的省略号是在底部对齐,但中 文的习惯是省略号垂直居中对齐,最后找到下面这个方法来显示垂直居中的省略号: 中文和英文输入法下一样: o ...

  10. 吐槽Windows 8,就没见过这么烂的平板操作系统

    本文带有严重个人情感色彩,反感者慎入. CSDN 博文大赛得了个奖品,联想的平板电脑, MIIX2 8 .系统是 Windows 8 . 今天媳妇再次使用它,惹得我再次吐槽. 一句话.Windows ...