[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 ...
随机推荐
- [LOJ#2980][THUSCH2017]大魔法师(线段树+矩阵)
每个线段树维护一个行向量[A,B,C,len]分别是这个区间的A,B,C区间和与区间长度,转移显然. 以及此题卡常,稍微哪里写丑了就能100->45. #include<cstdio> ...
- BZOJ 2286: [Sdoi2011]消耗战 虚树 树形dp 动态规划 dfs序
https://www.lydsy.com/JudgeOnline/problem.php?id=2286 wa了两次因为lca犯了zz错误 这道题如果不多次询问的话就是裸dp. 一棵树上多次询问,且 ...
- 在windows安装配置Git开发环境
开始配置Git的开发环境.首先从google code下载最新的windows的git安装包msysgit,当时我下载的是Git-1.7.4-preview20110204.exe,然后就开始安装了 ...
- Codeforces Round #359 (Div. 1) A. Robbers' watch 暴力
A. Robbers' watch 题目连接: http://www.codeforces.com/contest/685/problem/A Description Robbers, who att ...
- zoj 3629 Treasure Hunt IV 打表找规律
H - Treasure Hunt IV Time Limit:2000MS Memory Limit:65536KB 64bit IO Format:%lld & %llu ...
- Python学习笔记(三):随机生成函数方法
本文是在Python2下总结! Python中的random模块用于生成随机数,如果想生成随机数需要先导入random的模块然后才能使用其中的方法,下面简单介绍一下常用的结果函数方法: 1·.rand ...
- 机器学习(4):BP神经网络原理及其python实现
BP神经网络是深度学习的重要基础,它是深度学习的重要前行算法之一,因此理解BP神经网络原理以及实现技巧非常有必要.接下来,我们对原理和实现展开讨论. 1.原理 有空再慢慢补上,请先参考老外一篇不错的 ...
- [Linux] VIM Practical Note
Practical Vim 文件 1.1. 管理多个文件 1.1.1. 缓冲区 • :ls • 查看缓冲区列表 • :bprev • 前一项 • :bnext • 后一项 • :bfirst • 第一 ...
- ASP.NET WEB API 调试
ASP.NET WEB API 中的路由调试与执行过程跟踪 http://www.cnblogs.com/Irving/p/4305493.html 路由调试 RouteDebugger 是调试 AS ...
- FT项目开发技术点(三)
1.eclipse中开发的web项目怎么放到tomcat中? 记得开始学习Java Web的时候,首先用的是Eclipse开发,但是有一个问题始终没有弄明白,做好的Web项目是如何发布到Tomcat服 ...