Home > Blockchain >  Good practice to write to a file
Good practice to write to a file

Time:12-30

In the main function of my program, I call timesRule which is returning a Boolean value. In this function I want to write to a file. However, if I understood correctly the function times rule needs to return IO() if it writes to file. How should I structure my code to write to a file in a function returning a Boolean value ?

timesRule :: (MultiSet LocalType) -> Bool
timesRule sequent = do 
    let result = MultiSet.concatMap (\x -> if isPrl x then [checkTimes x, checkTimes2 x] else [x] ) sequent
    let file = "tmp/log.txt"
    let content = "TIMES rule: "     (show(MultiSet.toList result))
    let log = writeToFile file content
    prefixRule result

Used function:

import qualified System.IO.Strict as SIO

writeToFile :: FilePath -> String -> IO()
writeToFile file content = do 
    x <- SIO.readFile file
    writeFile file ("\n"  content)
    appendFile file x

CodePudding user response:

The somewhat obvious solution would be changing the type your function to IO Bool as @Robin Zigmond pointed out.

There is some problem with your syntax apart from calling writeToFile, though. For your function timesRule to have the given type it would need to look like this:

timesRule :: (MultiSet LocalType) -> Bool
timesRule sequent = -- there is no do here
    let 
        result = MultiSet.concatMap (\x -> if isPrl x then [checkTimes x, checkTimes2 x] else [x] ) sequent

        -- the following two lines are superfluous ...
        file = "tmp/log.txt"
        content = "TIMES rule: "     (show(MultiSet.toList result))

        -- ... because this still doesn't work
        log = writeToFile file content

        -- ... and what were you going to use `log` for, anyway?
    in  
        prefixRule result

Changing your type to IO Bool allows you to use monadic do-syntax. Bool by itself neither has an applicative nor a monad instance and thus there is no meaningful do-syntax. (In order to have either an applicative or a monad instance, you need a type function like Maybe or IO, fyi):

timesRule :: (MultiSet LocalType) -> IO Bool
timesRule sequent = do
    let 
        result = MultiSet.concatMap (\x -> if isPrl x then [checkTimes x, checkTimes2 x] else [x] ) sequent

        file = "tmp/log.txt"
        content = "TIMES rule: "     (show(MultiSet.toList result))

    -- the syntax to get the value of type `a` out of `IO a` is this:
    log <- writeToFile file content

    -- the function to turn a value of type `a` into `IO a` is `pure`:
    pure (prefixRule result)

You stil don't use log and might as well replace

log <- writeToFile file content

with

writeToFile file content

Given that writeToFile has type ... -> IO (), the () is pronounced "unit", the value of log is () and thus log does not contain any useful information (probably).


The less obvious solution is to refactor your code a bit and seperate the concerns. Sometimes it does make sense to have a function write to a file and return some boolean value. In your case, you probably want a funcion that returns result, i.e. turn this line into a function:

MultiSet.concatMap (\x -> if isPrl x then [checkTimes x, checkTimes2 x] else [x] ) sequent

Then you already have prefixRule that gives you the Bool and you have writeFile. This way you separate pure code (anything that does not have the type IO something) form code with IO-side effects.

  • Related