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.