Is there a better way of doing this in Haskell? - 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 =.

Related

Haskell: Parse error in pattern: a - 1 Possibly caused by a missing 'do'?

While trying to implement the famous Ackermann function in Haskell to test whether the promised difference in running time is actually measurable, I stumbled across this beauty of an error description:
Parse error in pattern: a - 1
Possibly caused by a missing 'do'?
I know that this parsing error is appearing pretty commonly and I have no idea why it does so.
Other than that, my code (see below) should be fine.
My code:
main :: IO ()
main = do
print "Please enter first operand: "
input <- getLine
let n = read input
print "Please enter second operand: "
input <- getLine
let m = read input
let r = ak(n,m)
print(n++" ackermann "++m++" = "++show r)
ak(a,b) = do
if a == 0
then return (b+1)
else if b == 0
then return ak(a-1, 1)
else
s1 <- ak(a-1, b)
s2 <- ak(a-1, s1)
return s2
Don't use do and return for ak. do blocks are used for monadic computations. You can work with:
ak 0 b = b + 1
ak a 0 = ak (a-1) 1
ak a b = ak (a-1) (ak a (b-1))
Then you can implement main as:
main :: IO ()
main = do
print "Please enter first operand: "
n <- readLn
print "Please enter second operand: "
m <- readLn
putStrLn (show n ++ " ackermann " ++ show m ++ " = " ++ show (ak n m))

My first haskell: best *inline* way to make a "natural language" listing of items? (like "1, 2, 3 and 4")

For my first line of Haskell I thought it'd be a nice case to produce a "natural listing" of items (of which the type supports show to get a string representation). By "natural listing" I mean summing up all items separated with , except the last one, which should read and lastitem. Ideally, I'd also like to not have a , before the "and".
To spice it up a bit (to show off the compactness of haskell), I wanted to have an "inline" solution, such that I can do
"My listing: " ++ ... mylist ... ++ ", that's our listing."
(Obviously for "production" making a function for that would be better in all ways, and allow for recursion naturally, but that's the whole point of my "inline" restriction for this exercise.)
For now I came up with:
main = do
-- hello
nicelist
nicelist = do
let is = [1..10]
putStrLn $ "My listing: " ++ concat [ a++b | (a,b) <- zip (map show is) (take (length is -1) $ repeat ", ") ++ [("and ", show $ last is)]] ++ ", that's our listing."
let cs = ["red", "green", "blue", "yellow"]
putStrLn $ "My listing: " ++ concat [ a++b | (a,b) <- zip (map show cs) (take (length cs -1) $ repeat ", ") ++ [("and ", show $ last cs)]] ++ ", that's our listing."
but this hardly seems optimal or elegant.
I'd love to hear your suggestions for a better solution.
EDIT:
Inspired by the comments and answer, I dropped the inline requirement and came up with the following, which seems pretty sleek. Would that be about as "haskellic" as we can get, or would there be improvements?
main = do
putStrLn $ "My listing: " ++ myListing [1..10] ++ ", that's the list!"
putStrLn $ "My listing: " ++ myListing ["red", "green", "blue", "yellow"] ++ ", that's the list!"
myListing :: (Show a) => [a] -> String
myListing [] = "<nothing to list>"
myListing [x] = "only " ++ (show x)
myListing [x, y] = (show x) ++ " and " ++ (show y)
myListing (h:t) = (show h) ++ ", " ++ myListing t
Here's how I would write it:
import Data.List
niceShow' :: [String] -> String
niceShow' [] = "<empty>"
niceShow' [a] = a
niceShow' [a, b] = a ++ " and " ++ b
niceShow' ls = intercalate ", " (init ls) ++ ", and " ++ last ls
niceShow :: [String] -> String
niceShow ls = "My listing: " ++ niceShow' ls ++ ", that's our listing."
niceList :: IO ()
nicelist = do
putStrLn $ niceShow $ show <$> [1..10]
putStrLn $ niceShow ["red", "green", "blue", "yellow"]
Steps:
Create niceShow to create your string
Replace list comprehensions with good old function calls
Know about intercalate and init
Add type signatures to top levels
Format nicely
niceShow can only be inlined if you know the size of the list beforehand, otherwise, you'd be skipping the edge cases.
Another way to state the rules for punctuating a list (without an Oxford comma) is this:
Append a comma after every element except the last two
Append “and” after the second-to-last element
Leave the final element unchanged
This can be implemented by zipping the list with a “pattern” list containing the functions to perform the modifications, which repeats on one end. We want something like:
repeat (<> ",") <> [(<> " and"), id]
But of course this is just an infinite list of the comma function, so it will never get past the commas and on to the “and”. One solution is to reverse both the pattern list and the input list, and use zipWith ($) to combine them. But we can avoid the repeated reversals by using foldr to zip “in reverse” (actually, just right-associatively) from the tail end of the input. Then the result is simple:
punctuate :: [String] -> [String]
punctuate = zipBack
$ [id, (<> " and")] <> repeat (<> ",")
zipBack :: [a -> b] -> [a] -> [b]
zipBack fs0 = fst . foldr
(\ x (acc, f : fs) -> (f x : acc, fs))
([], fs0)
Example uses:
> test = putStrLn . unwords . punctuate . words
> test "this"
this
> test "this that"
this and that
> test "this that these"
this, that and these
> test "this that these those them"
this, that, these, those and them
There are several good ways to generalise this:
zipBack is partial—it assumes the function list is infinite, or at least as long as the string list; consider different ways you could make it total, e.g. by modifying fs0 or the lambda
The punctuation and conjunction can be made into parameters, so you could use e.g. semicolons and “or”
zipBack could work for more general types of lists, Foldable containers, and functions (i.e. zipBackWith)
String could be replaced with an arbitrary Semigroup or Monoid
There’s also a cute specialisation possible—if you want to add the option to include an Oxford comma, its presence in the “pattern” (function list) depends on the length of the final list, because it should not be included for lists of 2 elements. Now, if only we could refer to the eventual result of a computation while computing it…

How are expressions allowed inside Haskell do blocks

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))

