Real World Haskell

So far, we have confined ourselves to the walled garden of CodeWorld. This provided us a smooth start, but of course Haskell is much more than that. This week we will learn about “real” Haskell: How to run it locally, how to compile programs, and what the standard libray provides for us.

# More ways to run Haskell

In order to use Haskell locally, we have to install it first. I will not go into the details of that now. Just follow the links on the resources page. There are plenty of competing installation instructions out there in the world. I do not particularly care which one you follow, but make sure you get GHC-7.10.3.

Once installed, you have two new programs ghc and ghci. The former is the compiler:

$ghc ghc: no input files Usage: For basic information, try the --help' option. Without an input file it does little. ## The interpreter The latter is an interpreter: $ ghci
GHCi, version 7.10.3: http://www.haskell.org/ghc/  :? for help
Prelude>

and running it gives us a prompt where we can now go ahead and evalute Haskell expressions:

Prelude> 1+1
2
Prelude> 2^2^2
16
Prelude> 2^2^2^2
65536
Prelude> 2^2^2^2^2
2003529930406846464979072351560255…

Besides evaluating expressions, we can bind variables or define functions (using let), and even declare data types:

Prelude> data Pair a = Pair a a deriving Show
Prelude> let four x = Pair (Pair x x) (Pair x x)
Prelude> let fourTrues = four True
Prelude> fourTrues
Pair (Pair True True) (Pair True True)

There are a few useful command that allow us to query the interpreter:

Prelude> :t four
four :: a -> Pair (Pair a)
Prelude> :i Pair
data Pair a = Pair a a           -- Defined at <interactive>:15:1
instance Show a => Show (Pair a) -- Defined at <interactive>:15:31

## Loading files

It is also possible to enter mult-line functions definition into the interpreter, but that gets quickly unweildy. But there is a better way: You can load files into the interpreter.

So here I load last week’s homework. The file gets loaded as Main, and I can use the :browse command to see the contents of the file:

$ghci LastWeek.hs GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help [1 of 1] Compiling Main ( LastWeek.hs, interpreted ) Ok, modules loaded: Main. *Main> :browse Main main :: IO () data List a = Empty | Entry a (List a) mapList :: (a -> b) -> List a -> List b combine :: List Picture -> Picture allList :: List Bool -> Bool So we can play around in the file: *Main> let list = Entry 23 (Entry 42 Empty) *Main> nth list 2 42 *Main> mapList (+1) list <interactive>:7:1: No instance for (Show (List b0)) arising from a use of ‘print’ In the first argument of ‘print’, namely ‘it’ In a stmt of an interactive GHCi command: print it The latter did not work. We can deduce from the error message that the interpreter is using the command print to print out the results, and that this function requires its argument to be of a type that has a Show instance. Luckily, that instance can also be dervied. So after editing the file, adding deriving Show to the data List a = … line, I can reload the file with :r and now this works (but I have to redefine list – all definitions are lost when you reload): *Main> :r [1 of 1] Compiling Main ( LastWeek.hs, interpreted ) Ok, modules loaded: Main. *Main> let list = Entry 23 (Entry 42 Empty) *Main> mapList (+1) list Entry 24 (Entry 43 Empty) I can also, if I so want, run the main function from the interpreter: *Main> main Open me on http://127.0.0.1:3000/ In this case, because main calls interactionOf, this starts a local webserver that I can access in the browser and play our game. It keeps running even when I close the tab; I have to press Ctrl-C to return to the prompt of the interpreter. ## Compiling a program But clearly, entering main in the interpreter is not the desired way of running a program. Therefore, we use the compiler to produce a binary that we can run like any other compiled program of any other programming language: $ ghc -O LastWeek.hs
[1 of 1] Compiling Main             ( LastWeek.hs, LastWeek.o )
Linking LastWeek ...
$./LastWeek Open me on http://127.0.0.1:3000/ Application needs to be re-compiled with -threaded flag ExitFailure 1 Nice, the program runs, but complains about a missing compile-time flag. So let’s just do what it says – for your own non-codeworld programs, this is not necessary: $ ghc --make -O -threaded LastWeek.hs
Linking LastWeek ...

The other operator is ($). This does not really do anything: f$ x is the same as f x. The trick is that $ has a low precedence, so you can use it to avoid parenthesis. Instead of trice f x = f (f (f x)) you can write trice f x = f$ f $f x When your arguments get large, this is a real time-saver. ## A glimpse of IO We will go into it in more detail later, but here is a glimpse. When you compile a program using ghc, its behaviour is determined by the main function, which needs to be of type IO (). In CodeWorld, we have had functions such as drawingOf or interactionOf that created a value of type IO (). In the prelude, these functions might be of interest to you at this point: putStr :: String -> IO () print :: Show a => a -> IO () interact :: (String -> String) -> IO () The function putStr simply outputs a string, print uses show to print some value, which is good for debugging, and finally interact reads the standard input of the program, runs the given function on it, and prints the result. You can already write many useful programs using interact! # Modules I have mentioned that there are modules besides Prelude, and you have already used the CodeWorld module. Here is a quick primer how to use modules: In the simplest case, you simply import a module import Data.List import CodeWorld ## Restricted imports You can restrict the import to certain types and functions. This is recommended in large, well-maintained code, as it is less likely to break your code if a newer version of the imported module contains a new name that clashes with one of yours: import Data.Function (on, (&)) import Data.Fixed (Fixed(MkFixed), E0) import Data.Complex (Complex(..)) You can selectively not import some function (and you can even do that with the Prelude): import Prelude hiding (head, tail) ## Qualifed imports You can also import a module qualified. You need to fully qualify its functions then: import qualified Data.Text With this, you would use the type Data.Text.text and the function Data.Text.lines. A common idiom is to import the type unqualified, and the whole module with a short name: import Data.Text (Text) import qualified Data.Text as T Now you can use Text in your type signatures, and use T.lines in the code. Certain modules, such as Data.Text or Data.Map, are designed to be imported qualified and intentionally use names that would otherwise clash with those in the Prelude. ## Install more packages Many more modules come with separate packages, which need to be downloaded and installed separately. You can use the cabal tool for that. For example to install the text package, you would run $ cabal update
\$ cabal install text`

To find packages, look at the package repository Hackage or consult the API search engine hayoo.