How are expressions allowed inside Haskell do blocks - haskell

In the following code, line 4, I have an expression sandwiched between two IO actions in a do block:
1 doubleX :: (Show x, Num x) => x -> IO ()
2 doubleX x = do
3 putStrLn ("I will now double " ++ (show x))
4 let double = x * 2
5 putStrLn ("The result is " ++ (show double))
I understand do notation as chaining monadic operations together using >>= or >>. But how does that work when you have an expression in between? You couldn't just glue lines 3-5 together using >>.

I'm going to crib from my very similar answer here (though probably not a duplicate since that question doesn't explicitly deal with let).
The Report gives a full translation from do syntax into kernel Haskell; the parts relevant to your question are:
do {e} = e
do {e;stmts} = e >> do {stmts}
do {let decls; stmts} = let decls in do {stmts}
So your code desugars like this:
doubleX x = do
putStrLn ("I will now double " ++ (show x))
let double = x * 2
putStrLn ("The result is " ++ (show double))
==> do {e;stmts} rule
doubleX x =
putStrLn ("I will now double " ++ (show x)) >> do
let double = x * 2
putStrLn ("The result is " ++ (show double))
==> do {let decls; stmts} rule
doubleX x =
putStrLn ("I will now double " ++ (show x)) >>
let double = x * 2 in do
putStrLn ("The result is " ++ (show double))
==> do {e} rule
doubleX x =
putStrLn ("I will now double " ++ (show x)) >>
let double = x * 2 in
putStrLn ("The result is " ++ (show double))

Related

New line in haskell IO

I am not able to print outputs in multiple lines with the following code.
average l = (sum l) / (fromIntegral (length l))
readDoubles s1 s2 = putStr (s1++"\n"++s2) >>
do x <- readDoublesHelper; return ("The average is " ++ (show (average x)) ++ " \n" ++ "The maximum is " ++ (show (maximum x)) ++ "\n" ++ "The minimum is " ++ (show (minimum x)) ++ "\n")
readDoublesHelper = putStr "Enter a number: " >>
do line <- getLine;
if line == "done"
then return [];
else do xs <- (readDoublesHelper)
return ((read line :: Float): xs)
interface = readDoubles "Enter some numbers." "When finished, type ’done’. \n"
The output that I get is
*Main> interface
Enter some numbers.
When finished, type ’done’.
Enter a number: 2
Enter a number: 3
Enter a number: 4
Enter a number: 5
Enter a number: 6
Enter a number: 7
Enter a number: done
"The average is 4.5 \nThe maximum is 7.0\nThe minimum is 2.0\n"
But I want the output to be printed like this
The average is 4.5
The maximum is 7.0
The minimum is 2.0
Do notice "The average is 4.5 \nThe maximum is 7.0\nThe minimum is 2.0\n" is actually not printed
Reformatting readDoubles:
readDoubles s1 s2 = do
putStr (s1++"\n"++s2)
x <- readDoublesHelper
return ("The average is " ++ (show (average x)) ++ " \n" ++ "The maximum is " ++ (show (maximum x)) ++ "\n" ++ "The minimum is " ++ (show (minimum x)) ++ "\n")
We can see that result is "returned" rather than "printed"
As the type signature of readDoubles is:
readDoubles :: String -> String -> IO String
It just returns a lifted string. Since GHCi will print out the final result of the function, you're getting "The average is 4.5 \nThe maximum is 7.0\nThe minimum is 2.0\n" as output (the return value of the function)
However, from you description, I guess you just like to print out the result, so it should be:
readDoubles :: String -> String -> IO ()
readDoubles s1 s2 = do
putStr (s1++"\n"++s2)
x <- readDoublesHelper
putStrLn ("The average is " ++ (show (average x)) ++ " \n" ++ "The maximum is " ++ (show (maximum x)) ++ "\n" ++ "The minimum is " ++ (show (minimum x)) ++ "\n")

Is there a better way of doing this in Haskell?

