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. 排序算法之直接插入排序Java实现

    排序算法之直接插入排序 舞蹈演示排序: 冒泡排序: http://t.cn/hrf58M 希尔排序:http://t.cn/hrosvb  选择排序:http://t.cn/hros6e  插入排序: ...

  2. [BZOJ4883][Lydsy1705月赛]棋盘上的守卫(Kruskal)

    对每行每列分别建一个点,问题转为选n+m条边,并给每条边选一个点覆盖,使每个点都被覆盖.也就是最小生成环套树森林. 用和Kruskal一样的方法,将边从小到大排序,若一条边被选入后连通块仍然是一个环套 ...

  3. [Assembly]汇编编写简易键盘记录器

    环境:Windows xp sp3工具:masmnotepad++ 首先列出本次编程程序要执行的步骤:(1).读取键盘所输入的字符(2).输出到屏幕上(3).完善Esc.Backspace.空格.回车 ...

  4. MyBatis -- generator 逆向工程

    一.引言 官网文档:http://www.mybatis.org/generator/index.html 通过使用官方提供的mapper自动生成工具,mybatis-generator-core-1 ...

  5. tyvj:1038 忠诚 线段树

    tyvj:1038 忠诚 Time Limit: 1 Sec  Memory Limit: 131072KiBSubmit: 9619  Solved: 3287 题目连接 http://www.ty ...

  6. EJB (Enterprise Java Bean) 理解

    做开发有段时间了,一直似懂非懂的. http://blog.csdn.net/jojo52013145/article/details/5783677

  7. Android peferenceActivity 自己定义标题简单方法

    Android peferenceActivity 自己定义标题简单方法 peferenceActivity 全然使用定义好的布局. 因此不能简单象其他好窗体进行自定,如今我们须要加 一个自己定义标题 ...

  8. 云计算大会有感—MapReduce和UDF

    (转载请注明出处:http://blog.csdn.net/buptgshengod) 1.參会有感       首先还是非常感谢CSDN能给我票,让我有机会參加这次中国云计算峰会.感觉不写点什么对不 ...

  9. ztree插件的使用及列表项拖拽的实现(jQuery)+异步加载节点数据

    为了实现如图所示的树状结构图,并使列表项可拖动到盒子里,研究了ztree这个插件的使用,并仔细研究了列表项的拖动事件.完成了预期需求,对jQuery的运用得到了提高.这个插件的功能非常强大,除了基本的 ...

  10. 解决TextView排版混乱或者自动换行的问题

    其实在TextView中遇到排版自动换行而导致混乱不堪的情况是非常常见的,而且导致这种问题产生的原因就是英文和中文混合输入,半角字符和全角字符混合在一起了.一般情况下,我们输入的数字.字母以及英文标点 ...