I am trying to parse a simple binary file in Haskell with the Data.Binary.Get monad.
A simplified version of my code looks like this:
data MsgString = Definition_msg {
msg_no :: Word16
} deriving (Show)
parseDef :: Get MsgString
parseDef = do
msg_no <- getWord16le
return $ Definition_msg msg_no
parseMain :: Get [MsgString]
parseMain = do
bit <- getWord8
msg <- parseDef
return msg:parseMain
And the error I get is the following:
Prelude> :l example.hs
[1 of 1] Compiling Main ( example.hs, interpreted )
example.hs:23:17:
Couldn't match expected type `[m MsgString]'
against inferred type `Get [MsgString]'
In the second argument of `(:)', namely `parseMain'
In the expression: return msg : parseMain
In the expression:
do { bit <- getWord8;
msg <- parseDef;
return msg : parseMain }
Failed, modules loaded: none.
Can anyone see what I do wrong?
Thanks!
The issue is your last line, which parses as:
(return msg) : parseMain
But that really isn't the only problem. parseMain is of type Get [MsgString] when you really just want a [MsgString] so you must run the monadic action first:
parseMain :: Get [MsgString]
parseMain = do
bit <- getWord8
msg <- parseDef
rest <- parseMain
return (msg : rest)
Notice this will get an infinite list of MsgString's and won't terminate without an exception. Perhaps you intended to have an if statement guarding that recursive call to parseMain?
Related
I have a program that works. However, I wasn't exactly sure why it was compiling.
Here's my main function
module Main where
import System.Environment
import Cover5
main :: IO ()
main = do
args <- getArgs
let numGames = if null args then 13 else read $ head args
putStrLn $ "Making picks for " ++ show numGames ++ " games."
print $ run numGames
where the function run has the signature run :: Int -> RVar [(Int, Char)].
What is confusing is that there is no instance of Show for RVar [(Int,Char)] so I figure it shouldn't compile, but it does (see Travis-CI build and the related source for that commit of Main.hs). I can force a warning with this command:
cabal build --ghc-options="-fforce-recomp -fno-code"
Preprocessing executable 'cover5' for cover5-0.1.0.1...
[1 of 2] Compiling Cover5 ( src/Cover5.hs, nothing )
[2 of 2] Compiling Main ( src/Main.hs, nothing )
src/Main.hs:13:10: error:
• No instance for (Show (RVar [(Int, Char)]))
arising from a use of ‘print’
• In a stmt of a 'do' block: print $ run numGames
In the expression:
do { args <- getArgs;
let numGames = ...;
putStrLn $ "Making picks for " ++ show numGames ++ " games.";
print $ run numGames }
In an equation for ‘main’:
main
= do { args <- getArgs;
let numGames = ...;
putStrLn $ "Making picks for " ++ show numGames ++ " games.";
.... }
I'd like to "fix" this and have followed guidance from the question Convert Data.RVar.RVar [Char] to [Char]
So I add the appropriate imports for StdRandom and runRVar and write the following lines
results <- runRVar (run numGames) StdRandom
putStrLn $ show results
But I'm tripping over myself trying to find instances of MonadRandom IO for my usage:
src/Main.hs:13:21: error:
• No instance for (MonadRandom IO) arising from a use of ‘runRVar’
• In a stmt of a 'do' block:
results <- runRVar (run numGames) StdRandom
In the expression:
do { args <- getArgs;
let numGames = ...;
putStrLn $ "Making picks for " ++ show numGames ++ " games.";
results <- runRVar (run numGames) StdRandom;
.... }
In an equation for ‘main’:
main
= do { args <- getArgs;
let numGames = ...;
putStrLn $ "Making picks for " ++ show numGames ++ " games.";
.... }
So two questions:
Why does the original version compile and work?
What might you suggest I do to remove the warning and write this "correctly"?
The answer(s) were provided by #leftaroundabout in the comments. Posting an answer here for others new to Random in case it proves helpful
First Question
I had written the original program about 3 years ago. I had no idea how to print results from RVars. I chose to use Data.Random.Show.Unsafe. Apparently this module provides an instance of Show for RVar a. I didn't remember that.
In addition, I was implicitly exporting everything from my Cover5 module so the main module whose code is above was importing that instance.
So that's why print was working.
Second Question
Instances are available in Data.Random, which I wasn't importing.
The reason why I was getting errors but was thinking they were warnings is because I changed 2 (actually more than 2) things at once:
I restricted what was exported from Cover5 module so the Unsafe
instance of Show wasn't exported anymore
I ran the build with some arguments which I thought were just going to show more warnings than a traditional build.
I have revisited Haskell lateley and constructed a toy programming language parser/interpreter. Using Parsec for lexing and parsing and a separate interpreter. I'm running in to some issues with feeding the result from the parser to my interpreter and handle the potential error from both the interpreter and parser. I end up with something like this:
main = do
fname <- getArgs
input <- readFile (head fname)
case lparse (head fname) input of
Left msg -> putStrLn $ show msg
Right p -> case intrp p of
Left msg -> putStrLn $ show msg
Right r -> putStrLn $ show r
This dosn't look pretty at all. My problem is that lparse returns Either ParseError [(String, Stmt)] and itrp returns the type Either ItrpError Stmt so I'm having a real hard time feeding the Right result from the parser to the interpreter and at the same time bail and print the possible ParseError or IntrpError.
The closest to what i want is something like this
main = do
fname <- getArgs
input <- readFile (head fname)
let prog = lparse (head fname) input
(putStrLn . show) (intrp <$> prog)
But this will not surprisingly yield a nested Either and not print pretty either.
So are there any nice Haskell ideomatic way of doing this threading results from one computation to another and handling errors (Lefts) in a nice way without nesting cases?
Edit
adding types of lparse and itrp
lparse :: Text.Parsec.Pos.SourceName -> String -> Either Text.Parsec.Error.ParseError [([Char], Stmt)]
intrp :: [([Char], Stmt)] -> Either IntrpError Stmt
While not perfect, I'd create a helper function for embedding any Showable error from Either into MonadError:
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.Except
strErr :: (MonadError String m, Show e) => Either e a -> m a
strErr = either (throwError . show) return
Then if you have a computation that can fail with errors, like
someFn :: ExceptT String IO ()
someFn = strErr (Left 42)
you can run it (printing errors to stdout) as
main :: IO ()
main = runExceptT someFn >>= either putStrLn return
In your case it'd be something like
main = either putStrLn return <=< runExceptT $ do
fname <- liftIO getArgs
input <- liftIO $ readFile (head fname)
prog <- strErr $ lparse (head fname) input
r <- strErr $ interp prog
print r
Well, if you want to chain successful computations, you can always use >>= to do that. For instance in your case:
lparse (head fname) input >>= intrp
And if you want to print out either your error message you can use the either class that takes two handler functions, one for the case when you have Left a (error in your case) and another for Right b (in your case a successful thing). An example:
either (putStrLn . show) (putStrLn . show) (lparse (head fname) input >>= intrp)
And if anything fails in your chain (any step of your monadic chain becomes Left a) it stops and can for instance print out the error message in the above case.
I know there is a Paginator package for Yesod but I prefer a simpler UI so I was creating a simple pagination logic for my app. However, I couldn't figure out a way to convert the parameter value to Integer.
import Data.Text (unpack, singleton)
import Data.Maybe
one = singleton '1' -- convert char to Text, required by fromMaybe
getTestPanelR :: Handler Html
getTestPanelR = do
ptext <- lookupGetParam "p" -- guessing returns Maybe Text
p <- fromMaybe one ptext -- ??? does not work
-- pn <- ??? Once p is extracted successfully, how to convert to an integer?
s <- runDB $ selectList [] [Asc PersonName, LimitTo 10 , OffsetBy $ (pn - 1) * 10]
(widget, enctype) <- generateFormPost $ entryForm Nothing
defaultLayout $ do
$(widgetFile "person")
When I run the above Code I get the following error message:
No instance for (MonadHandler Maybe)
arising from a use of `lookupGetParam'
Possible fix: add an instance declaration for (MonadHandler Maybe)
In the second argument of `($)', namely `lookupGetParam "p"'
In a stmt of a 'do' block:
p <- fromMaybe one $ lookupGetParam "p"
In the expression:
...
When I write out 'ptext' using #{show ptext} it shows Just "1". Having gotten the GET parameter, how do I convert it to an integer so I can do pagination? (need to add 1 for 'next' and subtract 1 for 'prev')
FWIW, when I try this using GHCi, it works fine:
Prelude Data.Maybe Data.Text> let one = singleton '1'
Prelude Data.Maybe Data.Text> let x = Just $ singleton '5'
Prelude Data.Maybe Data.Text> let y = fromMaybe one x
Prelude Data.Maybe Data.Text> y
"5"
Prelude Data.Maybe Data.Text> read $ Data.Text.unpack y ::Int -- This is probably unsafe because I cannot trust 'y' in my web app
5
Update:
I tired #Ankur's suggestion pageNumber <- (lookupGetParam "p" >>= return . (read :: String -> Int) . fromMaybe "1") and I get the following error:
Couldn't match expected type `String' with actual type `Text'
Expected type: Maybe Text -> String
Actual type: Maybe Text -> Text
In the return type of a call of `fromMaybe'
In the second argument of `(.)', namely `fromMaybe "1"'
Build failure, pausing...
If change the "1" to one (Data.Text.singleton '1'), I still get the exact same error message.
Thanks!
lookupGetParam returns ParamValue which is type ParamValue = String. So basically it is String rather than Text.
Try this:
pageNumber <- (lookupGetParam "p" >>= return . (read :: String -> Int) . fromMaybe "1")
UPDATE:
Actually the latest version of lookupGetParam is Text based so adding the OverloadedStrings language extension should get the job done:
Put this {-# LANGUAGE OverloadedStrings #-} at the start of the code file and use:
pageNumber <- (lookupGetParam "p" >>= return . (read :: String -> Int) . unpack . fromMaybe "1")
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
I'm trying to work with Ubigraph in haskell, but I believe my problem is more generic. I'm trying to compile:
import Graphics.Ubigraph
import Control.Monad
import System.Posix.Unistd
main = do
h <- initHubigraph "http://127.0.0.1:20738/RPC2"
runHubigraph op h
op = do
clear
vs <- mapM (const newVertex) [0..200]
mapM_ (setVAttr (VShape Sphere)) vs
putStrLn "something"
let bind i = zipWithM (\a b -> newEdge (a,b)) vs (drop i vs ++ take i vs)
mapM_ bind [1..15]
mapM_ (removeVertex) vs
return ()
and I am getting
Couldn't match expected type `Control.Monad.Trans.Reader.ReaderT
Ubigraph IO a0'
with actual type `IO ()'
In the return type of a call of `putStrLn'
In a stmt of a 'do' expression: putStrLn "something"
In the expression:
do { clear;
vs <- mapM (const newVertex) [0 .. 200];
mapM_ (setVAttr (VShape Sphere)) vs;
putStrLn "something";
.... }
I can see how the type of op is being implied as something different from the return type of putStrLn, but I am not sure how I would reengineer this code to compile properly. Can I simply change the return type of the op function?
Thanks
Your call to putStrLn buried down in op is in the IO monad. You will need to lift it into the Ubigraph monad, using
liftIO $ putStrLn "foo"
Such lifting functions help you access monadic functions lower in the stack. In this case, Ubigraph is a Reader monad composed with the IO monad, and the IO monad is at the bottom. So things in IO must be lifted.
liftIO is from the MonadIO class, in the transformers package.