0

I am designing the IO for a program I have written in Haskell. I would like to be able to read some arguments from file using -f or from the command line by default. The command line arguments get split up into convenient chunks meaning I do not need to parse them. However this does not happen when reading from file. So I wrote a simple little parser to do what I wanted.

parseFile :: String -> [String]
parseFile [] = [""]
parseFile ('"':xs) = parseString xs '"'
parseFile ('\'':xs) = parseString xs '\''
parseFile (' ':xs) = "":parseFile xs
parseFile ('\t':xs) = "":parseFile xs
parseFile ('\n':xs) = "":parseFile xs
parseFile (s:xs) = (\(a:xa)->(a++[s]):xa)$ parseFile xs

parseString :: String -> Char -> [String]
parseString (s:xs) a
 | s == a = "":parseFile xs
 | otherwise = (\(a:xa)->(a++[s]):xa)$ parseString xs a

I thought this would be pretty simple I would do something like

let myInput = if (flagDetected) then (parseFile $ readFile $ last args) else (args)

However the result of readFile is an IO action and not a string thus I cannot parse it. I've tried a number of configurations all which have fails, mostly for typing reasons. I've tried assigning the results before parsing, which resulted in a type mismatch between args which is a [[Char]] ( it is the result of using getArgs from the System.Environment module) and the result of readFile which is still an IO String. I tried wrapping args with a return which of course doesn't fix this problem because of a type mismatch. I'm really at a loss of ideas now. I feel like this is probably a problem with the way I am thinking about the issue.

How can I get the behavior I desire?

Here's a related question I asked earlier. The root of the problem is the same but the answers on the last one were far to specific to help me here. It seems this is a frequent problem for me.

Wheat Wizard
  • 3,982
  • 14
  • 34
  • write a one linner bash script to parse the input file and invoke your haskell ... post what the parm input file would look like – Scott Stensland Jul 07 '17 at 19:21
  • What have I done wrong? Everyone has the right to downvote but it would be helpful if I they could comment so I can get some feedback. – Wheat Wizard Jul 07 '17 at 19:43
  • 1
    Downvoters should comment. Anyway, probably you need to read an IO or monad tutorial, like [LYAH](http://learnyouahaskell.com/input-and-output). The idea is that you can use `parseFile <$> readFile (last args)` (or `fmap`, or `do` notation or many other alternatives) and its type will be `IO [String]`. But this is probably one of the most frequent FAQs in Haskell, and is a bit broad, so a tutorial should really be the best option. – chi Jul 07 '17 at 19:51
  • 3
    This is a really basic question about how to use `IO`. I recommend thinking about `(>>=) :: IO a -> (a -> IO b) -> IO b` (the real type is more general) and how you could use it to make the types fit. – melpomene Jul 07 '17 at 20:04
  • @chi Thanks for the advice. I read that a while ago when I first started programming in Haskell. I probably just forgot the important bits. – Wheat Wizard Jul 07 '17 at 20:15

1 Answers1

0

You need to do an inversion of control in the flow of your data.. Haskell's IO datatypes are there to manage the effects of i/o operations. Read string from a file is not a fact, the file couldn't exist, can be empty or so on. Many possibilities could happend. So for that reason main function use to be based on IO. The way you can use to "extract" wrapped value from a IO is using do-return block

inputIO = if flagDetected then
             do 
               cnt <- (readFile path)
               return (parseFile cnt)
          else return args
main = do
      input <- inputIO
      return yourProgram input

Look here

salc2
  • 577
  • 5
  • 14