Monadic IO与ramda和ramda幻想

试图找出IO monad是如何工作的。

使用下面的代码,我读取filenames.txt并使用结果来重命名目录testfiles中的文件。 这显然是未完成的,所以而不是实际重命名我login到控制台的任何东西。 🙂

我的问题是:

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

任何其他build议也表示赞赏,我是新来的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! 

你不是一个解决scheme太远。

为了避免第二次调用runIO ,可以利用runIO Fantasy中的IOtypes实现了fantasyland规范中的Apply接口。 这允许你renameDirect一个函数(比如你的renameDirect )来接受IOtypes的参数,并将函数应用到IO实例中包含的值。

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

在我们进入之前,我们可以稍微改变一下你使用R.zip然后R.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)在这里创build了一个IO (a -> b)的实例,它将部分应用R.zipWith(renameDirect)和存储在files的值。 然后将R.apnames值一起提供给R.ap ,这将生成一个新的IO实例,其中包含与IO(() => R.zipWith(renameDirect, value.runIO(), names.runIO())

现在,因为不得不调用R.map来部分应用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))