3

New to Haskell and wrestling with a problem. I know why my code doesn't work, but I cannot figure out the solution.

The intention is for the user to provide a file name either via argument or, if none provided, prompt the user to enter the data. A message with the file name is printed to the screen and the file is processed.

import System.Environment
main = do
    args <- getArgs
    let file = if null args
        then do putStr "File: " ; getLine
        else head args
    putStr "processing"
    putStrLn file
    writeFile file "some very nice text"

The code is wrong, of course, but demonstrates the logic I've been wrestling with. Neither Happy Learn Haskell nor Learn you Haskell could put me over the hump. The closest thread I could find was this one. Many thanks for assistance.

1 Answer 1

2

Let us analyze the type of file:

let file = if null args
    then do putStr "File: " ; getLine
    else head args

Here the if-then-else statement has as condition null args, which is indeed a Bool, so we are safe. But the the then part has as type IO String. So Haskell reasons that args should be an [IO String], but it is not: it is a list of Strings, so [String].

Later another problem pops up: you use file as a String, but it is not: it is still an IO String.

There are a few ways to fix it. Probably the minimal change is to use pure to wrap the head args back into an IO, and use replace the let clause with a <- statement:

import System.Environment

main = do
    args <- getArgs
    file <- if null args
        then do putStr "File: " ; getLine
        else pure (head args)
    putStr "processing "
    putStrLn file
    writeFile file "some very nice text"
Sign up to request clarification or add additional context in comments.

9 Comments

Dank u wel! Is there a reference for this? (Searching for "haskell pure" is impractical at best)
@Bill: well the pure function is a bit cryptic. An IO is a Applicative type, and so you can wrap elements in an IO object with pure (but you can use pure to wrap elements in a Maybe, [], etc. as well).
For example for the Maybe type, if you use pure 14 :: Maybe Int, you get a Just 14. For a list you get a singleton list (pure 25 :: [Int] is [25]). And for an IO you basically get a "no-operation" that has as output the parameter of the pure function.
@Bill If you'd like to make progress without being required to do a lot of research, I recommend The IO Monad for People who Simply Don't Care. If you'd like to do a lot of research and acquire a deep understanding before you proceed, I recommend You Could Have Invented Monads! (And Maybe You Already Have.).
Is there a particular reason to use pure over return in this context? I guess it's less confusing for people coming from languages where return means something else. But from what I've seen, return is historically more often used here, specifically in tutorials/books/etc, so I figure that's what people are more likely to recognize. I guess pure is better in general, since it's more ... well, general, but that's not as much of a factor if you know you're in IO from the get-go.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.