//edit 5
when the i use only these 2 lines
index :: [String] -> [String] -> Bool
index a b = and [x `elem` a |x <- b]
it works fine!!!!
eg:
index ["asd","asdd","dew"] ["asdd","asdad"]
False
But when i use the entire code mentioned below
empty [] = True
empty _ = False
index a b = do
if empty a
then do putStrLn "a is empty"
else return ()
if empty b
then do putStrLn "b is empty"
else return ()
where
index :: [String] -> [String] -> Bool
index a b = and [x `elem` a |x <- b]
theres no output!! and thats the issue i got!!
//edit 6
index a b = do index'
if empty a
then do putStrLn "a is empty"
else return ()
if empty b
then do putStrLn "b is empty"
else return ()
where
index' :: [String] -> [String] -> Bool
index' a b = and [x `elem` a |x <- b]
thanks
This is a little off-topic since maybe you are trying to learn the layout rule and how to nest ifs, but the code you show in revision 6 is just using if to do error checking. I would just do the error checking via pattern matching instead of if.
index [] _ = putStrLn "a is empty"
index _ [] = putStrLn "b is empty"
index a b = putStrLn (show index')
where index' = and [x `elem` a | x <- b]
(You don't need the return () after putStrLn because it already returns () for you.)
Pattern matching is a lot easier to get right because it doesn't require a lot of indentation, etc.
Edit:
I changed the definition of index' a little from yours. In my version it's a local variable instead of a local function. In Haskell, there's not much difference, it's just that index' uses a and b from the surrounding index function, so it doesn't need to take parameters. (Admittedly, index' is not such a good name for a variable.)
Also I left off the type annotation because I don't usually write them. But they are helpful when something isn't working. Here is what it would look like:
where index' :: Bool
index' = and [x `elem` a | x <- b]
An if construct in Haskell is a expression.
That means it must always evaluate to a value, so the else part is mandatory.
Also, the first part of if must be a boolean, so you can't call index' there, since that returns a [Int], not a Bool.
I would say, start with something like this:
if isEmpty a
then putStrLn "a is empty"
else if isEmpty b
then putStrLn "b is empty"
else putStrLn "neither are empty"
index a b = if index' [] bs
then putStrLn "a is empty"
if index' a []
then putStrLn "b is empty"
else
where
index' :: [String] -> [String] -> [Int]
index' a b = index' True [(elem x b) | x <- a]
If you indent your statement like this you'll see that the first if has no matching else clause.
Also, else clause doesn't return anything — a function should have a return value under all circumstances.
IO operations in Haskell are a bit difficult because they need so called monads
You would need a special do-notion here to sequence the outputs.
Write it like this:
empty [] = True
empty _ = False
index a b = do
if empty a
then do putStrLn "a is empty"
else return ()
if empty b
then do putStrLn "b is empty"
else return ()
Or Maybe you could just return the strings and use putStrLn separately.
You index' must return a boolean value when it should be used as an if condition!
Related
Why is this function allowed:
-- function 1
myfunc :: String
myfunc = do
x <- (return True)
show x
and this is not:
-- function 2
myfunc :: String
myfunc = do
x <- getLine
show x
The compile error:
Couldn't match type `[]' with `IO'
Expected type: IO Char
Actual type: String
I get why function 2 shouldn't work, but why then thus function 1 work?
and why does this then work:
-- function 3
myfunc = do
x <- getLine
return (show x)
I get that it returns IO String then, but why is function 1 also not forced to do this?
In function1 the do block in myfunc is working in the list monad, because String is really just [Char]. In there, return True just creates [True]. When you do x <- return True that "extracts" True out of [True] and binds it to x. The next line show x converts True into a String "True". which being the return value the compiler value expects to see, ends up working fine.
Meanwhile in function2, the do block in myfunc is also working on the list monad (for the same reason, String being really [Char]) but calls on getLine which is only available in the IO monad. So unsurprisingly, this fails.
-- EDIT 1
OP has added a function3
-- function 3
myfunc :: String
myfunc = do
x <- getLine
return (show x)
No this should not work for the same reason function2 fails.
-- EDIT 2
OP has updated function3 to fix a copy paste error.
-- function 3
myfunc = do
x <- getLine
return (show x)
This is mentioned in the comments, but for clarity sake, this works because, when the type information is unspecified, GHC makes it best inference and after seeing getLine, it figures it’s IO String which does provide getLine.
Note - I wrote this answer with as casual a tone as I could manage without being wrong with the intention of making it approachable to a beginner level.
do blocks work in the context of an arbitrary Monad. The Monad, in this case, is []. The Monad instance for lists is based on list comprehensions:
instance Monad [] where
return x = [x]
xs >>= f = [y | x <- xs, y <- f x]
You can desugar the do notation thus:
myfunc :: String
myfunc = do
x <- (return True)
show x
-- ==>
myfunc = [y | x <- return True, y <- show x]
-- ==>
myfunc = [y | x <- [True], y <- show x]
In a list comprehension, x <- [True] is really just the same as let x = True, because you're only drawing one element from the list. So
myfunc = [y | y <- show True]
Of course, "the list of all y such that y is in show True" is just show True.
I am trying to use Haskeline to write a program which asks the user a sequence of questions, each one optionally with a default value in [brackets], and reads in their responses. I want the user to be able to
Press Enter to submit the [default] value;
Type in a string, edit it if needed, and then press Enter to submit this value;
Press Ctrl-C to reset all values to the defaults and start over; and,
Press Ctrl-D or enter "quit" to quit, in which case all the values which they submitted are lost.
I have been able to get points 1-3 working, but I cannot get point 4 to work: pressing Ctrl-D (or entering "quit") just brings up the next prompt instead of making the program quit the questioning. Looking at my program (please see below) I understand why this happens, but I am not able to figure out how to fix this so that Ctrl-D (or "quit") actually makes the questioning stop. How do I fix the program to make this happen?
I did see this question which seems to ask something similar, but I could not get much from there; I am not even sure that they are asking the same question as I am.
As a secondary question: my current program has quite a few case statements which switch on Maybe values. In particular, I currently check for Nothing two or three levels deep so that I can correctly return a Nothing when the user presses Ctrl-D. I have a feeling that this could be simplified using (something like) the monadic >>= operator, but I am unable to figure out how to do this in this case. Is my hunch right? Is there a way to do away with all this pattern matching which looks for Nothing?
Also: please tell me anything else which could improve my code below. I am quite new to this, so it is very likely that I am missing many obvious things here.
My program asks the user about the composition of a fruit basket. The information associated with a fruit basket consists of the name of the owner of the fruit basket and the names of the different kinds of fruit in the basket. To be able to ask for the latter, I first ask for the number of different kind of fruit in the basket, and then ask for the name of each kind. We start with a default fruit basket whose information is then modified as per what the user tells us.
module Main where
import System.Console.Haskeline
type PersonName = String
type FruitName = String
data FruitBasket = FruitBasket { ownerName :: PersonName,
fruitCount :: Int,
fruitNames :: [FruitName]
} deriving Show
defaultBasket = FruitBasket "Mary" 2 ["Apple", "Peach"]
main :: IO ()
main = do
basket <- getBasketData defaultBasket
putStrLn $ "Got: " ++ show(basket)
-- Prompt the user for information about a fruit basket, and
-- return a FruitBasket instance containing this information. The
-- first argument is an instance of FruitBasket from which we get
-- the default values for the various prompts. The return value
-- has a Maybe type because the user may abort the questioning, in
-- which case we get nothing from them.
getBasketData :: FruitBasket -> IO (Maybe FruitBasket)
getBasketData basket = runInputT defaultSettings $ withInterrupt $ getData basket
where
getData :: FruitBasket -> InputT IO (Maybe FruitBasket)
getData initialBasket = handleInterrupt f $ do
outputStrLn banner
input <- getInputLine $ "Who owns this basket? [" ++ defaultOwner ++ "] : "
basket <- case input of
Nothing -> return Nothing -- User pressed Ctrl-D with the input being empty
Just "" -> return (Just initialBasket) -- User pressed Enter with the input being empty
Just "quit" -> return Nothing -- User typed in "quit" and pressed Enter
Just newOwner -> return (Just initialBasket{ownerName = newOwner})
input <- getInputLine $ "Number of kinds of fruit in the basket? [" ++ show defaultCount ++ "] : "
basket' <- case input of
Nothing -> return Nothing
Just "" -> return basket
Just "quit" -> return Nothing
Just count -> return $ updateFruitCount basket (read count)
where updateFruitCount Nothing _ = Nothing
updateFruitCount (Just realBasket) newCount = Just $ realBasket{fruitCount = newCount}
let defaultFruitNames = pruneOrPadNames basket'
newNames <- getFruitNames defaultFruitNames 1
case newNames of
Nothing -> return (Just defaultBasket)
Just newSetOfNames -> return $ updateFruitNames basket' newSetOfNames
where updateFruitNames Nothing _ = Nothing
updateFruitNames (Just realBasket) realNewNames = Just $ realBasket{fruitNames = realNewNames}
where f = (outputStrLn "Press Ctrl-D or enter \"quit\" to quit." >> getData initialBasket)
defaultOwner = ownerName initialBasket
defaultCount = fruitCount initialBasket
banner :: String
banner = "Please enter details of the fruit basket below. At each prompt you can do one of the following:\n\
\\t (a) Press Enter to submit the [default] value;\n\
\\t (b) Type in a string, edit it if needed, and then press Enter to submit this value;\n\
\\t (c) Press Ctrl-C to reset all values to the defaults and start over;\n\
\\t (d) Press Ctrl-D or enter \"quit\" to quit; all the values you submitted will be lost."
pruneOrPadNames :: Maybe FruitBasket -> Maybe [String]
pruneOrPadNames Nothing = Nothing
pruneOrPadNames (Just basket) = Just $ pruneOrPad (fruitNames basket) (fruitCount basket)
-- When requiredLength is not larger than (length inputList),
-- (pruneOrPad inputList requiredLength) is the prefix of
-- inputList of length requiredLength. Otherwise, it is inputList
-- padded with as many empty strings as required to make the total
-- length equal to requiredLength.
pruneOrPad :: [String] -> Int -> [String]
pruneOrPad inputList requiredLength
| requiredLength <= inputLength = take requiredLength inputList
| otherwise = inputList ++ (replicate difference "")
where inputLength = length inputList
difference = requiredLength - inputLength
getFruitNames Nothing _ = return Nothing
getFruitNames (Just []) _ = return $ Just [""]
getFruitNames (Just (name:names)) count = do
input <- getInputLine $ "Name of fruit " ++ (show count) ++ " [" ++ name ++ "] : "
newNames <- case input of
Nothing -> return Nothing
Just "" -> do -- Keep the default name for this fruit ...
newNames' <- getFruitNames (Just names) (count + 1)
case newNames' of
Nothing -> return Nothing
-- ... unless the user chose to quit
-- while entering a name
Just [""] -> return $ Just [name]
-- At this point names = [] so it is
-- already time to stop asking for
-- more names.
Just furtherNames -> return $ Just (name : furtherNames)
Just "quit" -> return Nothing
Just name' -> do
newNames' <- getFruitNames (Just names) (count + 1)
case newNames' of
Nothing -> return Nothing
Just [""] -> return $ Just [name']
Just furtherNames -> return $ Just (name' : furtherNames)
return newNames
With help from some of the advice here on the haskell-beginners mailing list I have managed to solve my problems, the Ctrl-D question entirely and the factoring question to my own satisfaction (as of now!). I am posting the answer here in the hope it helps others in my predicament.
First, the trouble with the Ctrl-D: The problem was that I was throwing away the control logic offered by the Maybe monad and just using the values from the monad, by referring to various variable names which contained these values. The first place where I do this is here, in the getBasketData function:
basket <- case input of ...
input <- getInputLine ...
basket' <- case input of
Nothing -> return Nothing
Just "" -> return basket
Notice how, in computing basket', I
Ignore the case where basket could have been Nothing, and
Use the value encapsulated by basket by referring to (and pattern matching on, when needed) the variable basket which is still in scope inside the expression for basket'.
This is the where the Ctrl-D was lost. Here, for contrast, is code for getBasketData which does not let the Nothings slip through the gaps (I renamed the basket variables to maybeBasket, because they are really instances of Maybe FruitBasket):
getBasketData basket = runInputT defaultSettings $ withInterrupt $ getData basket
where
getData :: FruitBasket -> InputT IO (Maybe FruitBasket)
getData initialBasket = handleInterrupt f $ do
outputStrLn banner
input <- getInputLine $ "Who owns this basket? [" ++ defaultOwner ++ "] : "
maybeBasket <- case input of
Nothing -> return $ Nothing -- User pressed Ctrl-D with the input being empty
Just "" -> return $ Just initialBasket -- User pressed Enter with the input being empty
Just "quit" -> return $ Nothing -- User typed in "quit" and pressed Enter
Just newOwner -> return $ Just initialBasket{ownerName = newOwner}
maybeBasket' <- case maybeBasket of
Nothing -> return $ Nothing
Just realBasket -> do input <- getInputLine $ "Number of kinds of fruit in the basket? [" ++ show defaultCount ++ "] : "
case input of
Nothing -> return $ Nothing
Just "" -> return $ maybeBasket
Just "quit" -> return $ Nothing
Just count -> return $ Just $ realBasket{fruitCount = (read count)}
maybeBasket'' <- case maybeBasket' of
Nothing -> return $ Nothing
Just realBasket -> do let defaultFruitNames = pruneOrPad (fruitNames realBasket) (fruitCount realBasket)
newNames <- getFruitNames defaultFruitNames 1
case newNames of
Nothing -> return $ Nothing
Just newSetOfNames -> return $ Just $ realBasket{fruitNames = newSetOfNames}
return maybeBasket''
where f = (outputStrLn interruptMessage >> getData initialBasket)
defaultOwner = ownerName initialBasket
defaultCount = fruitCount initialBasket
Thus, for instance, we try to do any real computation to get maybeBasket' --- including presenting the prompt for the number of different kinds of fruit --- only if maybeBasket is not Nothing.
This solves the Ctrl-D problem: the program now stops questioning and returns Nothing if the user presses Ctrl-D in response to any question.
Now onto the factoring. This is where advice from the mailing list answer helped: I started out by splitting up the big getData function into three pieces, one for each "big" use of the <- operator, and put these pieces into separate functions. This cleared up the logic a lot for me (indeed, this is how I found the fix to the Ctrl-D problem as well). Starting with this, I kept rephrasing the various parts till I got the following version which looks good enough to me. Notice how small and clean the getBasketData function has become!
module Main where
import System.Console.Haskeline
type PersonName = String
type FruitName = String
data FruitBasket = FruitBasket { ownerName :: PersonName,
fruitCount :: Int,
fruitNames :: [FruitName]
} deriving Show
defaultBasket :: FruitBasket
defaultBasket = FruitBasket "Mary" 2 ["Apple", "Peach"]
main :: IO ()
main = do
basket <- getBasketData defaultBasket
putStrLn $ "Got: " ++ show(basket)
-- Prompt the user for information about a fruit basket, and
-- return a FruitBasket instance containing this information. The
-- first argument is an instance of FruitBasket from which we get
-- the default values for the various prompts. The return value
-- has a Maybe type because the user may abort the questioning, in
-- which case we get nothing from them.
getBasketData :: FruitBasket -> IO (Maybe FruitBasket)
getBasketData basket = runInputT defaultSettings $ withInterrupt $ getData basket
where
getData :: FruitBasket -> InputT IO (Maybe FruitBasket)
getData initialBasket = handleInterrupt f $ do
outputStrLn banner
(ownerQ initialBasket) >>= (processOwner initialBasket) >>= processCount >>= processNames
where f = (outputStrLn interruptMessage >> getData initialBasket)
ownerQ :: FruitBasket -> InputT IO (Maybe PersonName)
ownerQ basket = getInputLine $ "Who owns this basket? [" ++ (ownerName basket) ++ "] : "
processOwner :: FruitBasket -> Maybe PersonName -> InputT IO (Maybe FruitBasket)
processOwner _ Nothing = return Nothing
processOwner _ (Just "quit") = return Nothing
processOwner basket (Just "") = return $ Just basket
processOwner basket (Just newOwner) = return $ Just basket{ownerName = newOwner}
processCount :: Maybe FruitBasket -> InputT IO (Maybe FruitBasket)
processCount Nothing = return Nothing
processCount (Just basket) = (fruitTypesQ basket) >>= processCount'
where processCount' :: Maybe String -> InputT IO (Maybe FruitBasket)
processCount' Nothing = return Nothing
processCount' (Just "quit") = return Nothing
processCount' (Just "") = return $ Just basket
processCount' (Just count) = return $ Just basket{fruitCount = (read count)}
fruitTypesQ :: FruitBasket -> InputT IO (Maybe String)
fruitTypesQ basket = getInputLine $ "Number of kinds of fruit in the basket? [" ++ show (fruitCount basket) ++ "] : "
processNames :: Maybe FruitBasket -> InputT IO (Maybe FruitBasket)
processNames Nothing = return Nothing
processNames (Just basket) = input >>= processNames'
where input = getFruitNames defaultFruitNames 1
defaultFruitNames = pruneOrPad (fruitNames basket) (fruitCount basket)
processNames' :: Maybe [FruitName] -> InputT IO (Maybe FruitBasket)
processNames' Nothing = return Nothing
processNames' (Just newSetOfNames) = return $ Just basket{fruitNames = newSetOfNames}
banner :: String
banner = "Please enter details of the fruit basket below. At each prompt you can do one of the following:\n\
\\t (a) Press Enter to submit the [default] value;\n\
\\t (b) Type in a string, edit it if needed, and then press Enter to submit this value;\n\
\\t (c) Press Ctrl-C to reset all values to the defaults and start over;\n\
\\t (d) Press Ctrl-D or enter \"quit\" to quit; all the values you submitted will be lost."
interruptMessage :: String
interruptMessage = "#################################################\n\
\You pressed Ctrl-C.\n\
\We will now reset all values and start over.\n\
\To quit, press Ctrl-D or enter \"quit\".\n\
\#################################################\n"
pruneOrPadNames :: Maybe FruitBasket -> Maybe [String]
pruneOrPadNames Nothing = Nothing
pruneOrPadNames (Just basket) = Just $ pruneOrPad (fruitNames basket) (fruitCount basket)
-- When requiredLength is not larger than (length inputList),
-- (pruneOrPad inputList requiredLength) is the prefix of
-- inputList of length requiredLength. Otherwise, it is inputList
-- padded with as many empty strings as required to make the total
-- length equal to requiredLength.
pruneOrPad :: [String] -> Int -> [String]
pruneOrPad inputList requiredLength
| requiredLength <= inputLength = take requiredLength inputList
| otherwise = inputList ++ (replicate difference "")
where inputLength = length inputList
difference = requiredLength - inputLength
getFruitNames :: [FruitName] -> Int -> InputT IO (Maybe [FruitName])
getFruitNames [] _ = return $ Just [""]
getFruitNames (name:names) count = do
input <- getInputLine $ "Name of fruit " ++ (show count) ++ " [" ++ name ++ "] : "
newNames <- case input of
Nothing -> return Nothing
Just "" -> do -- Keep the default name for this fruit ...
newNames' <- getFruitNames names (count + 1)
case newNames' of
Nothing -> return Nothing
-- ... unless the user chose to quit
-- while entering a name
Just [""] -> return $ Just [name]
-- At this point names = [] so it is
-- already time to stop asking for
-- more names.
Just furtherNames -> return $ Just (name : furtherNames)
Just "quit" -> return Nothing
Just name' -> do
newNames' <- getFruitNames names (count + 1)
case newNames' of
Nothing -> return Nothing
Just [""] -> return $ Just [name']
Just furtherNames -> return $ Just (name' : furtherNames)
return newNames
The moral of this story seems to be: "When confused, break things down."
I think your hunch is right here. Much of the pattern matching done via case can be replaced with using the Maybe Monad a bit more.
Instead of
basket <- case input of
Nothing -> return Nothing -- User pressed Ctrl-D with the input being empty
Just "" -> return (Just initialBasket) -- User pressed Enter with the input being empty
Just "quit" -> return Nothing -- User typed in "quit" and pressed Enter
Just newOwner -> return (Just initialBasket{ownerName = newOwner})
you could write something like
let basket' = do
i <- input
guard $ i /= "quit"
b <- basket
return $ if (null i) then b else b{fruitCount = read i}
you could even introduce some helpers like
guardInput :: Maybe String -> (String -> Maybe a) -> Maybe a
guardInput input λ = input >>= \i -> ((guard $ i /= "quit") >> λ i)
-- | Custom ternary operator .)
True ? (a, _) = a
False ? (_, b) = b
to write
let basket = guardInput input $
\i -> return $ (null i) ? (initialBasket, initialBasket{ownerName = i})
Sorry - I know this doesn't answer your problem with Ctrl+D, but I haven't figured that one out myself (yet).
Probably a silly question but I can't for the life of me figure this out.
I want to append to the end of a list based on a series of if statements.
In python (or most other languages I am familiar with) I could do something like this:
x = ["hi"]
if True:
x.append("hello")
if not True:
x.append("wait a minute...")
if True:
x.append("goodbye")
Which would give me:
['hi', 'hello', 'goodbye']
How does one achieve such a thing in Haskell?
I can get as far as:
res :: [[Char]]
res =
let x = ["Hi"]
in
if (True)
then x ++ ["hello"]
... what goes here???
else x
Or am I going about this totally wrong?
I am very new to Haskell so please don't bite...
Idiomatically,
x = concat [ [ "hi" ],
[ "hello" | True ],
[ "wait a minute..." | not True ],
[ "goodbye" | True ] ]
In Haskell every if expression must have an else clause. In that regard it's similar to the Python conditional operator:
a if test else b
In Haskell it would be written as:
if test then a else b
So how do you write the following in Haskell?
x = ["hi"]
if True:
x.append("hello")
You would do something like:
let x = ["hi"] in
if True then x ++ ["hello"] else x
See the else clause? It just returns the value as is.
However writing code like this sucks. We want to write code like in Python, and Python is stateful. The variable x is the state. In Haskell we have the State monad for writing code like that. Consider:
import Control.Monad.State
append a = modify (++ [a])
foo :: [String] -> [String]
foo = execState $ do
when True $ append "hello"
when (not True) $ append "wait a minute..."
when True $ append "goodbye"
x = ["hi"]
res = foo x
main = print res
Simple right?
In addition to #AaditMShah's answer, if you only want to append to a value, without other modifications, the writer monad would be the proper abstraction:
import Control.Monad
import Control.Monad.Writer
append :: a -> Writer [a] ()
append = tell . (: [])
x :: [String]
x = execWriter $ do
tell ["hi"]
when True $
append "hello"
when (not True) $
append "wait a minute..."
when True $
append "goodbye"
Haskell is different than the way it is done in Python or other languages:
List (and most other data structures) are immutable.
if else is an expression in Haskell as opposed to statement which is seen in other languages. You cannot ignore the else part in Haskell like what you did with Python.
Looking at your python code, it seems what you want to do is that if the condition is True, then you want to append an element to the list. That pattern can be abstracted in the following function:
appendIfTrue :: Bool -> a -> [a] -> [a]
appendIfTrue b x xs = if b
then xs ++ [x]
else xs
Once you have written the function you can achieve the same functionality using the following code:
x = ["hi"]
main = do
let x1 = appendIfTrue True "hello" x
x2 = appendIfTrue False "wait a minute" x1
x3 = appendIfTrue True "goodbye" x2
print x3
The thing to note here that is you are creating a new list here instead of modifying them as you did in your Python code.
Demo:
λ> main
["hi","hello","goodbye"]
You need to understand that in Haskell you cannot just modify x as in your python example. You need to thread a value (a list of Strings) though your conditionals, where each step appends or does not append another String. So you can't get away with nested if-then-else statement alone. You need access to the intermediate List of Strings built so far.
So the elementary operation is to append or not append a String to a List of Strings based on a boolean. A function which does this kind of conditionall appending could look like:
condAppend :: Bool -> String -> [String] -> [String]
condAppend c suff pref = if c then pref ++ [suff]
else pref
No you just have to chain a series of condAppends and apply it to an inial List of Strings:
res = condAppend True "goodby"
$ condAppend False "wait a minute"
$ condAppend True "hello" ["Hi"]
This gives you:
["Hi","hello","goodby"]
Note that the order of execution in res is bottom to top (or right to left if you write it all in one line) or inside-out if you like as as in f (g (h x)) where the leftmost function f is applied last.
If you don't like that, you need an alternative to ($) which operates left to right. You can write your own function (>>>) as in
res'= (
condAppend True "hello"
>>>
condAppend False "wait a minute"
>>>
condAppend True "goodbye"
) ["Hi"]
where
f >>> g = g . f
and it so happens that (a much generalized version of) (>>>) is already defined in Control.Arrow.
I don't understand this type error:
Couldn't match expected type `[t0]' with actual type `IO ()'
In the return type of a call of `printv'
In a stmt of a 'do' expression: px <- printv x
In the expression:
do { px <- printv x;
sep <- print ", ";
rest <- prints xs;
return (px : sep : rest) }
From:
data Value = IntValue Int
| TruthValue Bool
deriving (Eq, Show)
printv :: Value -> IO()
printv (IntValue i) = print i
printv (TruthValue b) = print ("boolean" ++ show b)
prints :: [Value] -> [IO()]
prints [] = []
prints (x:xs) = do px <- printv x
sep <- print ", "
rest <- prints xs
return (px:sep:rest)
It looks to me like every element (px) is converted into an IO() action, and then that is added to a list of the same things, thus producing an [IO()] list.
What am I missing here? Converting it to a list of strings, by removing the print's, works fine.
You're missing the return on the [] case of prints:
prints [] = return []
However, your prints is very strange. It returns a [()], because print is outputting strings to the console, not returning them.
Do you mean to return strings from your printv function?
Since you're trying to pretty print a syntax tree, here's roughly the right way to do it:
Use pretty-printing combinators
Use a pretty typeclass
Like so:
import Text.PrettyPrint
import Data.List
data Value
= VInt Int
| VBool Bool
deriving (Eq, Show)
class Pretty a where
pretty :: a -> Doc
instance Pretty Value where
pretty (VInt i) = int i
pretty (VBool b) = text "Boolean" <+> text (show b)
draw :: [Value] -> String
draw = intercalate ", " . map (render.pretty)
main = putStrLn $ draw [VInt 7, VBool True, VInt 42]
Running it:
*A> main
7, Boolean True, 42
Take a closer look at the type of your function:
prints :: [Value] -> [IO()]
But if we now take a look at prints [] = [], this can't match, because the type of that one is
prints :: [t] -> [a]
Therefore, you missed using prints [] = return [], to make it work.
If you're not evaluating an IO action, you don't need a do block. Just treat IO () as a normal type.
prints (x:xs) = printv x : print ", " : prints xs
You don't want prints to return an array of IO actions. You want it to return a single IO action that represents each of the IO actions bound together. Something like:
prints xs = mapM_ (\x -> printv x >> putStr ", ") xs
Except that I don't think the new lines are going to end up where you want them.
Look at the documentation for mapM and sequence for more information. In particular, the implementation of sequence is probably similar to what you're trying to do.
However, I would really recommend that instead doing all the work in an IO function, you should write a pure function to render the textual format you want, and then just print that. In particular, it seems that an instance of Show for Value would be appropriate.
instance Show Value where
show (IntValue i) = show i
show (TruthValue b) = "boolean " ++ show b
That way you can just call print value rather than printv value, and if you really wanted to you could define prints as follows.
import Data.List
prints :: (Show a) => [a] -> IO ()
prints = putStrLn . intercalate ", " . map show`.
Im new to Haskell!!
I wrote this code:
import Data.List
inputIndex :: [String] -> [String] -> Bool
inputIndex listx input = and [x `elem` listx |x <- input]
inputIndex = if inputIndex == true
then putStrLn ("ok")
It works fine without the if statement but when I put the if statement the following error is shown:
Syntax error in expression (unexpected `}', possibly due to bad layout)
What am I doing wrong here?
Thanks
A couple of things are wrong here:
You will need an else clause.
True must be capitalized.
inputIndex must always take two arguments (right now it does not, in the last case).
I guess you want something like this...
inputIndex :: [String] -> [String] -> IO ()
inputIndex listx input = if inputIndex' listx input
then putStrLn ("ok")
else putStrLn ("not ok")
where
inputIndex' :: [String] -> [String] -> Bool
inputIndex' listx input = and [x `elem` listx |x <- input]
(Here I defined a new function with a near-identical name, by appending a prime/apostrophe. By defining it in the where clause, it is only visible to the outer inputIndex function. You can call this a helper-function, if you will. I could also have chosen a completely different name, but I'm uncreative.)
You could also condense this to the following (which is also more general):
allPresent :: (Eq t) => [t] -> [t] -> IO ()
allPresent xs ys = putStrLn (if and [y `elem` xs | y <- ys] then "ok" else "not ok")
It's "True", not "true".
Your second inputIndex implementation is not compatible with the first one. All your pattern cases for a function must have the same signature ([String] -> [String] -> Bool)
The error you show here is not generated by this code, because there is no '}' here.
putStrLn has signature String -> IO() while your inputIndex looks like it's supposed to be pure - just return the value and print it somewhere else.