Cannot IO in haskell - haskell

I have a function to count no of times each word is repeated in a string:
keywords :: String -> [String]
keywords = words . map (\x -> if isAlpha x then x else ' ')
count :: Ord a => [a] -> [(a,Int)]
count = map (head &&& length) . group . sort
wordcount = count . keywords
which works perfectly.
I want to read a text file as input to this function using the IO. I did the coding like this:
wordcou :: IO ()
wordcou =
do
putStr "Please text file name :"
textfile <- getLine
text <- readFile textfile
let result = wordcount text
putStr result
The IO function is giving me an error. Can any one help me fix this error please?
error is
ERROR file:.\project.hs:194 - Type error in application
*** Expression : putStr result
*** Term : result
*** Type : [([Char],Int)]
*** Does not match : [Char]

Your code has 2 problems:
wordcou should be of type IO (), since it doesn't return anything
putStr should be replaced by print because result is not a String
After these changes, your code compiles and runs fine.

Add a type declaration for wordcount.
Look at the types and definitions of of putStr and print.
Do you see the difference?

Related

Pattern matching failing for valid pattern

I have the following code:
import Debug.Trace (trace)
mtrace :: Show a => String -> a -> a
mtrace msg value =
trace (msg ++ show value) value
isVowel :: Char -> Bool
isVowel = (`elem` "AEIOU")
vowelSlice :: String -> ([Maybe Char], String)
vowelSlice "" = ([], [])
vowelSlice (c:s)
| isVowel c = (Nothing:chars, c:vowels)
| otherwise = (Just c:chars, vowels)
where (chars, vowels) = vowelSlice s
stringTogether :: [Maybe Char] -> String -> String
stringTogether [] "" = ""
stringTogehter (Just c:cs) vowels = c:stringTogether cs vowels
stringTogehter (Nothing:cs) (v:vs) = v:stringTogether cs vs
process :: String -> String
process s = stringTogether (mtrace "chars: " chars) (mtrace "vowels: " cycledVowels)
where
(chars, vowels) = vowelSlice s
cycledVowels = tail vowels ++ [head vowels]
main :: IO ()
main = do
line <- getLine
putStrLn $ process line
for testing I run my file using the runhaskell command and the I enter HELLO PEOPLE as user input once the program is running. I'm expecting the output: HELLE POEPLO or something similar since my program is meant to shift the vowels only. My program works fine until it tries to run the stringTogether method. Specifically the issue lies with the pattern matching, I have an array:
[Just 'H',Nothing,Just 'L',Just 'L',Nothing,Just ' ',Just 'P',Nothing,Nothing,Just 'P',Just 'L',Nothing]
and a pattern
(Just c:cs) vowels that I expect it to match but somehow it doesn't seem to work. When I run the code and enter HELLO WORLD I receive the following error:
18:1-25: Non-exhaustive patterns in function stringTogether I logged a few things using the trace module and everything looks as expected before entering the stringTogether function
I'm probably overlooking something really obvious but I just can't get my head around why the pattern match won't work, I hope someone is able to help. Thanks in advance!
The pattern match fails because of a typo, 2 separate functions were defined instead of the intended one: stringTogether and stringTogehter. The patterns were valid but the compiler failed to find them because they had mismatching names. The function technically stringTogether only had one pattern [] "" so when the list was passed it raised the 18:1-25: Non-exhaustive patterns in function stringTogether error.

Remove digits from file

I'm trying to create new file without digits in strings
main :: IO ()
main = do
contents <- readFile "input1.txt"
putStr (process contents)
check = if isDigit x
x = "a"
process :: String -> String
process = map check
but getting that error: "Syntax error in expression (unexpected symbol "process")". What am I doing wrong?
In Haskell, if “statements” are actually expressions and must return a value. So you need to have an else block.
import Data.Char (isDigit)
check x = if isDigit x then 'a' else x
process :: String -> String
process = map check
main :: IO ()
main = do
contents <- readFile "input1.txt"
putStr (process contents)
Also, if you want to remove the digits, then filter is a better option than using map check. So you can refactor process to
process :: String -> String
process = filter (not . isDigit)

Haskell : parsing command line arguments

