String vs Char mismatch in haskell - haskell

I'm getting an error about a type mismatch:
Main.hs:47:28:
Couldn't match type ‘[Char]’ with ‘Char’
Expected type: IO Char
Actual type: IO String
In the first argument of ‘liftIO’, namely ‘prompt’
In the second argument of ‘($)’, namely ‘liftIO prompt’
and struggling to understand why an IO Char is expected. Since prompt does type-check as IO String in line 46, and I thought, perhaps mistakenly, that liftIO would turn it to a String as suggested in this answer.
module Main where
import Syntax
import Parser
import Eval
import Pretty
import Counter
import Control.Monad
import Control.Monad.Trans
import System.Console.Haskeline
import Control.Monad.State
showStep :: (Int, Expr) -> IO ()
showStep (d, x) = putStrLn ((replicate d ' ') ++ "=> " ++ ppexpr x)
process :: Counter -> String -> InputT (StateT [String] IO) ()
process c line =
if ((length line) > 0)
then
if (head line) /= '%'
then do
modify (++ [line])
let res = parseExpr line
case res of
Left err -> outputStrLn $ show err
Right ex -> do
let (out, ~steps) = runEval ex
--mapM_ showStep steps
out_ps1 c $ out2iout $ show out
else do
let iout = handle_cmd line
out_ps1 c iout
-- TODO: don't increment counter for empty lines
else do
outputStrLn ""
out2iout :: String -> IO String
out2iout s = return s
out_ps1 :: Counter -> IO String -> InputT (StateT [String] IO) ()
out_ps1 c iout = do
--out <- liftIO iout
let out_count = c 0
let prompt = (getPrompt out_count iout) :: IO String
outputStrLn $ liftIO prompt
outputStrLn ""
getPrompt :: IO Int -> IO String -> IO String
getPrompt ion iout = do
n <- ion
out <- iout
return $ "Out[" ++ (show n) ++ "]: " ++ out
handle_cmd :: String -> IO String
handle_cmd line = if line == "%hist"
then
evalStateT getHist []
else
return "unknown cmd"
joinHist :: IO [String] -> IO String
joinHist ixs = do
xs <- ixs
return $ unlines xs
getHist :: StateT [String] IO String
getHist = do
hist <- lift get
let hists = (zip [(1::Int)..] hist) :: [(Int, String)]
return $ combineHist hists
combineHist :: [(Int, String)] -> String
combineHist hists = unlines $ map (\(i, h) -> show i ++ ": " ++ show h) hists
main :: IO ()
main = do
c <- makeCounter
repl c
repl :: Counter -> IO ()
repl c = evalStateT (runInputT defaultSettings(loop c)) []
loop :: Counter -> InputT (StateT [String] IO) ()
loop c = do
minput <- getLineIO $ in_ps1 $ c
case minput of
Nothing -> return ()
Just input -> process c input >> loop c
getLineIO :: (MonadException m) => IO String -> InputT m (Maybe String)
getLineIO ios = do
s <- liftIO ios
getInputLine s
in_ps1 :: Counter -> IO String
in_ps1 c = do
let ion = c 1
n <- ion
let s = "Untyped: In[" ++ (show n) ++ "]> "
return s
More context can be found here.

You are passing an IO action in place of a String to outputStrLn. You should instead do:
prompt <- liftIO $ getPrompt out_count iout
outputStrLn prompt
to obtain the String from the IO action, using liftIO and then passing this to outputStrLn.

Related

putStr inside IO () function

How to call IO () function inside another IO () function? I want to print to standard output then call function to do the same.
For example,
p :: String -> IO ()
p [x] = putStr x
p xs = q xs
q :: String -> IO ()
q (x:xs) = putStr x ++ p xs
Your first problem is with typing
p [x] = putStr x
{- putStr :: String -> IO ()
x :: Char, not a String
-}
and
q (x:xs) = putStr x ++ p xs
{- (++) :: [a] -> [a] -> [a]
putStr x :: IO (), not a list of anything.
-}
Let's look at q first, since it follows from p. You're breaking it down into characters, so you should use putChar rather than putStr
Also we're looking at sequencing actions, so we should either use (>>) or (>>=) depending on whether or not you need the result. In this case the result is a value of the unit type (()) which is a useless result and safe to ignore.
q :: String -> IO ()
q (x:xs) = putChar x >> p xs
{- or using `do` notation:
q (x:xs) = do
putChar x
p xs
-}
p can be changed likewise to use putChar rather than putStr
p :: String -> IO ()
p [x] = putChar x
p xs = q xs
though be aware that you haven't matched an empty list on either p or q.
About this time you should notice that substituting putChar for putStr just so you can break strings down to Chars is kind of backward thinking. p = putStr and you're done. However, if you're committed to this backward thinking:
import Control.Monad (foldM_, mapM_)
p = foldM_ (\_ x -> putChar x) ()
-- or
p = foldM_ ((putChar .) . flip const) ()
-- or
p = mapM_ putChar

