Old-style Haskell manualGuides and tutorials
I always thought that for someone smart and curious, the right way to learn programming is how it was done in the Olden Days – with BASIC and a manual that didn't spend ten pages explaining each concept.
I looked around and haven't found anything like this, so here's a tutorial for Haskell that follows that style. (See the BBC Micro manual for inspiration.) It describes one thing, and one only: how to get the computer do what you want. Which is pretty useful, isn't it?
The command stack runghc foo.hs
runs a program contained in foo.hs
. For instance, this program prints “Hello world” to the screen:
-- This should be put into a file called 'foo.hs'.
--
-- Lines starting with “--” are comments and they
-- are ignored by Haskell.
main = do
putStrLn "Hello world"
stack
can be obtained here: https://docs.haskellstack.org/en/stable/README/#how-to-install. Before running programs, you would need to install a compiler; to do that, run stack setup
.
Note 1: lines starting with --
are comments. They can be left in code or deleted, as you wish. You may want to leave comments in your programs even if you understand everything at the moment of writing, because later on you might forget and become unable to read your own code.
Here's a program that calculates sum of all numbers between one number and another:
main = do
a <- readLn -- (1)
b <- readLn
putStrLn ("There are " ++ show (b-a+1) ++ " numbers") -- (2)
putStrLn ("Their sum is " ++ show (sum [a..b])) -- (3)
a <- readLn
takes the entered input and turns it into something before putting it into a
– in our case, a number. The readLn
action is equivalent to this:
readLn = read <$> getLine
read
is a function that can convert a string to a number (or something else, as needed).
In lines (2)
and (3)
we use the opposite of read
– i.e. show
– to turn the result of a calculation into a string. This is needed because ++
can't add a string to a number, or a number to a string – it needs two strings.
Finally, sum [a..b]
lists all numbers between a
and b
and calculates their sum. If you wrote show [a..b]
instead of show (sum [a..b])
, the numbers themselves would've been printed. Try it.
Note 1: you may be curious what <$>
is. It's an operator that says “do something with the result”. For instance, we could write length <$> getLine
instead of read <$> getLine
to make an action that gets entered text and gives you a number saying how many characters are in that text.
Note 2: there are several operators for doing something with the result, and they have to be used in different cases. If you want to do something with the result of a simple computation (like 2+3
), don't use any operator. If you have an action (like getLine
), you should use <$>
. However, if you have an action and you want to perform another action on its result (like printing it), use =<<
.
| action | value |
------------+----------------------+----------------+
action | putStrLn =<< getLine | print [1..3] |
computation | read <$> getLine | length [1..3] |
Later we'll give precise rules about how to find out what operator should be used in what case.
Here's a program that waits for a name to be entered and then prints it:
main = do
name <- getLine -- (1)
putStrLn ("Hello, " ++ name) -- (2)
name <- getLine
takes entered text and calls it name
so that in the rest of the program we'd be able to use it. In the line (2)
we add “Hello” to name
and show the result on screen.
Note 1: of course, the original name
doesn't change when we do putStrLn ("Hello, " ++ name)
. If it did, that'd be pretty inconvenient.
Note 2: parentheses around ("Hello, " ++ name)
are important. Without them, putStrLn
would try to print "Hello, "
instead of "Hello, " ++ name
, and then ++ name
would confuse the compiler and you'll see this error:
foo.hs:3:3: error:
• Couldn't match expected type ‘[Char]’ with actual type ‘IO ()’
• In the first argument of ‘(++)’, namely ‘putStrLn "Hello, "’
In a stmt of a 'do' block: putStrLn "Hello, " ++ name
In the expression:
do { name <- getLine;
putStrLn "Hello, " ++ name }