Search code examples
functional-programmingclean-language

Clean language: append number in end of file, fwritei doesn't work


I'm trying to write function that receive [String] which are names of files, String which is the name of the files directory and *f. The function will append to each file an integer in the end.

Here is what I got so far:

import StdEnv
import StdFile
import FileManipulation

appendNumInEndOfVmFiles :: [String] String *f -> String
appendNumInEndOfVmFiles [] dirname w = "finished"
appendNumInEndOfVmFiles [x:xs] dirname w
# path = dirname +++ "\\\\" +++ x
# (ok,file,files) = fopen path FAppendText w
# file = fwritei 12 file 
# (ok2,_) = fclose file w
= appendNumInEndOfVmFiles xs dirname w


Start w
// 1. Receive name of directory from the user.
# (io,w) = stdio w                                  // open stdio
# io = fwrites "Enter name of directory:\n" io      // ask for name
# (name,io) = freadline io                          // read in name
# name = name % (0, size name - 2)                  // remove \n from name
# (ok,w) = fclose io w                              // close stdio
| not ok = abort "Couldn't close stdio"             // abort in case of         failure

// 2. Get a list of all file names in that directory.
# (dir,w) = getDirectoryContents (RelativePath [PathDown name]) w
# fileList = getNamesOfFilesInDirectory (getEntriesList dir)

= appendNumInEndOfVmFiles (getVmFiles fileList) name w

Assume that getVmFiles is defined in my FileManipulation.dcl file and in the context of this problem name is "myDir" and file list is ["hello.vm","Wiki.vm"]

For some reason, even that I got "finished" message on the screen, the files aren't modified. No matter what kind of integer I give to fopen, even if its FWriteText or FWriteData its still doing nothing... also even if I'm using fwritec or fwrites with characters nothing happened.

What I'm missing here? Thanks a lot!


Solution

  • For some reason, even that I got "finished" message on the screen, the files aren't modified.

    This is due to lazy evaluation. In appendNumInEndOfVmFiles, the result of fclose is not used, so fclose is not evaluated. Because of this, fwritei does not need to be evaluated either. You can fix this by adding a guard on ok2:

    # (ok2,_) = fclose file w
    | not ok2 = abort "fclose failed\n"
    = appendNumInEndOfVmFiles xs dirname w
    

    However, the typical way to do this would be to rewrite the function to return a *f instead of a String, so that this unique value is not lost. As long as the result is used, then, the fwritei is evaluated. You can potentially make the *f argument strict (i.e. add a ! in front). This would make sure that it is evaluated before entering the function, so that all lingering file closes have been performed.


    There are some more issues with your code:

    1. Here, w is used twice, which is illegal because it is of a strict type. You should use (ok2,w) in the guard to continue with the same environment.

      # (ok2,_) = fclose file w
      = appendNumInEndOfVmFiles xs dirname w
      
    2. The appendNumInEndOfVmFiles needs to have a type context | FileSystem f to resolve overloading of fopen and fclose.


    Lastly:

    ... even if its FWriteText or FWriteData ...

    Just so you know: the difference would be that the first would write the integer in an ASCII representation whereas the second would write it binary as 4 or 8 bytes (depending on the bitwidth of your system).