I want to write a program in Haskell which will take command line arguments. For example: to print the sum of the first 6 elements of the series (which will be calculated by another function), I will write:
sum 6
and the correct answer should be displayed. I have to do this for another 5-7 different commands by checking the command line. How should I do it? Is switch case a good idea? If so, can anyone tell me how it can be done.
SOLUTION:
main = do
--Get some input
f <- getLine
--Split the input into 2 strings; one is COMMAND field and other is the ARGUMENT field using the condition the there is one space between them
let cmd = takeWhile (/=' ') f
let arg = dropWhile (/=' ') f
let val = tail arg
let p = read val::Int
--Check for the COMMAND
case cmd of
"SUM" -> if (empty arg) then do { putStrLn "ERR"; exitWith ExitSuccess} else if (check val) then print (sum1 p) else do { putStrLn "ERR"; exitWith ExitSuccess}
"NTH" -> if (empty arg) then do { putStrLn "ERR"; exitWith ExitSuccess} else if (check val) then print (fact p) else do { putStrLn "ERR"; exitWith ExitSuccess}
"BOUNDS" -> if (empty arg) then do { putStrLn "ERR"; exitWith ExitSuccess} else if (check val == False) then do { putStrLn "ERR"; exitWith ExitSuccess} else if (p > 1) then do { print c; print d} else do { putStrLn"ERR"; exitWith ExitSuccess}
"QUIT" -> if (empty arg) then exitWith ExitSuccess else do { putStrLn "ERR"; exitWith ExitSuccess}
_ -> do { putStrLn "ERR"; exitWith ExitSuccess}
--Repeat main until QUIT
main
optparse-applicative is one example of a library which supports this kind of sub-command parsing.
Let's say your program has two commands for now, "sum" and "mean". We can represent the command and its arguments using an algebraic data type, here called Command.
import Data.Monoid (mconcat)
import Options.Applicative
data Command = Sum Integer
| Mean Integer
-- et cetera
We can build a parser which accepts all of the commands, by writing parsers for each individual command, and composing them.
parseNumber :: Parser Integer
parseNumber = argument auto (metavar "N")
sumParser :: ParserInfo Command
sumParser = info (Sum <$> parseNumber)
(progDesc "Sum first N elements in series")
meanParser :: ParserInfo Command
meanParser = info (Mean <$> parseNumber)
(progDesc "Mean of first N elements in series")
commandParser :: ParserInfo Command
commandParser = info commands $ progDesc "My program" where
commands = subparser $ mconcat [
command "sum" sumParser
, command "mean" meanParser
]
If you are wondering what Parser and ParserInfo are about: Usually we build a Parser, then put it into a ParserInfo, using the info combinator to decorate it with additional information about how it should be run (for example, with progDesc). Parsers may be composed with other Parsers, typically using the applicative combinators, but a ParserInfo is only a Functor, as it represents the entry point to the program.
Since each command is like a little sub-program, we need a ParserInfo for each one. The command and subparser combinators let us take some ParserInfos and wrap them up in a Parser, turning multiple entry points into one.
Once we have a result from the parser, we can dispatch to the appropriate routine by pattern matching on the result.
main :: IO ()
main = do
cmd <- execParser commandParser
case cmd of
Sum n -> return () -- TODO perform sum command
Mean n -> return () -- TODO perform mean command
Of course, if you have the time and the need, is much better to use a command line parser library than a switch case. A proper parser gives you the ability to have flags in any order, automatic documenation etc ... Although if you don't need any of this now, you might need it later.
However, pattern matching allows you check value(s) within a list, but although the size of the list, and this at the same time. This makes writing poor man command line parsing dead-easy in Haskell.
Example
main = do
args <- getArg
case args of
["command1", a, b] -> command1 a b -- 2 argument
["command2", a ] -> command2 a -- 1 argument
"command3":as -> command3 as -- n arguments
otherwise -> putStrLn "Please read the code to see which arguments are acceptable :-)"
So even though I would propably recommend using a parsing library, if you only need a couple of options without flags , and don't have time to learn/choose one, a simple case ... of is pretty neat and much quicker/simpler to write.
You can write your own simple applicative-style parser in just a few lines. The idea is: accept a list of string pairs, where the first string is an option name and the second string is an option value, lookup for a current option name, and if it's found, treat the associated value somehow and delete the pair from the list. If it's not found, return Nothing. So Parser is defined like this:
type Parser = StateT [(String, String)] Maybe
And here is the main function:
option :: (String -> Maybe a) -> String -> Parser a
option f str = StateT $ \xs -> do
(v, xs') <- lookupDelete str xs
v' <- f v
return (v', xs')
where lookupDelete does what it says. Actual option parsers are:
sopt :: String -> Parser String
sopt = option Just
opt :: Read a => String -> Parser a
opt = option $ reads >>> listToMaybe >=> finish
finish (x, []) = Just x
finish _ = Nothing
The opt parser tries to read a string, and it succeeds if the string is read fully.
optPairs [] = Just []
optPairs (('-':'-':name):opt:xs) = ((name, opt) :) <$> optPairs xs
optPairs _ = Nothing
This function splits input into pairs. And lastly
parse :: Parser a -> String -> Maybe a
parse p = words >>> optPairs >=> runStateT p >=> finish
Here is an example:
data SubCommand = SubCommand String (Double, Double)
deriving (Show)
data Command = Sum [Integer]
| Sub SubCommand
deriving (Show)
subcommandParser :: Parser SubCommand
subcommandParser = SubCommand <$> sopt "str" <*> opt "dbls"
commandParser :: Parser Command
commandParser = Sum <$> opt "sum" <|> Sub <$> subcommandParser
main = mapM_ (print . parse commandParser)
[ "--sum [1,2,3,4]"
, "--str option --dbls (2.2,0)"
, "--dbls (2.2,0) --str option"
, "--smth smth"
]
Results in
Just (Sum [1,2,3,4])
Just (Sub (SubCommand "option" (2.2,0.0)))
Just (Sub (SubCommand "option" (2.2,0.0)))
Nothing
The whole code: http://lpaste.net/114365

haskell evaluation $ sign

I am going through 'learn you some haskell' and I have written following application:
import System.IO
main = do
filename <- getLine
handle <- openFile filename ReadMode
content <- hGetContents handle
putStr . unlines . (map isLong) . lines $ content
hClose handle
isLong :: String -> String
isLong x = if length x > 10 then x ++ " long enough" else x ++ " could be better!"
And it works but when I remove "$" between lines and content a compilation fails.
Could you help me understand why this is wrong?
I was thinking that I compose statements with dots and I get a function (String -> IO ()) and I apply it to the "content" but why is "$" needed here?.
Thanks!
The operator (.) has type (b -> c) -> (a -> b) -> a -> c.... Its first two inputs must be functions.
lines content, however, is of type [String], not a function, hence f . lines content will fail. The compiler treats it as
f . (lines content)
By adding the ($), you change the precedence, and it becomes
f . lines $ content = (f . lines) $ content
which works, because f and lines are both functions.
The dollar sign in haskell is used for application of a function on a value.
Its backround is, that you do not need complicated parentheses in terms.

Couldn't match type `[]' with `IO' -- Haskell

I'm beginner in Haskell. In this task i'm performing the split operation but i'm facing problem because of type mis match. I'm reading data from text file and the data is in table format. Ex. 1|2|Rahul|13.25. In this format. Here | is delimiter so i want to split the data from the delimiter | and want to print 2nd column and 4th column data but i'm getting the error like this
"Couldn't match type `[]' with `IO'
Expected type: IO [Char]
Actual type: [[Char]]
In the return type of a call of `splitOn'"
Here is my code..
module Main where
import Data.List.Split
main = do
list <- readFile("src/table.txt")
putStrLn list
splitOn "|" list
Any help regarding this will appreciate.. Thanks
The problem is that you're trying to return a list from the main function, which has a type of IO ().
What you probably want to do is print the result.
main = do
list <- readFile("src/table.txt")
putStrLn list
print $ splitOn "|" list
Not Haskell, but it looks like a typical awk task.
cat src/table.txt | awk -F'|' '{print $2, $4}'
Back to Haskell the best I could find is :
module Main where
import Data.List.Split(splitOn)
import Data.List (intercalate)
project :: [Int] -> [String] -> [String]
project indices l = foldl (\acc i -> acc ++ [l !! i]) [] indices
fromString :: String -> [[String]]
fromString = map (splitOn "|") . lines
toString :: [[String]] -> String
toString = unlines . map (intercalate "|")
main :: IO ()
main = do
putStrLn =<<
return . toString . map (project [1, 3]) . fromString =<<
readFile("table.txt")
If not reading from a file, but from stdin, the interact function could be useful.

Resources