Repeated timing of function

I have a very simple function f :: Int -> Int and I want to write a program that calls f for each n = 1,2,...,max. After each call of f the (cumulative) time that was used up to that point should be displayed (along with n and f n). How can this be implemented?
I'm still really new to input/output in Haskell, so this is what I've tried so far (using some toy example function f)
f :: Int -> Int
f n = sum [1..n]
evalAndTimeFirstN :: Int -> Int -> Int -> IO()
evalAndTimeFirstN n max time =
if n == max
then return () -- in the following we have to calculate the time difference from start to now
else let str = ("(" ++ (show n) ++ ", " ++ (show $ f n) ++ ", "++ (show time)++ ")\n")
in putStrLn str >> evalAndTimeFirstN (n+1) max time -- here we have to calculate the time difference
main :: IO()
main = evalAndTimeFirstN 1 5 0
I don't quite see how I have to introduce the timing here. (The Int for time probably has to be replaced with something else.)
You probably want something like this. Adapt the following basic example as needed for your recursive function.
import Data.Time.Clock
import Control.Exception (evaluate)
main :: IO ()
main = do
putStrLn "Enter a number"
n <- readLn
start <- getCurrentTime
let fact = product [1..n] :: Integer
evaluate fact -- this is needed, otherwise laziness would postpone the evaluation
end <- getCurrentTime
putStrLn $ "Time elapsed: " ++ show (diffUTCTime end start)
-- putStrLn $ "The result was " ++ show fact
Uncomment the last line to print the result (it gets very large very quickly).
I finally managed to find a solution. In this case we're measuring the "real" time in ms.
import Data.Time
import Data.Time.Clock.POSIX
f n = sum[0..n]
getTime = getCurrentTime >>= pure . (1000*) . utcTimeToPOSIXSeconds >>= pure . round
main = do
maxns <- getLine
let maxn = (read maxns)::Int
t0 <- getTime
loop 1 maxn t0
where loop n maxn t0|n==maxn = return ()
loop n maxn t0
= do
putStrLn $ "fun eval: " ++ (show n) ++ ", " ++ (show $ (f n))
t <- getTime
putStrLn $ "time: " ++ show (t-t0);
loop (n+1) maxn t0

Trying to make a simple math equation in Haskell

I'm trying to make a simple function that gets 2 variables from the user (x,y)
makes a calculation, and prints it out.
for some reason without success:
main = do
putStrLn "Insert Number1"
x <- readLn
putStrLn "Insert Number2"
y <- readLn
z = (x * y * 0.01)
putStrLn "Result: " ++z
The Error I get:
test.hs:6:11: parse error on input `='
Use let to bind new variables. You also have a few errors on the final line: first, you must explicitly convert between Double and String (using, for example, show), and secondly, you need to remember precedence. In Haskell, function application binds tighter than anything except record updates, so what you wrote parses as (putStrLn "Result: ") ++ z, which doesn't really make sense. With these things fixed:
main = do
putStrLn "Insert Number1"
x <- readLn
putStrLn "Insert Number2"
y <- readLn
let z = x * y * 0.01
putStrLn ("Result: " ++ show z)

Resources