Trying to figure out how the IO monad works.
Using the code below I read filenames.txt and use the results to rename files in the directory testfiles. This is obviously unfinished, so instead of actually renaming anything I log to console. :)
My questions are:
- I call 
runIOtwice, but it feels like it only should be called once, in the end? - I'm want to use 
renameIOinstead ofrenaneDirectbut can't find the proper syntax. 
Any other suggestions are also appreciated, I'm new to 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!
				
                        
You're not too far off a solution.
Too avoid the second call to
runIOyou can make use of the fact that theIOtype in Ramda Fantasy implements theApplyinterface from the fantasyland spec. This allows you to lift a function (like yourrenameDirect) to accept arguments of theIOtype and apply the function to the values contained within theIOinstances.We can make use of
R.aphere, which has a signature (specialised here toIO) ofIO (a -> b) -> IO a -> IO -> b. This signature suggests that if we have anIOinstance that contains a function that takes some typeaand returns some typeb, along with anotherIOinstance that contains some typea, we can produce anIOinstance containing some type ofb.Before we get into that, we can make a slight change to your use of
R.zipthenR.apply(renameDirect)by combining the two usingR.zipWith(renameDirect).Now your example can now look like:
In this example we've created an instance of
IO (a -> b)here by callingR.map(R.zipWith(renameDirect), files)which will partially applyR.zipWith(renameDirect)with the value stored infiles. This is then given toR.apalong with thenamesvalue, which will produce a newIOinstance containing the effective result of something equivalent toIO(() => R.zipWith(renameDirect, value.runIO(), names.runIO())Now because having to call
R.mapto partially apply against the first argument toR.aptends to be a bit clunky, there is another helper functionR.liftthat can be useful for this purpose, which takes care of the job of lifting a given function to produce a new function that now acceptsApplyinstances.So in the above example:
Can be simplified to: