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