[Functional Programming] mapReduce over Async operations with first success prediction (fromNode, alt, mapReduce, maybeToAsync)
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)的更多相关文章
- [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 ...
- [Functional Programming] Use Task/Async for Asynchronous Actions
We refactor a standard node callback style workflow into a composed task-based workflow. Original Co ...
- [Functional Programming] Reader with Async ADT
ReaderT is a Monad Transformer that wraps a given Monad with a Reader. This allows the interface of ...
- Functional Programming without Lambda - Part 2 Lifting, Functor, Monad
Lifting Now, let's review map from another perspective. map :: (T -> R) -> [T] -> [R] accep ...
- Monad (functional programming)
In functional programming, a monad is a design pattern that defines how functions, actions, inputs, ...
- JavaScript Functional Programming
JavaScript Functional Programming JavaScript 函数式编程 anonymous function https://en.wikipedia.org/wiki/ ...
- Beginning Scala study note(4) Functional Programming in Scala
1. Functional programming treats computation as the evaluation of mathematical and avoids state and ...
- 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 ...
- 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 ...
随机推荐
- [COGS2580]偏序 II
[COGS2580]偏序 II 题目大意: \(n(n\le50000)\)个五元组,求五维偏序. 思路: CDQ分治套CDQ分治套CDQ分治套树状数组. 时间复杂度\(\mathcal O(n\lo ...
- CentOS的利手:“Screen”一个可以在多个进程之间多路复用一个物理终端的窗口管理器
你是不是经常需要远程登录到Linux服务器?你是不是经常为一些长时间运行的任务头疼?还在用 nohup 吗?那 么来看看 screen 吧,它会给你一个惊喜! 你是不是经常需要 SSH 或者 tele ...
- 高并发系列之——负载均衡,web负载均衡
1 前言 负载均衡,一般包含两方面的含义.一方面是,将单一的重负载分担到多个网络节点上做并行处理,每个节点处理结束后将结果汇总返回给用户,这样可以大幅提高网络系统的处理能力: 第二个方面的含义是,将大 ...
- ACM需要掌握算法
数据结构 栈,队列,链表 哈希表,哈希数组 堆,优先队列 双端队列 可并堆 左偏堆 二叉查找树 Treap 伸展树 并查集 集合计数问题 二分图的识别 平衡二叉树 二叉排序树 线段树 一维线段树 二维 ...
- BZOJ 1022 [SHOI2008]小约翰的游戏John AntiNim游戏
1022: [SHOI2008]小约翰的游戏John Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 1475 Solved: 932[Submit][ ...
- C#高级编程9 第11章 Linq
Linq 1.Linq概述 列表和实体 准备数据: public class Championship { public int Year { get; set; } public string Fi ...
- shell中set的用法(转)
使用set命令可以设置各种shell选项或者列出shell变量.单个选项设置常用的特性. 在某些选项之后-o参数将特殊特性打开.在某些选项之后使用+o参数将关闭某些特性, 不带任何参数的set命令将显 ...
- WINDOWS 的 MKLINK : 硬链接,符号链接 : 文件符号链接, 目录符号链接 : 目录联接
玩转WIN7的MKLINK 引言: 换了新电脑,终于再次使用上啦WIN7 ,经过一个周每天重装N次系统,... ... ... ... 在xp系统下,junction命令要用微软开发的小程序 junc ...
- Android 菜单键和返回键互换
打开RE管理器找到system/usr/keylayout/ 长按qwerty.kl选择以文本编辑器查看 将里面的MENU和BACK全部替换掉 保存,退出管理器,重启手机,菜单键和返回键的位置就调换过 ...
- ASP.NET中Cookie跨域的问题及解决代码
ASP.NET中Cookie跨域的问题及解决代码 http://www.liyumei.net.cn/post/share18.html Cookies揭秘 http://www.cnblogs.c ...