combining StateT with InputT

It is a follow-up to this question. I'm trying to combine shell from #ErikR's answer in my InputT loop.
main :: IO [String]
main = do
c <- makeCounter
execStateT (repl c) []
repl :: Counter -> StateT [String] IO ()
repl c = lift $ runInputT defaultSettings loop
where
loop = do
minput <- getLineIO $ in_ps1 $ c
case minput of
Nothing -> lift $ outputStrLn "Goodbye."
Just input -> (liftIO $ process c input) >> loop
getLineIO :: (MonadException m) => IO String -> InputT m (Maybe String)
getLineIO ios = do
s <- liftIO ios
getInputLine s
And getting an error
Main.hs:59:10:
Couldn't match type ‘InputT m0’ with ‘IO’
Expected type: StateT [String] IO ()
Actual type: StateT [String] (InputT m0) ()
Relevant bindings include
loop :: InputT (InputT m0) () (bound at Main.hs:61:3)
In the expression: lift $ runInputT defaultSettings loop
In an equation for ‘repl’:
repl c
= lift $ runInputT defaultSettings loop
where
loop
= do { minput <- getLineIO $ in_ps1 $ c;
.... }
Main.hs:62:5:
No instance for (Monad m0) arising from a do statement
The type variable ‘m0’ is ambiguous
Relevant bindings include
loop :: InputT (InputT m0) () (bound at Main.hs:61:3)
Note: there are several potential instances:
instance Monad (Text.Parsec.Prim.ParsecT s u m)
-- Defined in ‘Text.Parsec.Prim’
instance Monad (Either e) -- Defined in ‘Data.Either’
instance Monad Data.Proxy.Proxy -- Defined in ‘Data.Proxy’
...plus 15 others
In a stmt of a 'do' block: minput <- getLineIO $ in_ps1 $ c
In the expression:
do { minput <- getLineIO $ in_ps1 $ c;
case minput of {
Nothing -> lift $ outputStrLn "Goodbye."
Just input -> (liftIO $ process c input) >> loop } }
In an equation for ‘loop’:
loop
= do { minput <- getLineIO $ in_ps1 $ c;
case minput of {
Nothing -> lift $ outputStrLn "Goodbye."
Just input -> (liftIO $ process c input) >> loop } }
The full code can be found here, it's based on Write you a haskell.
I know haskelline has a built-in support for history, but I'm trying to implement it myself as an exercise.
Feel free to suggest replacements for the monad transformers to get the same functionality.
My Real Problem
I'd like to add ipython like capabilities to the lambda REPL in Write You a Haskell, namely:
I. A counter for input and output, that will appear in the prompt, i.e
In[1]>
Out[1]>
This is already done.
II. Save each command to history (automatically), and display all previous commands using a special command, e.g. histInput (same as hist in ipython). Also, save a history of all output results and display them using histOutput. This is what I'm trying to do in this question (input history only for the moment).
III. Reference previous inputs and outputs, e.g. if In[1] was x, then In[1] + 2 should be substituted by x + 2, and likewise for the output.
Update
I've tried to combine #ErikR's answer, and temporarily disabled showStep, coming up with:
module Main where
import Syntax
import Parser
import Eval
import Pretty
import Counter
import Control.Monad
import Control.Monad.Trans
import System.Console.Haskeline
import Control.Monad.State
showStep :: (Int, Expr) -> IO ()
showStep (d, x) = putStrLn ((replicate d ' ') ++ "=> " ++ ppexpr x)
process :: Counter -> String -> InputT (StateT [String] IO) ()
process c line =
if ((length line) > 0)
then
if (head line) /= '%'
then do
modify (++ [line])
let res = parseExpr line
case res of
Left err -> outputStrLn $ show err
Right ex -> do
let (out, ~steps) = runEval ex
--mapM_ showStep steps
out_ps1 c $ out2iout $ show out
else do
let iout = handle_cmd line
out_ps1 c iout
-- TODO: don't increment counter for empty lines
else do
outputStrLn ""
out2iout :: String -> IO String
out2iout s = return s
out_ps1 :: Counter -> IO String -> InputT (StateT [String] IO) ()
out_ps1 c iout = do
out <- liftIO iout
let out_count = c 0
outputStrLn $ "Out[" ++ (show out_count) ++ "]: " ++ out
outputStrLn ""
handle_cmd :: String -> IO String
handle_cmd line = if line == "%hist"
then
evalStateT getHist []
else
return "unknown cmd"
getHist :: StateT [String] IO String
getHist = do
hist <- lift get
forM_ (zip [(1::Int)..] hist) $ \(i, h) -> do
show i ++ ": " ++ show h
main :: IO ()
main = do
c <- makeCounter
repl c
repl :: Counter -> IO ()
repl c = evalStateT (runInputT defaultSettings(loop c)) []
loop :: Counter -> InputT (StateT [String] IO) ()
loop c = do
minput <- getLineIO $ in_ps1 $ c
case minput of
Nothing -> return ()
Just input -> process c input >> loop c
getLineIO :: (MonadException m) => IO String -> InputT m (Maybe String)
getLineIO ios = do
s <- liftIO ios
getInputLine s
in_ps1 :: Counter -> IO String
in_ps1 c = do
let ion = c 1
n <- ion
let s = "Untyped: In[" ++ (show n) ++ "]> "
return s
which still doesn't compile:
Main.hs:59:5:
Couldn't match type ‘[]’ with ‘StateT [String] IO’
Expected type: StateT [String] IO String
Actual type: [()]
In a stmt of a 'do' block:
forM_ (zip [(1 :: Int) .. ] hist)
$ \ (i, h) -> do { show i ++ ": " ++ show h }
In the expression:
do { hist <- lift get;
forM_ (zip [(1 :: Int) .. ] hist) $ \ (i, h) -> do { ... } }
In an equation for ‘getHist’:
getHist
= do { hist <- lift get;
forM_ (zip [(1 :: Int) .. ] hist) $ \ (i, h) -> ... }
I'm going to take a guess at what you are trying to do.
This program recognizes the following commands:
hist -- show current history
add xxx -- add xxx to the history list
clear -- clear the history list
count -- show the count of history items
quit -- quit the command loop
Program source:
import System.Console.Haskeline
import Control.Monad.Trans.Class
import Control.Monad.Trans.State.Strict
import Control.Monad
main :: IO ()
main = evalStateT (runInputT defaultSettings loop) []
loop :: InputT (StateT [String] IO) ()
loop = do
minput <- getInputLine "% "
case minput of
Nothing -> return ()
Just "quit" -> return ()
Just input -> process input >> loop
process input = do
let args = words input
case args of
[] -> return ()
("hist": _) -> showHistory
("add" : x : _) -> lift $ modify (++ [x])
("clear": _) -> lift $ modify (const [])
("count": _) -> do hs <- lift get
outputStrLn $ "number of history items: " ++ show (length hs)
_ -> outputStrLn "???"
showHistory = do
hist <- lift get
forM_ (zip [(1::Int)..] hist) $ \(i,h) -> do
outputStrLn $ show i ++ " " ++ h
The first error is because you have declared
main :: IO ()
but also
execStateT (...) :: IO [String]
execStateT returns the computation's final state, and your state is of type [String]. Usually this is fixed by just not declaring a type for main and letting it be inferred to be IO a for some a. The second one I'm not sure about, but maybe it's the same thing.
The code you have here compiles, and it defines process as:
process :: Counter -> String -> IO ()
To create a version of process with this signature:
Counter -> String -> InputT (StateT [String] IO) ()
just use liftIO:
process' :: Counter -> String -> InputT (StateT [String] IO) ()
process' counter str = liftIO $ process counter str

