Picking a random line from a file in Haskell - haskell

I am trying to have Haskell pick a random line from a file and print it. My attempt is below:
import Data.Random.Extras (choice)
main :: IO ()
main = do
filecontents <- readFile "wordlist.txt"
let words = lines filecontents
let word = choice $ words
word >>= putStrLn
The last line is where the error occurs. >>= expects an IO String, but word is a Data.RVar.RVar String. (The variable is called `word' because each line should be one word.)
I have read the docs for RVar but after some hacking, I do not see how to solve my problem. Any ideas?
I am using ghc 7.6.3 from an installation of the Haskell Platform, OS X 10.9.
The complete error is below:
[ 01:46 PM (51) integral:thoth ~/Source/pwgen ] > ghc -o pwgen pwgen.hs
[1 of 1] Compiling Main ( pwgen.hs, pwgen.o )
pwgen.hs:40:3:
Couldn't match type `Data.RVar.RVarT
Data.Functor.Identity.Identity'
with `IO'
Expected type: IO String
Actual type: Data.RVar.RVar String
In the first argument of `(>>=)', namely `word'
In a stmt of a 'do' block: word >>= putStrLn
In the expression:
do { filecontents <- readFile "wordlist.txt";
let words = lines filecontents;
let word = choice $ words;
word >>= putStrLn }
Finally, I am aware that there are more efficient ways to pick a random line from a file. I'm just going for the bare minimum that works. I am also very much a Haskell beginner and may have some fundamental misconceptions, especially regarding IO and monads.

You can use
import Data.Random
and then modify main
main = do
...
let word = sample $ choice words
putStrLn =<< word
This works because when you import Data.Random it contains an instance of MonadRandom for IO and gives you sample as a convenient wrapper for runRVar with a generator obtained from the IO monad.

Related

Trouble shoot a Haskell program

Can anyone tell me what is the problem with this Haskell program
import Control.Monad
import Data.Char
main = do
contents <- getContents
putStrLn $ contents
putStr $ "shortLinesOnly version is " ++ (shortLinesOnly contents)
putStr $ "printOnlyLessChars version is " ++ (printOnlyLessChars contents)
shortLinesOnly :: String -> String
shortLinesOnly input =
let allLines = lines input
shortLines = filter (\line -> length line < 10) allLines
result = unlines shortLines
in result
--------------------the other way of doing this is -----------------
printOnlyLessChars contents = unlines $ filter (\a -> length a < 10) $ lines $ contents
The program works fine, but it fails when I try to print the contents (line 5). Why is it having problems printing the string via putStrLn
The error message I get is
* Couldn't match expected type `(String -> IO ())
-> t0 -> IO String'
with actual type `IO String'
* The function `getContents' is applied to one argument,
but its type `IO String' has none
In the expression: getContents putStrLn
Thanks,
This is the line that you need to focus on:
In the expression: getContents putStrLn
This is haskell showing you how it views your code, but your code doesn't look like that. This is almost always an indentation error. Check that you don't have an extra space or a tab where it doesn't belong.
As a suggestion when reading haskell type error messages there are three places to look, and you should scan all of them before fixating on a single one:
The type signature information -- do your types really match?
The expression information -- does the expression the compiler sees match your expectations, or do you need to add $ or parens
Is there a typo or indentation problem.
I frequently feel my brain starting to overheat as I try to read through a really messy Couldn't match expected type so before I get too upset over trying to read that part of the error message I carefully check the In the expression: part to make sure that there is an easy to fix issue with how I entered the code.

Read a single int from a file and print it

How do I create a program that reads a line from a file, parse it to an int and print it(ignoring exceptions of course). Is there anything like "read" but for IO String?
I've got this so far but I couldn't get around the IO types:
readFromFile = do
inputFile <- openFile "catalogue.txt" ReadMode
isbn <- read( hGetLine inputFile)
hClose inputFile
You can specify the type explicitly, change the read line to
isbn <- fmap read (hGetLine inputFile) :: IO Int
As hGetLine inputFile is of type IO String, you should use fmap to get "inside" to read as an Int.
You can use the readFile function to convert your file to a string.
main = do
contents <- readFile "theFile"
let value = read $ head $ lines contents::Int
print value
You should add better error detection, or this program will fail if there isn't a first line, or if the value is malformed, but this is the basic flow....
First, observe that reading stuff and then immediately printing it can result in mysterious errors:
GHCi, version 8.0.0.20160421: http://www.haskell.org/ghc/ :? for help
Prelude λ read "123"
*** Exception: Prelude.read: no parse
The reason is that you don't specify what type you want to read. You can counter this by using type annotations:
Prelude λ read "123" :: Integer
123
but it is sometimes easier to introduce a little helper function:
Prelude λ let readInteger = read :: String -> Integer
Prelude λ readInteger "123"
123
Now to the main problem. read( hGetLine inputFile) doesn't work because hGetLine inputFile returns and IO String and read needs a String. This can be solved in two steps:
line <- hGetLine inputFile
let isbn = readInteger line
Note two different constructs <- and let .. =, they do different things. Can you figure out exactly what?
As shown in another answer, you can do it in a less verbose manner:
isbn <- fmap readInteger (hGetLine inputFile)
which is great if you do a simple thing like read. But it is often desirable to explicitly name intermediate results. You can use <- and let .. = constructs in such cases.

Haskell - loop over user input

I have a program in haskell that has to read arbitrary lines of input from the user and when the user is finished the accumulated input has to be sent to a function.
In an imperative programming language this would look like this:
content = ''
while True:
line = readLine()
if line == 'q':
break
content += line
func(content)
I find this incredibly difficult to do in haskell so I would like to know if there's an haskell equivalent.
The Haskell equivalent to iteration is recursion. You would also need to work in the IO monad, if you have to read lines of input. The general picture is:
import Control.Monad
main = do
line <- getLine
unless (line == "q") $ do
-- process line
main
If you just want to accumulate all read lines in content, you don't have to do that. Just use getContents which will retrieve (lazily) all user input. Just stop when you see the 'q'. In quite idiomatic Haskell, all reading could be done in a single line of code:
main = mapM_ process . takeWhile (/= "q") . lines =<< getContents
where process line = do -- whatever you like, e.g.
putStrLn line
If you read the first line of code from right to left, it says:
get everything that the user will provide as input (never fear, this is lazy);
split it in lines as it comes;
only take lines as long as they're not equal to "q", stop when you see such a line;
and call process for each line.
If you didn't figure it out already, you need to read carefully a Haskell tutorial!
It's reasonably simple in Haskell. The trickiest part is that you want to accumulate the sequence of user inputs. In an imperative language you use a loop to do this, whereas in Haskell the canonical way is to use a recursive helper function. It would look something like this:
getUserLines :: IO String -- optional type signature
getUserLines = go ""
where go contents = do
line <- getLine
if line == "q"
then return contents
else go (contents ++ line ++ "\n") -- add a newline
This is actually a definition of an IO action which returns a String. Since it is an IO action, you access the returned string using the <- syntax rather than the = assignment syntax. If you want a quick overview, I recommend reading The IO Monad For People Who Simply Don't Care.
You can use this function at the GHCI prompt like this
>>> str <- getUserLines
Hello<Enter> -- user input
World<Enter> -- user input
q<Enter> -- user input
>>> putStrLn str
Hello -- program output
World -- program output
Using pipes-4.0, which is coming out this weekend:
import Pipes
import qualified Pipes.Prelude as P
f :: [String] -> IO ()
f = ??
main = do
contents <- P.toListM (P.stdinLn >-> P.takeWhile (/= "q"))
f contents
That loads all the lines into memory. However, you can also process each line as it is being generated, too:
f :: String -> IO ()
main = runEffect $
for (P.stdinLn >-> P.takeWhile (/= "q")) $ \str -> do
lift (f str)
That will stream the input and never load more than one line into memory.
You could do something like
import Control.Applicative ((<$>))
input <- unlines . takeWhile (/= "q") . lines <$> getContents
Then input would be what the user wrote up until (but not including) the q.

Haskell String to List of Strings using Words

I'm rather new to Haskell, and I'm currently using LearnYouAHaskell.
I am trying to take a string separated by white space, and break it into a list of smaller word strings.
My current program:
main = do
putStrLn "Insert a string to convert: "
-- Input string
line <- getLine
words line;
But in this case, it tells me I'm having an IO error.
TO my understanding, getLine is an action, and so since this is impure, I have to bind it to "line". Line is an accurate representation of getLine, which is an IO String.
However, shouldn't line be a string?
When I try to use words on line, it tells me
"Couldn't match expected type "IO a0" with actual type [String]
As if line isn't a string.
Furthermore, can I use :t line in the program itself when I make it to see if it's actual of the right type or not?
I apologize for the novice question, but I'm a bit stuck.
EDIT:
I did something similar in GHCI, and it tells me that my type is in fact a normal string.. I don't get it.
Prelude> line <- getLine
"Hello fellows"
Prelude> :t line
line :: String
Prelude> words line
["Hello","fellows"]
Why doesn't that work?
In haskell if you want to return a value, you have to say so:
main = do
putStrLn "Insert a string to convert: "
-- Input string
line <- getLine
return (words line)
words line isn't an IO action, it's a list of strings, so it can't be a statement in a do block.
return :: Monad m => a -> m a and in this case we can specialise it to the type a -> IO a and then to [String] -> IO [String]. Each of the statements in your do block must be IO statements.
Taking it further:
If you want to compile your program, you should have main :: IO(), which means you shouldn't return your list.
If, for example, you wanted to process those strings into a single string then output that, you could do
process :: [String] -> String
process xss = "I don't know, some answer"
main = do
putStrLn "Insert a string to convert: "
-- Input string
line <- getLine
putStrLn (process (words line))
although I'd personally write that last line as putStrLn $ process.words $ line.
Your interaction in GHCi
Prelude> line <- getLine
"Hello fellows"
Prelude> :t line
line :: String
Prelude> words line
["Hello","fellows"]
is using the fact that GHCi isn't actually just running in the IO monad. In GHCi, if your input is a valid line in a do block, it'll get run, but if it's pure code it'll get evaluated and printed. (An interactive interpreter like this is often called a REPL for Read-Eval-Print-Loop.)
Well, the question is what do you want to do with words line?
Having words line as a line inside a do block is doing nothing, but to get it to work you have to use return to wrap it up in the IO monad:
main = do
putStrLn "Insert a string to convert: "
-- Input string
line <- getLine
return (words line);
Anyway, perhaps you want to print it instead?
main = do
putStrLn "Insert a string to convert: "
-- Input string
line <- getLine
print (words line);

parsec error in haskelwiki tutorial

I was following the code in http://www.haskell.org/haskellwiki/Hitchhikers_guide_to_Haskell, and the code (in chapter 2) gives an error. There is no author name/email mentioned with the tutorial, so I am coming here for advise. The code is below, and the error occurs on the "eof" word.
module Main where
import Text.ParserCombinators.Parsec
parseInput =
do dirs <- many dirAndSize
eof
return dirs
data Dir = Dir Int String deriving Show
dirAndSize =
do size <- many1 digit
spaces
dir_name <- anyChar `manyTill` newline
return (Dir (read size) dir_name)
main = do
input <- getContents
putStrLn ("Debug: got inputs: " ++ input)
That tutorial was written a long time ago, when parsec was simple. Nowadays, since parsec-3, the library can wrap monads, so you now have to specify (or otherwise disambiguate) the type to use at some points. This is one of them, giving eof e.g. the expression type signature eof :: Parser () makes it compile.

Resources