User can give id, width, height and description rectangle and then I write it to a file. Now I would like to load this content from the file to my program but I have error:
Couldn't match expected type [RectangleType] against inferred type IO [Rectangletype]. In the first argument of menuRectangles namely db. In the expression menuRectangles db. In a do expression menuRectangles db.
What is going on ? This is content of my file:
[Rectangle 2 5 6 "abcabc",Rectangle 1 2 4 "abcabc"]
This is code:
import IO
import Char
import System.Exit
import Maybe
data RectangleType = Rectangle Int Int Int deriving(Show, Read)
loadFile :: FilePath -> IO [RectangleType]
loadFile fname =
catch (do fileContent <- readFile fname
return (read fileContent)
) errorHandler
where
errorHandler e = do putStrLn ("Error file")
exitFailure
db = loadFile "db.txt"
main = do
putStrLn "Choose option:"
n <- getLine
case n of
"1" -> do menuRectangles db; main
"2" -> putStrLn "bye, bye"
otherwise -> do putStrLn "Bad option"; main
menuRectangles :: [RectangleType] -> IO [RectangleType]
menuRectangles rs = do
putStrLn "Please choose option:"
putStrLn "1 - Add rectangle"
putStrLn "2 - Show rectangle"
putStrLn "3 - Quit"
putStr "Number: "
n <- getLine
case n of
"1" -> do { {- rs_new <- addRectangle rs; -} menuRectangles rs };
"2" -> do { {- showRectangle rs; -} menuRectangles rs }
"3" -> do { putStrLn "Quitting"; return rs }
otherwise -> do { putStrLn "The End"; return rs }
EDIT:
correct code:
import IO
import Char
import System.Exit
import Maybe
data RectangleType = Rectangle Int Int Int deriving(Show, Read)
loadFile :: FilePath -> IO [RectangleType]
loadFile fname =
catch (do fileContent <- readFile fname
return (read fileContent)
) errorHandler
where
errorHandler e = do putStrLn ("Error file")
exitFailure
main = do
db <- loadFile "db.txt"
mainMenu db
mainMenu rs = do
putStrLn "Choose option:"
n <- getLine
case n of
"1" -> do menuRectangles rs; mainMenu rs
"2" -> putStrLn "bye, bye"
otherwise -> do putStrLn "Bad option"; mainMenu rs
menuRectangles :: [RectangleType] -> IO [RectangleType]
menuRectangles rs = do
putStrLn "Please choose option:"
putStrLn "1 - Add rectangle"
putStrLn "2 - Show rectangle"
putStrLn "3 - Quit"
putStr "Number: "
n <- getLine
case n of
"1" -> do { {- rs_new <- addRectangle rs; -} menuRectangles rs };
"2" -> do { {- showRectangle rs; -} menuRectangles rs }
"3" -> do { putStrLn "Quitting"; return rs }
otherwise -> do { putStrLn "The End"; return rs }
In Haskell there exists a concept called pure code. Pure code does not have any of the following: user-input values, system calls, pseudorandom number generation, calls to non-pure code, etc.
Pure functions are guaranteed to always always always have the same behavior based on the lexical content of your program (e.g. they can return different values, but the reason they return different values cannot depend on "the world").
This is a very powerful policy in functional languages, and allows very powerful code. For example, you know that calling a function won't change some unrelated global variable or the state of some other data structure. I often apply this policy to python code.
The way Haskell enforces the purity-impurity thing is with the IO monad.
Any of things that "the world" touches are wrapped up in the IO monad, which represented that the values have been "tainted". If anything touches these "tainted" values, the values they return must also be tainted.
You will need to run your pure code within the IO monad if you want it to have access to those values. Once within the IO monad, you can "unwrap" the value you've read and pass it into your pure code, then your pure code returns value, then you can print your value. Everything works as expected, but you have to do it in the IO monad.
Note that it is good form to ensure that most of your code is written in a pure, functional form outide of the IO monad. e.g. a pure doStuffWithRectangles function, that is then called from within the IO monad.
The beautiful thing about it is that your pure code doesn't need to know the Rectangle value has been tainted by being of type IO Rectangle. As long as you work in the IO monad, your pure code will think it is just a normal Rectangle.
And just to make this answer more explicit: readFile returns things wrapped in the IO monad, because those things come from "the world" (the filesystem) and for example if you changed the file during program execution, it might return a different value.
--db = loadFile "db.txt" REMOVED--
main = do --within the IO monad
putStrLn "Choose option:"
n <- getLine
**DB <- LOADFILE "db.txt"** --db is now a pure value
case n of
"1" -> do menuRectangles db; main
"2" -> putStrLn "bye, bye"
otherwise -> do putStrLn "Bad option"; main
More information: http://www.haskell.org/tutorial/io.html
A good online/offile read: http://learnyouahaskell.com/
Also a good online/offile read: http://book.realworldhaskell.org/
Related
I want to write a toy program that has an interactive prompt and that can save and display all previous inputs. This is my first attempt, but does not compile (using ghc):
import System.IO
import Control.Monad.State
data ProgramState = ProgramState
{ events :: [Int] } -- Placeholder for now
parse_input :: String -> State ProgramState Bool
parse_input prompt = do
putStr prompt
hFlush stdout
current_state <- get
str <- getLine
case str of
"c" -> do
put (current_state { events = [1,2,3] } ) -- this should become actual appending
return True
"l" -> return True
"q" -> return False
"quit" -> return False
"h" -> return True
_ -> do
putStrLn "Invalid input."
parse_input prompt
main :: IO ()
main = do
should_continue <- parse_input "Enter your command."
if should_continue then main else return ()
main.hs:9:5: error:
• Couldn't match type ‘IO’
with ‘StateT ProgramState Data.Functor.Identity.Identity’
Expected type: StateT
ProgramState Data.Functor.Identity.Identity ()
Actual type: IO ()
Note: line 9 is putStr prompt
The same error is given for lines 10, 12, 22, 27.
I have since thought of doing the recursion purely inside parse_input, in which case I don't seem to need the state monad. But I am still curious why I get the compilation error. Any help is appreciated, I am very new to Haskell.
You seem to be mixing values of type State s a with values of type IO a. In your main action, you call parse_input in a context expecting IO. In parse_input, you call putStr and so on in a context expecting State. That's not going to work!
The usual way to do this kind of thing is to switch from State to StateT, and import Control.Monad.IO.Class. Now, you can use
evalStateT :: StateT s m a -> s -> m a
to "lower" your loop to IO, and
-- liftIO :: IO a -> StateT s IO a
liftIO :: MonadIO m => IO a -> m a
to "lift" the IO actions to StateT within the loop. Now (untested code ahead):
-- Needed for flexible use of
-- the MonadState class.
{-# LANGUAGE FlexibleContexts #-}
import System.IO
-- You almost always want the "strict"
-- version of `StateT`; the lazy one is weird.
import Control.Monad.State.Strict
import Control.Monad.IO.Class
data ProgramState = ProgramState
{ events :: [Int] } -- Placeholder for now
-- Renaming your function to follow convention.
parseInput
:: (MonadState ProgramState m, MonadIO m)
=> String -> m Bool
parseInput prompt = do
str <- liftIO $ do
putStr prompt
hFlush stdout
getLine
current_state <- get
case str of
"c" -> do
put (current_state { events = [1,2,3] } ) -- this should become actual appending
return True
"l" -> return True
"q" -> return False
"quit" -> return False
"h" -> return True
_ -> do
liftIO $ putStrLn "Invalid input."
parseInput prompt
main :: IO ()
main = do
-- You need to supply the initial state; I've just guessed here.
should_continue <- evalStateT (parseInput "Enter your command.") (ProgramState [])
if should_continue then main else return ()
As Daniel Wagner points out, this will not preserve the state from one main run to the next. If that's your intention, you can write
main :: IO ()
main = evalStateT loop (ProgramState [])
where
loop = do
should_continue <- parseInput "Enter your command."
if should_continue then loop else return ()
If you like, you can import Control.Monad and shorten this to
main :: IO ()
main = evalStateT loop (ProgramState [])
where
loop = do
should_continue <- parseInput "Enter your command."
when should_continue loop
Final note: if you want to capture the final state of your loop, use runStateT instead of evalStateT.
Given the proof of concept code below I'd like to be able to somehow perform my foo function with the ability to output the string Paul! and the possibility of getting its return value inside the InputT monad-transformer without using unsafePerformIO to remove the IO wrapper after runExceptT.
import Control.Monad.Except
import System.IO.Unsafe (unsafePerformIO)
import System.Console.Haskeline
type ErrorWithIO = ExceptT String IO
foo :: String -> ErrorWithIO String
foo "paul" = do liftIO $ putStrLn "Paul!"
return "OK!"
foo _ = throwError "ERROR!"
runRepl :: IO ()
runRepl = runInputT defaultSettings $ loop
loop :: InputT IO ()
loop = do
line <- getInputLine "> "
case line of
Nothing -> return ()
Just input -> do return $ putStrLn "asd"
case unsafePerformIO $ runExceptT $ foo input of
Left err -> outputStrLn err >> loop
Right res -> do
x <- outputStrLn . show $ res
loop
main :: IO ()
main = runRepl >> putStrLn "Goodbye!"
Am I missing something obvious here?
Since InputT IO is a MonadIO, you can use liftIO with this type:
liftIO :: IO a -> InputT IO a
So,
do ...
x <- liftIO $ runExceptT $ foo input
case x of
Left err -> ...
Right res -> ...
Alternatively, use Control.Monad.Trans.lift instead.
I have a function that accepts some arguments and returns IO (Either String String), say
testEither :: Int -> IO (Either String String)
testEither 0 = return (Left "we got an error")
testEither _ = return (Right "everything is ok")
(Real function fetches some stuff from real world and might fail)
I want to send output from that function to writeFile fileName. Expected behavior: if I bind testEither 0 to writeFile "test.txt", I fail with Left ..., and if I call it with testEither 1, I get everything is ok in file test.txt.
I guess the type of the whole expression should be something like IO (Either String ()), but I may be wrong.
You can use the ErrorT1 monad transformer to give you pure error handling on top of the IO monad:
import Control.Monad.Error
testEither :: Int -> IO (Either String String)
testEither 0 = return (Left "we got an error")
testEither _ = return (Right "everything is ok")
main = runErrorT $ do
result <- ErrorT $ testEither 0
lift $ writeFile "test.txt" result
1 ErrorT appears to have been replaced with ExceptT in the newest version of mtl, but the functionality should be similar.
This is easy using Either's Traversable instance:
import Data.Traversable
main = do
traverse (writeFile "test.txt") (Left "we got an error")
traverse (writeFile "test.txt") (Right "everything is ok")
This won't happen automatically, but you can easily use pattern matching to perform this action:
writeFileEither :: FilePath -> Either a String -> IO ()
writeFileEither _ (Left _) = return ()
writeFileEither fp (Right text) = writeFile fp text
Then you can bind them together with
main = testEither 1 >>= writeFileEither "test.txt"
Or with do notation:
main = do
result <- testEither 1
writeFileEither "test.txt" result
The main function below is an example that show how to use testEither in IO: if testEither returns an error then the error is written to stderr else the correct result is extracted from Right and written to the file test.txt:
import System.IO
testEither :: Int → IO (Either String String)
testEither 0 = return (Left "we got an error")
testEither _ = return (Right "everything is ok")
main :: IO 𐌏
main = do
res ← testEither 0
case res of
Left err → hPutStrLn stderr ("Error: " ++ err)
Right s → writeFile "test.txt" s
Here is my new main with the error: parse error on input '->'
I commented where the error is. Could it be an indentation error somewhere?
main :: IO()
main = do
expression <- evaluate_input
putStrLn $ show $ compute expression
evaluate_input :: IO ()
evaluate_input = do
args <- getArgs
case args of
a:s -> return a
-> do putStrLn "Enter Expression or 'end' to exit calculator"
hFlush stdout
getLine
unless (expression -> "end") $ showExpr expression --error here
where
showExpr expression = do putStrLn $ evaluateExpr expression
evaluate_input
evaluateExpr :: String -> String
evaluateExpr = show
Few problems with your code
until is not used correctly. I find it better to recurse when I have to repeat same action again and again. You can write the monadic version of until and use that.
It is better to use getArgs inside main once. You don't need to repeat it every time.
A corrected version is here. I haven't implemented all the functions so you still need to do the hard work of parsing and evaluating expressions.
import Control.Monad (unless)
main :: IO ()
main = evaluate
evaluate :: IO ()
evaluate = do
putStrLn "Enter Expression"
expr <- getLine
unless (expr == "end") $ showExpr expr
where
showExpr expr = do putStrLn $ evaluateExpr expr
evaluate
evaluateExpr :: String -> String
evaluateExpr = show
I'm trying to learn Haskell, but the small bit of sample code I tried to write is running into a fairly large amount of "Couldn't match expected type" errors. Can anyone give me some guidance as to what I'm doing wrong/how I should go about this?
These are the errors, but I'm not really sure how I should be writing my code.
toDoSchedulerSimple.hs:6:14:
Couldn't match expected type `[t0]' with actual type `IO String'
In the return type of a call of `readFile'
In a stmt of a 'do' block: f <- readFile inFile
In the expression:
do { f <- readFile inFile;
lines f }
toDoSchedulerSimple.hs:27:9:
Couldn't match expected type `[a0]' with actual type `IO ()'
In the return type of a call of `putStr'
In a stmt of a 'do' block: putStr "Enter task name: "
In the expression:
do { putStr "Enter task name: ";
task <- getLine;
return inFileArray : task }
toDoSchedulerSimple.hs:34:9:
Couldn't match expected type `IO ()' with actual type `[a0]'
In a stmt of a 'do' block:
putStrLn "Your task is: " ++ (inFileArray !! i)
In the expression:
do { i <- randomRIO (0, (length inFileArray - 1));
putStrLn "Your task is: " ++ (inFileArray !! i) }
In an equation for `getTask':
getTask inFileArray
= do { i <- randomRIO (0, (length inFileArray - 1));
putStrLn "Your task is: " ++ (inFileArray !! i) }
toDoSchedulerSimple.hs:41:9:
Couldn't match expected type `[a0]' with actual type `IO ()'
In the return type of a call of `putStr'
In a stmt of a 'do' block:
putStr "Enter the task you would like to end: "
In the expression:
do { putStr "Enter the task you would like to end: ";
task <- getLine;
filter (endTaskCheck task) inFileArray }
toDoSchedulerSimple.hs:60:53:
Couldn't match expected type `IO ()'
with actual type `[String] -> IO ()'
In a stmt of a 'do' block: schedulerSimpleMain
In the expression:
do { (getTask inFileArray);
schedulerSimpleMain }
In a case alternative:
"get-task"
-> do { (getTask inFileArray);
schedulerSimpleMain }
This is the code itself. I think it's fairly straightforward, but the idea is to run a loop, take input, and perform actions based off of it by calling other functions.
import System.Random (randomRIO)
import Data.List (lines)
initializeFile :: [char] -> [String]
initializeFile inFile =
do f <- readFile inFile
let parsedFile = lines f
return parsedFile
displayHelp :: IO()
displayHelp =
do putStrLn "Welcome to To Do Scheduler Simple, written in Haskell."
putStrLn "Here are some commands you might find useful:"
putStrLn " 'help' : Display this menu."
putStrLn " 'quit' : Exit the program."
putStrLn " 'new-task' : Create a new task."
putStrLn " 'get-task' : Randomly select a task."
putStrLn " 'end-task' : Mark a task as finished."
putStrLn " 'view-tasks' : View all of your tasks."
quit :: IO()
quit =
do putStrLn "We're very sad to see you go...:("
putStrLn "Come back soon!"
createTask :: [String] -> [String]
createTask inFileArray =
do putStr "Enter task name: "
task <- getLine
return inFileArray:task
getTask :: [String] -> IO()
getTask inFileArray =
do i <- randomRIO (0, (length inFileArray - 1))
putStrLn "Your task is: " ++ (inFileArray !! i)
endTaskCheck :: String -> String -> Bool
endTaskCheck str1 str2 = str1 /= str2
endTask :: [String] -> [String]
endTask inFileArray =
do putStr "Enter the task you would like to end: "
task <- getLine
return filter (endTaskCheck task) inFileArray
viewTasks :: [String] -> IO()
viewTasks inFileArray =
case inFileArray of
[] -> do putStrLn "\nEnd of tasks."
_ -> do putStrLn (head inFileArray)
viewTasks (tail inFileArray)
schedulerSimpleMain :: [String] -> IO()
schedulerSimpleMain inFileArray =
do putStr "SchedulerSimple> "
input <- getLine
case input of
"help" -> displayHelp
"quit" -> quit
"new-task" -> schedulerSimpleMain (createTask inFileArray)
"get-task" -> do (getTask inFileArray); schedulerSimpleMain
"end-task" -> schedulerSimpleMain (endTask inFileArray)
"view-tasks" -> do (viewTasks inFileArray); schedulerSimpleMain
_ -> do putStrLn "Invalid input."; schedulerSimpleMain
main :: IO()
main =
do putStr "What is the name of the schedule? "
sName <- getLine
schedulerSimpleMain (initializeFile sName)
Thanks, and apologies if this isn't the correct place to be asking such a question.
There are several issues with your code, which require varying levels of work to fix. In the order that I discovered them, you have...
Incorrect Types
Lots of your type signatures are incorrect. If a function does any I/O at all, it needs to wrap its return type in IO. For example, instead of
createTask :: [String] -> [String]
you need to have
createTask :: [String] -> IO [String]
which reflects the fact that createTask does I/O (it asks the user for the name of a task).
Fortunately, the fix for this is easy - just delete all your type signatures! This sounds crazy, but it can be very helpful. GHC has a powerful type inference mechanism, which means that types can often be inferred without you specifying them explicitly. In your program, all the types are simple enough to be inferred, so you can delete all your type signatures, load the module in GHCi and type e.g. :t createTask, whereupon the interpreter will tell you the inferred type (which you can then add to the source).
Operator Precedence Issues
In Haskell, function application has the tightest binding. In particular, when you write
putStrLn "Your task is: " ++ (inFileArray !! i)
this is parsed by Haskell as
(putStrLn "Your task is: ") ++ (inFileArray !! i)
which doesn't type check, since the left hand side is of type IO () and the right-hand side is of type String. This is also easy to fix. You simply need to write what you intend, which is either
putStrLn ("Your task is: " ++ (inFileArray !! i))
or
putStrLn $ "Your task is: " ++ (inFileArray !! i)
where the operator $ means "function application with the lowest possible precedence", and is often used to avoid parentheses.
Incorrect List Concatenation
After adding parentheses, your code has the line
return (inFileArray:task)
where inFileArray is of type [String] and task is of type String. Presumably you intend to add task to the end of inFileArray.
The : operator is for adding a single item to the front of a list (an O(1) operation). You can't use it to add items to the end of a list (an O(n) operation). All lists in Haskell are linked lists, so adding an item to the front of the list is fundamentally different to adding it to the end. You want either
return (task:inFileArray)
which will add the task to the front of the list, or
return (inFileArray ++ [task])
which creates a new single-element list from task and uses the list concatenation operator ++ to add it to the end of the list.
Misunderstanding do notation and >>=
This is the most fundamental misunderstanding in your code, and will require the most work to explain. Let's look at the following (highly edited) code snippet:
schedulerSimpleMain :: [String] -> IO () -- 1
schedulerSimpleMain inFileArray = -- 2
do input <- getLine -- 3
case input of -- 4
"new-task" -> schedulerSimpleMain (createTask inFileArray) -- 5
_ -> do putStrLn "Invalid input."; schedulerSimpleMain -- 6
We already know that the type of createTask is [String] -> IO [String]. Therefore line 5 doesn't type check. The function schedulerSimpleMain expects a [String] but you are passing it an IO [String].
What you need to do is unwrap the IO layer from the result of createTask inFileArray, and pass the resulting [String] to schedulerSimpleMain (which re-wraps it in the IO layer). This is exactly what the operator >>= (pronounced bind) does. You can write this line as
createTask inFileArray >>= schedulerSimpleMain
where you can think of the >>= operator as "piping forward" the result (a bit like the Unix pipe operator) but also doing all the necessary unwrapping/rewrapping on the way.
It can be a bit tricky to use the bind operator correctly when you're just starting out, which is one of the reasons we're provided with do notation in the first place. You can write this snippet as
do newInFileArray <- createTask inFileArray
schedulerSimpleMain newInFileArray
which is simply syntactic sugar for the code I wrote above, but is a bit more readable if you're not comfortable with the bind operator.
In line 6, you have a different but related problem. The sequencing operator ; essentially means "do the computation on the left, ignore the result, then do the computation on the right". It requires the left computation to have the type IO a and the right computation to have the type IO b (for any a and b).
Unfortunately, your right computation has the type of [String] -> IO [String], so again this line doesn't typecheck. To correct it, you just need to make sure you feed the appropriate argument to schedulerSimpleMain:
do putStrLn "Invalid input."; schedulerSimpleMain inFileArray
which now typechecks. You have this kind of error all over your code. I'm not going to detail all of the fixes for you here. I think you should try and fix it yourself first. If you're still running into problems in a day or so, I can put the corrected code on hpaste for you to study.
I suggest you to break your program in smaller bits and test them one by one.
I've fixed a couple of your functions: you can do similarly for the others.
import System.Random (randomRIO)
import Data.List (lines)
-- ERROR
-- f.hs:6:14:
-- Couldn't match expected type `[t0]' with actual type `IO String'
-- In the return type of a call of `readFile'
-- In a stmt of a 'do' block: f <- readFile inFile
-- In the expression:
-- do { f <- readFile inFile;
-- let parsedFile = lines f;
-- return parsedFile }
-- WHY?
-- initializeFile reads a file, therefore it must live inside the IO monad
initializeFile :: String -> IO [String]
initializeFile inFile = do
f <- readFile inFile
let parsedFile = lines f
return parsedFile
quit :: IO()
quit = do
putStrLn "We're very sad to see you go...:("
putStrLn "Come back soon!"
-- ERROR
-- f.hs:76:44:
-- Couldn't match expected type `IO ()'
-- with actual type `[String] -> IO ()'
-- In a stmt of a 'do' block: schedulerSimpleMain
-- In the expression:
-- do { putStrLn "Invalid input.";
-- schedulerSimpleMain }
-- In a case alternative:
-- _ -> do { putStrLn "Invalid input.";
-- schedulerSimpleMain }
-- WHY?
-- in the "_" case, schedulerSimpleMain is called without parameters, but
-- it needs a [String] one.
schedulerSimpleMain :: [String] -> IO()
schedulerSimpleMain inFileArray = do
putStr "SchedulerSimple> "
input <- getLine
case input of
"quit" -> quit
_ -> do putStrLn "Invalid input."; schedulerSimpleMain inFileArray
main :: IO()
main = do
putStr "What is the name of the schedule? "
sName <- getLine
-- Extract the lines from the IO monad
ls <- initializeFile sName
-- Feed them to the scheduler
schedulerSimpleMain ls