getLine x times haskell

Based on Hackerrank question
My problem is how can I do getLine t times on stdIn?
main = do
t <- getInt
let x = [divisorsInNumber unsafeGetInt | a <-[1..t] ]
print x
getInt :: IO Int
getInt = fmap read getLine
unsafeGetInt :: Int
unsafeGetInt = unsafePerformIO getInt
divisorsInNumber n = length $ filter (== True) $ map (isDivisor n) (integralToListOfInts n)
Just replicate t times the getLine operation with replicateM:
import Control.Monad (replicateM)
getLines :: Int -> IO [String]
getLines t = replicateM t getLine
Thus getInts, that is getInt t times, can be expressed with:
getInts :: Int -> IO [Int]
getInts = fmap read <$> getLines
The full code rewritten to use getInts could be:
import Control.Applicative ((<$>))
import Control.Monad (replicateM)
getLines :: Int -> IO [String]
getLines n = replicateM n getLine
getInts :: Int -> IO [Int]
getInts n = fmap read <$> getLines n
getInt :: IO Int
getInt = fmap read getLine
divisorsInNumber :: Int -> Int
divisorsInNumber n = length $ filter (isDivisor n) (integralToListOfInts n)
main :: IO ()
main = do
t <- getInt
nums <- getInts t
let x = [divisorsInNumber num | num <- nums]
print x

