Blog Hub Previous Next

Post 8
Input/Output and Compiling

Compiling Haskell Programs


So far, we’ve only run our Haskell programs in GHCI. That’s been fine for demonstration purposes, but what if you actually want to distribute your program? The answer is… GHC, the non-I version of GHCI! It’s not really that exciting, it’s just compiling your haskell program into an executable.

This is done simply with the ghc command! Go ahead and try to compilie some of your code by running the following command in your terminal (or powershell if you’re using windows):

ghc --make [file path]

The only problem with that is if we tried to compile any of the code we’ve written so far, it would just spit out an error:

The IO action `main' is not defined in module `Main'

That’s a pretty good clue into why our code isn’t complining! Thank you GHC! We are missing a main function, just like is necessary in so many other languages.

The astute among you may notice that the error message makes reference to main being an “IO action”. You may also recall that I’ve mentioned IO to be a type of monad. Good eye!

As it turns out, the function header for main is main :: IO ()

It should seem obvious that IO () would be the type of main. For the program to do anything useful, there has to be some way for the user and the program to communicate, and input/output is the way to do that. So, main is a function that take no arguments and returns an IO () monad and is the entry point into our program.

If you’re working on a project larger than one file, it is useful to add the line

module Main where

at the top of your code to denote the file containing the main function as the where the computer should look for the entry point into the program.


Simple Commandline Output


Well then, how do we actually use the IO monad? Very similarly to how we’ve used other types of monads: with the do notation!

So, let’s our journey with the timeless “Hello World” program! (Yes, it’s coming 8 posts in, but at least you’ll understand it better than if we had done it at the start!)

module Main where

main :: IO ()
main = do
    putStr "Hello World\n"

Compile your program and execute it:

[Windows]
ghc --make .\HelloWorld.hs
.\HelloWorld.exe

[Unix]
ghc --make ./HelloWorld.hs
./HelloWorld

BOOM! Now you’re as proficient in Haskell as you were in your first language on day one of learning how to code! Except for, well, all the other stuff you already know about Haskell.

You may have guessed this already, but the putStr function is of type String -> IO (), so in the example we have above, you don’t actually need the “do”. However, if you want more than a single line of output (among other things) you need to use do or the monad notation using >>= and return, but that’s yucky so we’ll stick with do.

Something else to note, when using multiple putStr statements, you have the option of using the left facing arrow for any line but the final, however it isn’t necessary. The following two programs are equivalent:

main = do
    putStr "Line 1\n"
    putStr "Line 2\n"

main = do
    x <- putStr "Line 1\n"
    putStr "Line 2\n"

In the second program, the x wouldn’t really have a useable value associated with it, so skipping the _ <- is fine. And remember, the final line of the do statement can’t have a <- because the result of whatever expression is on the last line is what the function (main, in this case) as a whole evaluates to.

Just like many other languages, Haskell also has a function that automatically adds a new line character to the end of a line it prints. This is simply putStrLn. It also has putChar that acts how you would expect.

Say you wanted to print a list or just a number, or really anything that isn’t already a string. Well, as long as it derives from Show then just use the print function!

 main = do
    print [1, 2, 3, 4]

Simple Commandline Input


What we have now is fantastic, but it doesn’t quite let the user control the program just yet. For that we’ll need some input functions! After all, IO doesn’t stand for ioutput, it stants for input output!

main :: IO ()
main = do
    putStrLn "What is your name?  "
    x <- getLine
    putStrLn ("Hello " ++ x ++ ", how are you?")

The outcome of this code should be pretty self explanatory. Clearly, the getLine function asks for a line of input and sets the outcome of the getLine function to be the value of x.

As you might expect, a getChar function exists too!


File IO


You can also, fairly easily, read from and write to files using the IO monad. To handle file names and paths, the type FilePath is used. Luckily, it’s just a wrapper for String, no conversion necessary.

To write to a file, use the function writeFile :: FilePath -> String -> IO ():

[Windows]
main = do
    putStrLn "What is your name?  "
    x <- getLine
    writeFile ".\\temp.txt" ("Hello " ++ x ++ ", how are you?")

[Unix]
main = do
    putStrLn "What is your name?  "
    x <- getLine
    writeFile "./temp.txt" ("Hello " ++ x ++ ", how are you?")

To read from a file, use the function readFile :: FilePath -> IO String:

main = do
    x <- readFile ".\\temp.txt"
    putStrLn x

Notice that the entire contents of the file are stored in x. You can do with that string whatever you please!

For the full list of IO based functions defined in prelude see here


Reference

Blog Hub Previous Next