[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.114.K大异或和(线性基)
题目链接 如何求线性基中第K小的异或和?好像不太好做. 如果我们在线性基内部Xor一下,使得从高到低位枚举时,选base[i]一定比不选base[i]大(存在base[i]). 这可以重构一下线性基, ...
- 【动态规划+Floyd】OpenJudge3368
OpenJudge上刷水都不会了……这题居然写了一个半小时…… [题目大意] 诸葛亮要征服N城市.然而,City-X在击败City-2,City-3……City-x-1后击败.关羽,张飞,赵云,每个人 ...
- zoj 3629 Treasure Hunt IV 打表找规律
H - Treasure Hunt IV Time Limit:2000MS Memory Limit:65536KB 64bit IO Format:%lld & %llu ...
- Oil Deposits 搜索 bfs 强联通
Description The GeoSurvComp geologic survey company is responsible for detecting underground oil dep ...
- poj 1218 THE DRUNK JAILER
THE DRUNK JAILER Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 23358 Accepted: 1472 ...
- SGU 406 Goggle
406. Goggle Time limit per test: 0.25 second(s)Memory limit: 65536 kilobytes input: standardoutput: ...
- CentOS 7 下编译安装lnmp之MySQL篇详解
一.安装环境 宿主机=> win7,虚拟机 centos => 系统版本:centos-release-7-5.1804.el7.centos.x86_64 二.MySQL下载 MySQL ...
- powerdesigner反向SQLServer2008数据库生成物理数据模型
方法一:通过数据库脚本生成物理数据模型 具体步骤如下图所示:
- msgpack传文件
msgpack传文件 procedure TForm1.Button1Click(Sender: TObject);var ms, ms2: TMemoryStream; pack: TSimpleM ...
- UITextView in iOS7 doesn't scroll
UITextView in iOS7 has been really weird. As you type and are entering the last line of your UITextV ...