Switching to ByteStrings

EDIT: I followed Yuras and Dave4420's advices (Thanks). I still have some errors. Updated the question. Finally I will use meiersi's version (Thanks) but I still want to find my errors...
I have a simple script that goes like this:
import System.Environment
getRow :: Int -> String -> String
getRow n = (!!n) . lines
getField :: Int -> String -> String
getField n = (!!n) . words'
words' :: String -> [String]
words' str = case str of
[] -> []
_ -> (takeHead " ; " str) : (words' (takeTail " ; " str))
takeHead :: String -> String -> String
takeHead st1 st2 = case st2 of
[] -> []
_ -> if st1 == (nHead (length st1) st2) then [] else (head st2):(takeHead st1 (tail st2))
takeTail :: String -> String -> String
takeTail st1 st2 = case st2 of
[] -> []
_ -> if st1 == (nHead (length st1) st2) then nTail (length st1) st2 else takeTail st1 (tail st2)
nTail :: Int -> String -> String
nTail n str = let rec n str = if n == 0 then str else rec (n - 1) (tail str)
in if (length str) < n then str else rec n str
nHead :: Int -> String -> String
nHead n str = let rec n str = if n == 0 then [] else (head str):(rec (n - 1) (tail str))
in if (length str) < n then str else rec n str
getValue :: String -> String -> String -> String
getValue row field src = getField (read field) $ getRow (read row) src
main :: IO ()
main = do
args <- getArgs
case args of
(path: opt1: opt2: _) -> do
src <- readFile path
putStrLn $ getValue opt1 opt2 src
(path: _) -> do
src <- readFile path
putStrLn $ show $ length $ lines src
It compiles and works. Then I wanted to switch to ByteStrings. Here is my attempt:
import qualified Data.ByteString.Lazy as B
import qualified Data.ByteString.Lazy.Char8 as Bc (cons, empty,unpack)
import qualified Data.ByteString.Lazy.UTF8 as Bu (lines)
import qualified System.Posix.Env.ByteString as Bg (getArgs)
separator :: B.ByteString
separator = (Bc.cons ' ' (Bc.cons ';' (Bc.cons ' ' Bc.empty)))
getRow :: Int -> B.ByteString -> B.ByteString
getRow n = (`B.index` n) $ Bu.lines
getCol :: Int -> B.ByteString -> B.ByteString
getCol n = (`B.index` n) $ wordsWithSeparator
wordsWithSeparator :: B.ByteString -> [B.ByteString]
wordsWithSeparator str = if B.null str then [] else (takeHead separator str):(wordsWithSeparator (takeTail separator str))
takeHead :: B.ByteString -> B.ByteString -> B.ByteString
takeHead st1 st2 = if B.null st2 then B.empty else if st1 == (nHead (toInteger (B.length st1)) st2) then B.empty else B.cons (B.head st2) (takeHead st1 (B.tail st2))
takeTail :: B.ByteString -> B.ByteString -> B.ByteString
takeTail st1 st2 = if B.null st2 then B.empty else if st1 == (nHead (toInteger (B.length st1)) st2) then nTail (toInteger (B.length st1)) st2 else takeTail st1 (B.tail st2)
nTail :: Integer -> B.ByteString -> B.ByteString
nTail n str = let rec n str = if n == 0 then str else rec (n - 1) (B.tail str)
in if (toInteger (B.length str)) < n then str else rec n str
nHead :: Integer -> B.ByteString -> B.ByteString
nHead n str = let rec n str = if n == 0 then B.empty else B.cons (B.head str)(rec (n - 1) (B.tail str))
in if (toInteger (B.length str)) < n then str else rec n str
getValue :: B.ByteString -> B.ByteString -> B.ByteString -> B.ByteString
getValue row field = getCol (read (Bc.unpack field)) . getRow (read (Bc.unpack row))
main = do args <- Bg.getArgs
case (map (B.fromChunks . return) args) of
(path:opt1:opt2:_) -> do src <- B.readFile (Bc.unpack path)
B.putStrLn $ getValue opt1 opt2 src
(path:_) -> do src <- B.readFile (Bc.unpack path)
putStrLn $ show $ length $ Bu.lines src
It doesn't work. I could not debug it. Here is what GHC tells me:
BETA_getlow2.hs:10:23:
Couldn't match expected type `GHC.Int.Int64' with actual type `Int'
In the second argument of `B.index', namely `n'
In the expression: (`B.index` n)
In the expression: (`B.index` n) $ Bu.lines
BETA_getlow2.hs:13:23:
Couldn't match expected type `GHC.Int.Int64' with actual type `Int'
In the second argument of `B.index', namely `n'
In the expression: (`B.index` n)
In the expression: (`B.index` n) $ wordsWithSeparator
Any tips would be appreciated.
getRow n = (!!n) . lines
Compare with
getRow n = B.index . Bu.lines
In the second version you don't use n at all, so it is the same as
getRow _ = B.index . Bu.lines
In the fist example you use n as an argument to the (!!) operator. You need to do the same in the second version.
Looks like it is not the only issue in your code, but I hope it is a good point to start ;)
I'm taking the liberty to interpret the following two sub-questions into your original question.
What Haskell code would one typically write for a script like the one you posted.
What are the right data structures to efficiently perform the desired functionality.
The following code gives one answer to these two sub-questions. It uses the text library to represent sequences of Unicode characters. Moreover, it exploits the text library's high-level API to implement the desired functionality. This makes the code easier to grasp and thereby avoids potential mistakes in the implementation of low-level functions.
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text as T
import qualified Data.Text.IO as T
import System.Environment (getArgs)
type Table a = [[a]]
-- | Split a text value into a text table.
toTable :: T.Text -> Table T.Text
toTable = map (T.splitOn " ; ") . T.lines
-- | Retrieve a cell from a table.
cell :: Int -> Int -> Table a -> a
cell row col = (!! col) . (!! row)
main :: IO ()
main = do
(path:rest) <- getArgs
src <- T.readFile path
case rest of
row : col : _ -> T.putStrLn $ cell (read row) (read col) $ toTable src
_ -> putStrLn $ show $ length $ T.lines src
The first two errors Yuras has resolved for you, I think.
Re the 3rd error:
words' :: B.ByteString -> [B.ByteString]
words' str = if B.null str then B.empty else ...
The B.empty should be []. B.empty :: B.ByteString, but the result is supposed to have type [B.ByteString].
Re the 4th-7th errors:
length :: [a] -> Int
B.length :: B.ByteString -> Int64
In this case I would change the type signatures of nTail and nHead to use Int64 instead of Int. If that didn't work, I'd use Integer on all Integral types, using toInteger to do the conversion.
Re the 8th error:
The input to read must be a String. There's no getting round that. You'll have to convert the B.ByteString to a String and pass that to read.
(Incidently, are you sure you want to switch to ByteString and not Text?)
Re the 9th (final) error:
args :: [Data.ByteString.ByteString] (n.b. a list of strict bytestrings, not the lazy bytestrings you use elsewhere) but in the pattern match you expect args :: B.ByteString for some reason.
You should pattern match on a [ByteString] the same way you pattern match on a [String]: they are both lists.
Convert args to something of type [B.ByteString] with map (B.fromChunks . return) args.

getLine and is value integer

I get value using x <- getLine, how can I check that x can be interpreted as an integer number?
do x <- getLine
case filter (\(_,s) -> s == "") (reads x :: [(Int, String)]) of
[] -> putStrLn "x cannot be parsed as an Int"
(xAsInt, _) : _
-> putStrLn (concat ["x can be parsed as an Int, *and* its Int value is ",
show xAsInt])
Look into Data.Char.isNumber.
Haskell: Check if integer, or check type of variable
You could create a maybeIO function that performs an IO action in a catch, returning Just the result of the action if successful, or Nothing if an exception occurred. Then, you can use readLn instead of getLine + reads, with maybeIO to convert any exception into a Nothing.
import Control.Monad (liftM)
maybeIO :: IO a -> IO (Maybe a)
maybeIO f = catch (liftM Just f) (const $ return Nothing)
main = do
i <- maybeIO (readLn :: IO Int)
print i

Resources