load and use code file in haskell - haskell

I'm just starting to learn haskell and i dont really understand how to use files i created with an ordinary editor in the GHCi interpreter...
This is my file list-comprehension.hs
main = do
let substantive = [" Student", " Professor", " Tutor"]
let adjektive = ["fauler", "fleissiger", "hilfreicher"]
let tupel = [a ++ s | a <- adjektive, s <- substantive]
return (tupel)
When I load the file in GHCi it works alright, but then I cant actually use it. So when I try to execute tupel, it wont work.
Prelude> :load list-comprehension.hs
[1 of 1] Compiling Main ( list-comprehension.hs, interpreted )
Ok, modules loaded: Main.
*Main> tupel
<interactive>:3:1: error: Variable not in scope: tupel
This also happens when I try to get the other variables.
I have researched this a lot, but I cant find out whats wrong with my file or how this generally works...
I'm not at all sure about the "main = do" and the "return" part, but this is the only beginning and end that doesnt produce a parse error when loading .

GHCi only has the top level definitons from a file in scope. Try this:
main :: IO ()
main = print tupel
substantive :: [String]
substantive = [" Student", " Professor", " Tutor"]
adjektive :: [String]
adjektive = ["fauler", "fleissiger", "hilfreicher"]
tupel :: [String]
tupel = [a ++ s | a <- adjektive, s <- substantive]

Related

How to correctly parse arguments with Haskell?

I'm trying to learn how to work with IO in Haskell by writing a function that, if there is a flag, will take a list of points from a file, and if there is no flag, it asks the user to enter them.
dispatch :: [String] -> IO ()
dispatch argList = do
if "file" `elem` argList
then do
let (path : otherArgs) = argList
points <- getPointsFile path
else
print "Enter a point in the format: x;y"
input <- getLine
if (input == "exit")
then do
print "The user inputted list:"
print $ reverse xs
else (inputStrings (input:xs))
if "help" `elem` argList
then help
else return ()
dispatch [] = return ()
dispatch _ = error "Error: invalid args"
getPointsFile :: String -> IO ([(Double, Double)])
getPointsFile path = do
handle <- openFile path ReadMode
contents <- hGetContents handle
let points_str = lines contents
let points = foldl (\l d -> l ++ [tuplify2 $ splitOn ";" d]) [] points_str
hClose handle
return points
I get this: do-notation in pattern Possibly caused by a missing 'do'?` after `if "file" `elem` argList.
I'm also worried about the binding issue, assuming that I have another flag that says which method will be used to process the points. Obviously it waits for points, but I don't know how to make points visible not only in if then else, constructs. In imperative languages I would write something like:
init points
if ... { points = a}
else points = b
some actions with points
How I can do something similar in Haskell?
Here's a fairly minimal example that I've done half a dozen times when I'm writing something quick and dirty, don't have a complicated argument structure, and so can't be bothered to do a proper job of setting up one of the usual command-line parsing libraries. It doesn't explain what went wrong with your approach -- there's an existing good answer there -- it's just an attempt to show what this kind of thing looks like when done idiomatically.
import System.Environment
import System.Exit
import System.IO
main :: IO ()
main = do
args <- getArgs
pts <- case args of
["--help"] -> usage stdout ExitSuccess
["--file", f] -> getPointsFile f
[] -> getPointsNoFile
_ -> usage stderr (ExitFailure 1)
print (frobnicate pts)
usage :: Handle -> ExitCode -> IO a
usage h c = do
nm <- getProgName
hPutStrLn h $ "Usage: " ++ nm ++ " [--file FILE]"
hPutStrLn h $ "Frobnicate the points in FILE, or from stdin if no file is supplied."
exitWith c
getPointsFile :: FilePath -> IO [(Double, Double)]
getPointsFile = {- ... -}
getPointsNoFile :: IO [(Double, Double)]
getPointsNoFile = {- ... -}
frobnicate :: [(Double, Double)] -> Double
frobnicate = {- ... -}
if in Haskell doesn't inherently have anything to do with control flow, it just switches between expressions. Which, in Haskell, happen to include do blocks of statements (if we want to call them that), but you still always need to make that explicit, i.e. you need to say both then do and else do if there are multiple statements in each branch.
Also, all the statements in a do block need to be indented to the same level. So in your case
if "file" `elem` argList
...
if "help" `elem` argList
Or alternatively, if the help check should only happen in the else branch, it needs to be indented to the statements in that do block.
Independent of all that, I would recommend to avoid parsing anything in an IO context. It is usually much less hassle and easier testable to first parse the strings into a pure data structure, which can then easily be processed by the part of the code that does IO. There are libraries like cmdargs and optparse-applicative that help with the parsing part.

