Basic input and output
It’s been an uphill battle. I’m actually surprised I got where I am!
My second program’s purpose is to retrieve two numbers from the command line, assume they are the shorter edges of a right triangle and compute the longest edge.
The “Hello, World!” program evolved only slightly, but it took a long time to arrive at this:
main = do putStrLn "Please enter edge A:" sa <- getLine let a = read sa putStrLn "Please enter edge B:" sb <- getLine let b = read sb let c = sqrt $ a^2 + b^2 putStrLn "" putStrLn $ "A = " ++ show(a) ++ ", B = " ++ show(b) ++ ", C = " ++ show(c)
The first thing to notice here is that in line 3 we are creating a local variable sa and assigning the output of getLine to it. The return value of getLine is an IO monad, this is why we can’t just assign it, we have to use the <- operator. I don’t understand monads yet, so let’s assume they are something different from regular functions. The back story of this is that probably the whole expression has some special meaning.
Another fact about it is that the do expression, which is the entire main function, is also a monad (whatever that means).
The most difficult thing to get to work was to actually treat the value received from getLine as a number, because normally it returns a string. The thing did not want to compile no matter what! After a lot of searching I learned that local variables can be assigned/created with let, which in this case is a special, simplified version of the let statement, which works inside the do statements. The other thing is that read can be used like a function, which wasn’t apparent at first.
In other words, read turns a string into another value (like a number), and show turns any value into a string.
The next thing I noticed was that there is some boiler plate code here. In particular we are repeating the following pattern twice: printing a prompt, reading a line from stdin and converting the read string into a usable value.
Again, an uphill battle to extract this into a function. And again it didn’t want to compile. The errors were suggesting something about the types, but heck, I wished I understood what it wanted from me! So I left it and returned the next day to arrive at this:
getData :: String -> IO Double getData prompt = do putStrLn prompt val <- getLine return (read val) main = do a <- getData "Please enter edge A:" b <- getData "Please enter edge B:" let c = sqrt $ a^2 + b^2 putStrLn "" putStrLn $ "A = " ++ show(a) ++ ", B = " ++ show(b) ++ ", C = " ++ show(c)
The first line describes the types of the function defined in line two. It’s optional and can be deleted without consequences, but in this case it enforces that there is one argument of type String and the return type is Double. There is a monad involved, too, but I don’t understand yet how it’s linked with the main function.
The dollar sign in lines 10 and 12 changes the order of evaluation from its position to the end of the line, making the rest of the line form a single argument to the function. I could have used parentheses instead.
The ++ operator in line 12 concatenates strings.
I began to wonder if it will continue to be an uphill battle and when it will become easier. I also started feeling a little bit like fighting PERL for a moment, I hope that feeling won’t persist…
Haskell is difficult. I am not sure yet whether it impacts its usability. We will see.