2016-10-05 4 views
4

IO Monadの仕組みを理解しようとしています。ramdaとramda-fantasyのMonadic IO

私はfilenames.txtを読んで、その結果を使ってディレクトリtestfilesのファイルの名前を変更します。これは明らかに未完成なので、実際に何かをリネームするのではなく、コンソールにログします。 :)

私の質問は以下のとおりです。

  1. 私は二回runIOを呼び出し、それが唯一の終わりに、一度 と呼ばれるべきであるようにそれは感じていますか?
  2. renaneDirectの代わりにrenameIOを使用しますが、 という正しい構文を見つけることができません。

他の提案もありがとうございます。私は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! 

答えて

9

解決策は遠すぎませんか。

あまりにもあなたがRAMDAファンタジーでIOタイプはファンタジースペックからApplyインタフェースを実装しているという事実を利用することができますrunIOへの2回目の呼び出しを避けます。これにより、(renameDirectのような)関数を持ち上げてIO型の引数を受け入れ、その関数をIOインスタンスに含まれる値に適用することができます。

R.apここでは、IO (a -> b) -> IO a -> IO -> bの署名(ここではIOに特化)を使用できます。このシグネチャは、あるタイプがaで、あるタイプがbで、あるタイプがaの別のIOインスタンスを返す関数を含むIOインスタンスを持っていれば、bを含むIOインスタンスを生成できることを示しています。

の場合、R.apply(renameDirect)を少し変更して、R.zipWith(renameDirect)を使用して2つを組み合わせることができます。我々は、部分的にfilesに格納されている値でR.zipWith(renameDirect)を適用するどのR.map(R.zipWith(renameDirect), files)を呼び出すことによって、ここでIO (a -> b)のインスタンスを作成した。この例では

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.apに最初の引数に対して適用するR.mapを呼び出すために持つことになる傾向があるので、今IO(() => R.zipWith(renameDirect, value.runIO(), names.runIO())

に相当するものの効果的な結果を含む新しいIOインスタンスを生成するnames値と共にR.apに与えられます。少しぎこちなく、R.liftがこの目的のために役に立つかもしれません。これは与えられた関数を持ち上げて、今やApplyのインスタンスを受け付ける新しい関数を生成する仕事を担当します。

は、したがって、上記の例では:でも私はFPに新しいです、おかげでたくさん

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

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

はに単純化することができます。ラムダを長い間使ってきました。しかし、Applicative Functorsを使用して持ち上げることは、依然として混乱しています。この回答は、私がこれらの概念をより良く理解するのに役立ちます。 – Jigar