How the input should go inside the ghci interpreter?

I'm trying a program from this tutorial. The program goes like this:
type Name = String
type PriceInCents = Int
type ShoppingListItem = (Name, PriceInCents)
type ShoppingList = [ShoppingListItem]
shoppingList :: ShoppingList
shoppingList = [ ("Bananas", 300)
, ("Chocolate", 250)
, ("Milk", 300)
, ("Apples", 450)
]
sumShoppingList :: ShoppingList -> PriceInCents
sumShoppingList [] = 0
sumShoppingList (x:xs) = getPriceFromItem x
+ sumShoppingList xs
getPriceFromItem :: ShoppingListItem -> PriceInCents
getPriceFromItem (_, price) = price
main :: IO ()
main = putStrLn ("Price of shopping list is "
++ show (sumShoppingList shoppingList)
++ " cents.")
I tried running it and there were no errors but I don't know what to input. I tried but I guess I got it wrong since I got this error:
ERROR - Undefined data constructor
Can anyone tell me what I should input?
The program, as it's written, doesn't take any input. Compiling with ghc will produce a working executable. Alternatively, running with runhaskell should work just as well.
I suspect, based on your question, that you're running inside the ghci interpreter. In this case, you can load a file using :l filename.hs (or :r to reload) and then run your main function by simply invoking main.

How to print the results of sampling an RVar

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.

Read in multiple lines from standard input with arguments in Haskell

I'm trying to read in multiple lines from standard input in Haskell, plus one argument, then do something with the current line and write something to the standard output.
In my case I am trying to normalize lambda expressions. The program may receive 1 or more lambda expressions to normalize and then it has to write the result (normalized form or error) to the standard output. And the program may receive an argument (the max number of reductions). Here is the main function:
main :: IO ()
main = do
params <- getArgs
fullLambda <- getLine
let lambda = convertInput fullLambda
let redNum | (length params) == 1 = read (head params)
| otherwise = 100
case (parsing lambda) of
Left errorExp -> putStrLn ("ERROR: " ++ lambda)
Right lambdaExp -> do
let normalizedLambdaExp = reduction lambdaExp redNum
if (isNormalForm normalizedLambdaExp) && (isClosed lambdaExp)
then putStrLn ("OK: " ++ show normalizedLambdaExp)
else putStrLn ("ERROR: " ++ lambda)
where
convertInput :: String -> String
convertInput ('\"':xs) = take ((length xs) - 2) xs
convertInput input = input
So this code handles one line and completes the reductions and then writes something to the standard output. How can I change this to handle multiple lines? I've read about replicateM but I can't seem to grasp it. My mind is very OO so I was thinking maybe some looping somehow, but that is surely not the preferred way.
Also, this program has to be able to run like this:
echo "(\x.x) (\x.x)" | Main 25
And will produce:
OK: (\x.x)
And if there are multiple lines, it has to produce the same kind of output for each line, in new lines.
But also has to work without the argument, and has to handle multiple lines. I spent time on google and here, but I'm not sure how the argument reading will happen. I need to read in the argument once and the line(s) once or many times. Does someone know a not too lengthy solution to this problem?
I've tried it like this, too (imperatively):
main :: IO ()
main = do
params <- getArgs
mainHelper params
main
mainHelper :: [String] -> IO ()
mainHelper params = do
fullLambda <- getLine
And so on, but then it puts this to the standard output as well:
Main: <stdin>: hGetLine: end of file
Thank you in advance!
It appears you want to:
Parse a command line option which may or may not exist.
For each line of input process it with some function.
Here is an approach using lazy IO:
import System.Environment
import Control.Monad
main = do args <- getArgs
let option = case args of
[] -> ... the default value...
(a:_) -> read a
contents <- getContents
forM_ (lines contents) $ \aline -> do
process option aline
I am assuming your processing function has type process :: Int -> String -> IO (). For instance, it could look like:
process :: Int -> String -> IO ()
process option str = do
if length str < option
then putStrLn $ "OK: " ++ str
else putStrLn $ "NOT OK: line too long"
Here's how it works:
contents <- getContents reads all of standard input into the variable contents
lines contents breaks up the input into lines
forM_ ... iterates over each line, passing the line to the process function
The trick is that getContents reads standard input lazily so that you'll get some output after each line is read.
You should be aware that there are issues with lazy IO which you may run into when your program becomes more complex. However, for this simple use case lazy IO is perfectly fine and works well.

Parsing simple binary file with Data.Binary.Get in Haskell

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?

Resources