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. C# 字符串处理小工具

    之前刚上大学时沉迷于安全方面,当时一直想写一个处理字符串的小程序. 无奈当时没有太多时间,一直拖延到这寒假. 寒假闲来无事,所以就写写小程序来练手,顺便复习一下窗体和基础. 实现的功能有以下: 转换为 ...

  2. 【HackerRank Week of Code 31】Colliding Circles

    https://www.hackerrank.com/contests/w31/challenges/colliding-circles/problem 设E(n)为序列长度为n时的期望值. \[ \ ...

  3. BZOJ.1032.[JSOI2007]祖码(区间DP)

    题目链接 BZOJ 洛谷 AC代码: 区间DP,f[i][j]表示消掉i~j需要的最少珠子数. 先把相邻的相同颜色的珠子合并起来. 枚举方法一样,处理一下端点可以碰撞消除的情况就行. 当然合并会出现问 ...

  4. BZOJ 1588: [HNOI2002]营业额统计 双向链表

    BZOJ 1588: [HNOI2002]营业额统计 Time Limit: 5 Sec  Memory Limit: 512 MBSubmit: 9619  Solved: 3287 题目连接 ht ...

  5. js删除字符串的最后一个字符三种方法

    字符串 var basic = "abc,def,ghi,"; 第一种 basic = basic.substr(0, basic.length - 1); 第二种 basic = ...

  6. 【Go入门教程5】流程(if、goto、for、switch)和函数(多个返回值、变参、传值与传指针、defer、函数作为值/类型、Panic和Recover、main函数和init函数、import)

    这小节我们要介绍Go里面的流程控制以及函数操作. 流程控制 流程控制在编程语言中是最伟大的发明了,因为有了它,你可以通过很简单的流程描述来表达很复杂的逻辑.Go中流程控制分三大类:条件判断,循环控制和 ...

  7. javascript 实现页面加载完的操作

    document.onreadystatechange = function () { if (document.readyState == "complete") {//页面加载 ...

  8. 线程系列07,使用lock语句块或Interlocked类型方法保证自增变量的数据同步

    假设多个线程共享一个静态变量,如果让每个线程都执行相同的方法每次让静态变量自增1,这样的做法线程安全吗?能保证自增变量数据同步吗?本篇体验使用lock语句块和Interlocked类型方法保证自增变量 ...

  9. Spring-Bean配置-使用外部属性文件(转)

    Spring-Bean配置-使用外部属性文件 所以可以通过@value注解获取配置文件的key-value,生成一个配置文件bean.用以在代码中直接使用bean的方式. •在配置文件里配置Bean时 ...

  10. x-superobject

    x-superobject GITHUB: https://github.com/onryldz/x-superobject **Delphi Cross Platform Rapid JSON**- ...