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. jconsole监控上Linux上的JVM

    说明: 首先JConsole这个是JDK里面自带的工具  在JAVA_HOME/bin目录下,今天主要测试远程监控JVM 第一步:设置好需要远程机器的Tomcat 修改Tomcat下的配置文件:/us ...

  2. Oracle VM VisualBox 虚拟机创建共享文件夹。

    先来啰嗦几句,公司的电脑用的是 VMware10的虚拟机  相信大家都很熟悉了   VMware 创建共享文件功能可以直接安装tools来实现 但是 Oracle VM VisualBox  第一次玩 ...

  3. 反射生成SQL语句入门

    今天我们来学习学习通过反射技术来生成SQL语句. 反射提供了封装程序集.模块和类型的对象.您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型.然后,可以调用类型的方法或访 ...

  4. ecshop功能目录

    右上 开店向导 1设置商店的一些基本信息 商店的名字.地址.配送方式.支付方式等 2给商店添加一些商品 商品的名称.数量.分类.品牌.价格.描述等 3恭喜您,您的网店可以使用了!下面是一些常用功能的链 ...

  5. AES advanced encryption standard 2

    /* * FIPS-197 compliant AES implementation * * Copyright (C) 2006-2007 Christophe Devine * * Redistr ...

  6. struts2对拦截器使用带实例

    拦截器是struts2的核心.拦截器可以拦截请求,控制视图的走向.那么怎么来实现自定义的拦截器呢? 这里我们做一个例子. 首先假现在做了两个jsp页面一个是登陆的信息的(用session来模拟),一个 ...

  7. FT项目开发技术点(四)

    1.jsp页面form传递过来的值是在后台ishi通过name获得的而非ID.上传文字要用input type=text属性 <tr> <th>品牌:</th> & ...

  8. Android定制争夺战 三大主流ROM横评

    随着MIUI在广大“机油”们心目中位置的逐渐攀升,越来越多的厂商也相继推出了属于自己的定制Android ROM,想以此来抢占这一新兴市场,像点心OS.腾讯的Tita以及近期比较热门的百度云ROM等等 ...

  9. UCOS源码剖析 (一)

    UCOS源码详解 uC/OS-II源码分析(总体思路 一) 首先从main函数开始,下面是uC/OS-II main函数的大致流程: main()      { OSInit(); TaskCreat ...

  10. python测试开发django-8.windows系统安装mysql8教程

    前言 MySQL 是最流行的关系型数据库管理系统,可以在本地搭建一个mysql的环境,便于学习. windows7/windows10 mysql-8.0.11-winx64 下载安装包 mysql的 ...