试图弄清楚 IO monad 是如何工作的。
使用下面的代码,我读取了 filenames.txt
并使用结果重命名目录 testfiles
中的文件。这显然是未完成的,所以我没有实际重命名我登录到控制台的任何内容。 :)
我的问题是:
- 我调用了
runIO
两次,但感觉只应该调用它 一次,到底? - 我想使用
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
的实例,我们可以生成包含某种类型 b
的 IO
实例。
在开始之前,我们可以对您对 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)
创建了一个 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/