
Joachim Breitner wrote:
* The 5th line does not have this effect. Because this gets desugared to (>>), the special implementation of (>>) means that the next line still sees the same dependency state as the before the call to liftIO.
You are violating the monad laws. (f >> k) and (f >>= \_ -> k) should do the same thing. You might write a version of liftIO that has the effect you want, however.
* A change to inFile3 causes outFile1 to be re-written, although from looking at the code, _we_ know that this is not necessary, but the ODIO monad can not tell. The programmer should have swapped the lines.
Let me reverse engineer your algorithm (aside from the screwy >>): Every readFile that is encountered in processing ODIO is added to a list of source files. The reading deferred to be lazy with unsafePerformIO. When a writeFile is encountered it is assumed to depend on all previously read files. If this output file already exists and is newer than all the source files, then writing it is skipped (and perhaps also the lazy reads are skipped). Otherwise, the writing is strict. ---- I would say this is an unusual module. I rather prefer Makefile semantics, which could be improved in some ways by using a DSL in Haskell instead. The syntactic form of a file-oriented Makefile declaration is output : input1 input2 shell script more shell script And the "shell script" has access to the output file name, and also has access to the input names. In Haskell you could have a monadic DSL where the output name (and perhaps some explicit input names) are accessible like MonadReader. The result of running the DSL would do no IO at all but, much like a compiler, would return an IO action (the program to create the output file) and a list of inferred dependencies (an improvement over the Makefile syntax). Even if the DSL does not allow liftIO, it can still compile to various IO actions. Then you have a map from (outputname) to (dependencies,ioAction). And when outputname is demanded you can walk the dependencies to see if the timestamps are newer or older, using the ioActions to create the desired files. So perhaps to run the DSL monad you have a function like: makeRule :: DSL () -> FilePath -> [FilePath] -> ( [FilePath], IO () ) type Depends = Map FilePath ([FilePath], IO ()) demand :: Depends -> FilePath -> Maybe ByteString