node.js - 带有 ramda 和 ramda-fantasy 的 Monadic IO

标签 node.js functional-programming ramda.js ramda-fantasy

试图弄清楚 IO monad 是如何工作的。

使用下面的代码,我读取了 filenames.txt 并使用结果重命名目录 testfiles 中的文件。这显然是未完成的,所以我没有实际重命名我登录到控制台的任何内容。 :)

我的问题是:

  1. 我调用了 runIO 两次,但感觉只应该调用它 一次,到底?
  2. 我想使用 renameIO 而不是 renaneDirect 但不能 找到正确的语法。

任何其他建议也很感激,我是 FP 的新手!

    var R = require('ramda');
    var IO = require('ramda-fantasy').IO
    var fs = require('fs');

    const safeReadDirSync = dir => IO(() => fs.readdirSync(dir));
    const safeReadFileSync = file => IO(() => fs.readFileSync(file, 'utf-8'));

    const renameIO = (file, name) => IO(() => console.log('Renaming file ' + file + ' to ' + name + '\n'));
    const renameDirect = (file, name) => console.log('Renaming file ' + file + ' to ' + name + '\n');

    safeReadFileSync("filenames.txt") // read future file names from text file
            .map(R.split('\n')) // split into array
            .map(R.zip(safeReadDirSync('./testfiles/').runIO())) // zip with current file names from dir
            .map(R.map(R.apply(renameDirect))) // rename
            .runIO(); // go!

最佳答案

您离解决方案不远了。

为了避免第二次调用 runIO,您可以利用 Ramda Fantasy 中的 IO 类型实现 fantasyland 规范中的 Apply 接口(interface)这一事实。这允许您提升一个函数(例如您的 renameDirect)以接受 IO 类型的参数并将该函数应用于包含在 IO 中的值> 实例。

我们可以在这里使用 R.ap,它具有 IO (a -> b) -> IO a -> IO -> b< 的签名(专门用于 IO)/。这个签名表明,如果我们有一个 IO 实例,它包含一个接受某种类型 a 并返回某种类型 b 的函数,以及另一个 IO 包含某种类型 a 的实例,我们可以生成包含某种类型 bIO 实例。

在开始之前,我们可以对您对 R.zipR.apply(renameDirect) 的使用稍作更改,方法是使用 R.zipWith(renameDirect) 将两者结合起来。

现在你的例子看起来像:

var R = require('ramda')
var IO = require('ramda-fantasy').IO
var fs = require('fs')

const safeReadDirSync = dir => IO(() => fs.readdirSync(dir));
const safeReadFileSync = file => IO(() => fs.readFileSync(file, 'utf-8'))
const renameDirect = (file, name) => console.log('Renaming file ' + file + ' to ' + name + '\n')

const filesIO = R.map(R.split('\n'), safeReadFileSync('filenames.txt'))
const testfilesDirIO = safeReadDirSync('./testfiles/')

const renameDirectIO = (files, names) =>
  R.ap(R.map(R.zipWith(renameDirect), files), names)

renameDirectIO(testfilesDirIO, filesIO).runIO()

在此示例中,我们通过调用 R.map(R.zipWith(renameDirect), files) 创建了一个 IO (a -> b) 实例这将部分应用 R.zipWith(renameDirect) 以及存储在 files 中的值。然后将其与 names 值一起提供给 R.ap,这将生成一个新的 IO 实例,其中包含等效于以下内容的有效结果IO(() => R.zipWith(renameDirect, value.runIO(), names.runIO())

现在因为必须调用 R.map 来部分应用 R.ap 的第一个参数往往有点笨拙,所以还有另一个辅助函数 R.lift可用于此目的,它负责提升给定函数的工作以生成现在接受 Apply 实例的新函数。

所以在上面的例子中:

const renameDirectIO = (files, names) =>
  R.ap(R.map(R.zipWith(renameDirect), files), names)

可以简化为:

const renameDirectIO = R.lift(R.zipWith(renameDirect))

关于node.js - 带有 ramda 和 ramda-fantasy 的 Monadic IO,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39885055/

相关文章:

javascript - 在 CardAction BotBuilder 中检测按钮按下情况

scala - 何时以及为什么应该在 Scala 中使用 Applicative Functors

haskell - 'match' 如何在 Haskell 的 FGL 中实现为 O(1)?

javascript - 使用 ramda.js assocPath 为多个键分配特定值?

javascript - 根据 Ramda 中数组中的值过滤集合

javascript - 无法使用nodemailer发送html文本

javascript - 如何正确使用 Chalk Terminal 字符串样式

functional-programming - "pivots"列表列表的类似 zip 方法的正确名称是什么?

javascript - Ramda ifElse 行为异常

angularjs - 使用 Angular 和 Express JS 下载所有文件格式