I have written the following to assist grand kids with their home schooling work and to keep mind working by learning how to program (I thought haskell sounded awesome).
main :: IO ()
main = do
putStrLn "Please enter the dividend :"
inputx <- getLine
putStrLn "Please enter the divisor :"
inputy <- getLine
let x = (read inputx) :: Int
let y = (read inputy) :: Int
let z = x `div` y
let remain = x `mod` y
putStrLn ( "Result: " ++ show x ++ " / " ++ show y ++ " = " ++ show z ++ " remainder " ++ show remain )
putStrLn ( "Proof: (" ++ show y ++ " x " ++ show z ++ ") = " ++ show (y * z) ++ " + " ++ show remain ++ " = " ++ show ((y * z) + remain))
putStrLn ( "Is this what you had? ")
Is their a neater/nicer/better/more compact way of doing this?
It would benefit from a key principle: separate your pure code from your IO as much as possible. This will let your programs scale up and keep main breif. Lots of let in a big main isn't a very functional approach and tends to get much messier as your code grows.
Using a type signature and readLn which is essentially fmap read getLine helps cut down some cruft. (If you're not familiar with fmap, visit the question How do functors work in haskell?. fmap is a very flexible tool indeed.)
getInts :: IO (Int, Int)
getInts = do
putStrLn "Please enter the dividend :"
x <- readLn
putStrLn " Please enter the divisor :"
y <- readLn
return (x,y)
Now the processing. If I were doing more with this kind of data, or more frequently, I'd be using a record type to store the dividend, divisor, quotient and remainder, so bear that in mind for the future, but it's an overkill here.
I'm hackishly returning a list rather than a tuple, so I can use map to show them all:
sums :: (Int, Int) -> [Int]
sums (x,y) = [x, y, q, r, y * q, y * q + r] where
q = x `div` y
r = x `mod` y
The final piece of the jigsaw is the output. Again I prefer to generate this outside IO and then I can just mapM_ putStrLn on it later to print each line. I'd prefer this to take the record type, but I'm tolerating a list of strings as input instead since I'm assuming I've already shown them all.
explain :: [String] -> [String]
explain [x,y,q,r,yq,yq_r] =
[ concat ["Result: ", x, " / ", y, " = ", q, " remainder ", r]
, concat ["Proof: (", y, " x ", q, ") + ", r, " = ", yq, " + ", r, " = ", yq_r]
, "Is this what you had? "]
Now we can write main as
main = do (x,y) <- getInts
let ns = map show ( sums (x,y) )
es = explain ns
mapM_ putStrLn es
or even more succinctly, by piping together the functions explain . map show . sums, and applying that to the output of getInts using fmap:
main :: IO ()
main = fmap (explain . map show . sums) getInts
>>= mapM_ putStrLn
You might notice that I added a +r in the proof to make = always mean =, which is the correct mathematical usage, and mirror's Haskell's meaning for =.

"drawing" char/string shapes by a variable diagonal length in haskell

I'm looking for a function (let's name it diamond) that "draws" a diamond-shaped "object" by defining only the length of it's diagonal (e.g.: as shown below, so the length of the diagonal in this case is 5 "*" but only 3 are visible):
Main> diamond 5
Output:
*
***
*****
***
*
I have these functions:
stars x
| (x mod 2 == 1) = (firstHalf x) ++ [x] ++ reverse(firstHalf x)
| otherwise = (firstHalf x) ++ reverse(firstHalf x)`
firstHalf x = take (x div 2) (iterate (2+) 1)
space x = map (div 2) (map ((x+1)-) (stars x))
Example output:
space 5 = [2,1,0,1,2]
stars 5 = [1,3,5,3,1]
This function might work but I don't know how to fix this error:
Type error in application:
Expression : replicate (stars x) ['*']
Term : stars x
Type : [Int]
Does not match : Int
diamond x = mapM_ putStrLn $ (replicate (space x) [' ']) ++ (replicate (star x) ['*'])
And I guess even if this error was fixed it would still need a tweak that would join those two lists to get the desired result. Any ideas?
You can generate a number of stars with replicate:
ghci> replicate 5 '*'
"*****"
You can also do this with any other character, such as a space (' ').
You can print a line to the screen with putStrLn:
ghci> putStrLn (replicate 5 '*')
*****
Here is a program that generates the star above:
main = do
putStrLn $ replicate 2 ' ' ++ replicate 1 '*'
putStrLn $ replicate 1 ' ' ++ replicate 3 '*'
putStrLn $ replicate 0 ' ' ++ replicate 5 '*'
putStrLn $ replicate 1 ' ' ++ replicate 3 '*'
putStrLn $ replicate 2 ' ' ++ replicate 1 '*'
You can define functions with =:
hello x = putStrLn $ "Hello " ++ x
main = do
hello "Bob" -- prints "Hello Bob"
(If you do this from the ghci prompt, you have to say let first: eg. let hello x = putStrLn $ "Hello " ++ x)
I don't know why I spent so much time on this question, but I felt like it. Typically we want questions to provide some evidence that you have spent some time trying to solve the problem before asking for help. Merry Christmas...
I have given you all the pieces. Now you have to put them together.

Printing the values inside a tuple in Haskell

I have a list of tuples. For example: [("A",100,1),("B",101,2)]. I need to display it in a simple way. For example: "your name is: A", "Your id is: 100".
If anyone can find a solution for this, it would be a great help. Thanks in advance.
The easiest way to do this is to create a function that works for one of the elements in your list. So you'll need something like:
showDetails :: (String, Int, Int) -> String
showDetails (name, uid, _) = "Your name is:" ++ name ++ " Your ID is: " ++ show uid
Then you would apply this function to each element in the list, which means you want to use the mapping function:
map :: (a -> b) -> [a] -> [b]
So, if your list is called xs, you would want something like:
map showDetails xs
This obviously gives you a result of type [String], so you might be interested in the unlines function:
unlines :: [String] -> String
This simply takes a list of strings, and creates a string where each element is separated by a new line.
Putting this all together, then, gives you:
main :: IO ()
main = putStrLn . unlines . map showDetails $ [("A",100,1),("B",101,2)]
For a single tuple, just pattern match all the elements, and do something with them. Having a function that does that, you can use map to transform the entire list.
import Data.List (foldl')
show_tuple :: (Num a, Num b) => (String, a, b) -> String
show_tuple (name, id, something) =
"Your name is: " ++ name ++ "\n" ++
"Your ID is: " ++ (show id) ++ "\n" ++
"Your something: " ++ (show something) ++ "\n\n"
-- transforms the list, and then concatenates it into a single string
show_tuple_list :: (Num a, Num b) => [(String, a, b)] -> String
show_tuple_list = (foldl' (++) "") . (map show_tuple)
The output:
*Main Data.List> putStr $ show_tuple_list [("ab", 2, 3), ("cd", 4, 5)]
Your name is: ab
Your ID is: 2
Your something: 3
Your name is: cd
Your ID is: 4
Your something: 5
Quick and dirty solution
f (x,y,z) = "your id is " ++ (show y) ++ ", your name is " ++ (show x) ++ "\n"
main = putStrLn $ foldr (++) "" (map f [("A",100,1),("B",101,2)])
OR (by #maksenov)
main = putStrLn $ concatMap f [("A",100,1),("B",101,2)]
Please try:
get1st (a,_,_) = a
get2nd (_,a,_) = a
get3rd (_,_,a) = a
showTuples [] = ""
showTuples (x:xs) = "Your name is:" ++ show(get1st(x)) ++ " Your ID is: " ++ show(get2nd(x)) ++ "\n" ++ showTuples xs
main = do
let x = [("A",100,1),("B",101,2)]
putStrLn . showTuples $ x

Moving To New Line In Haskell - Updated

I have the following functions in Haskell that must print the sales of weeks. Each sale in a new line. But it is not working the way i expect it to. The problem i have is the newline character '\n'.
Code:
printWeeks :: Int->String
printWeeks 0 = printWeek 0
printWeeks x = printWeeks(x-1) ++ printWeek x
printWeek :: Int->String
printWeek x = show(x) ++ " " ++ stars (sales x) ++ "'\n'"
I have tried many ways but the new line character is not working as expected. Everything is printed on the same line whichis not what i want.
Need help?
thanks
UPDATE
The following is not working because of compile errors. The errors comes from the second line of formatLines. The type decalaration is causing errors. Need help here
formatLine :: (Name,Price)->IO()
formatLine (a,b) = putStrLn (a ++ dots ++ p)
where
x=(length a)
p=(formatPence b)
y=length p
z=lineLength-(x+y)
dots = printDots z
formatLines :: [(Name,Price)]->IO()
formatLines []= ""
formatLines (a:x) = formatLines x ++ formatLine a
You should use ++ "\n" to append a newline to the output; your current code will add a ', then a newline, then another '.
As #marcog points out, be sure to use putStr to print it out (or don't append the newline at all and use putStrLn). Example:
Hugs> putStr (show 4 ++ "\n")
4
Hugs> putStrLn (show 4 ++ "\n")
4
Hugs> print (show 4 ++ "\n")
"4\n"
(Note that the Hugs interpreter adds extra newlines after each output.)
You are probably printing the string using print x, which is equivalent to putStrLn (show x). show x is converting the newlines into readable characters \ and n. You need to use putStrLn x instead, or putStr x if you don't want to append a newline to the end of the string.
You should also remove the single quotes you have around the newline, unless that was intentional.
It's a bit of a riddle why so much action is happening under the heading of IO. This is maybe a little verbose. I couldn't tell where lineLength was coming from so I made it a parameter.
formatLine :: Int -> (Name,Price) -> String
formatLine linelength (name, price) = name ++ dotfill ++ showprice
where
showprice :: String
showprice = formatPence price
extra :: Int
extra = linelength - length (name ++ showprice)
dotfill :: String
dotfill = replicate extra '.'
formatLines :: Int -> [(Name, Price)] -> String
formatLines linelength []= ""
formatLines linelength (first:rest) =
(formatLine linelength first ++ "\n") ++ formatLines linelength rest
standardPrint :: [(Name, Price)] -> IO ()
standardPrint listing = putStrLn (formatLines 50 listing)
fileAwayPrices :: FilePath -> [(Name,Price)] -> IO()
fileAwayPrices filename listing = writeFile filename (formatLines 70 listing)
testlist :: [(Name,Price)]
testlist = [("oats",344),("barley", 299),("quinoa",599)]
-- *Main> standardPrint testlist
-- oats...........................................344
-- barley.........................................299
-- quinoa.........................................599
type Name = String
type Price = Integer
formatPence n = show n
Re your update: your type declaration is correct, it's the rest of formatLines that's wrong.
formatLines :: [(Name,Price)]->IO()
formatLines [] = return ()
formatLines (a:x) = formatLines x >> formatLine a
A more concise way of writing that is
formatLines :: [(Name,Price)]->IO()
formatLines = mapM_ formatLine . reverse

Resources