
-----------------------------------
wtd
Thu Mar 24, 2005 8:20 pm

Working in the GHC Interactive Environment
-----------------------------------
One of the keys to learning any programming language is learning how to run programs. Most of the time, though, we simply create a program, possibly compile it, then run it. It either runs fine or quits with errors, or gives us output we didn't expect. We can then go back and make modifications to our program and start the whole process over again.

Some languages*, though, have an additional tool. An interactive interpreter, which lets us get inside the program and test each little piece of it separately.

Haskell is just such a language, having both the Hugs interpreter and the interpreter component of the Glasgow Haskell Interpreter. Hugs is a worthy program, but GHCI is the more updated program, so I'll be covering it here. Fortunately Hugs and GHCI function very similarly.

* Such languages include Ruby, Python, Objective-Caml, SML/NJ, and Scheme, among others.

First Steps

The first step in using GHCI, of course, is to install GHC itself.  Linux users will likely be able to find a package for it using the package management tool of their choosing.  For Windows users an $ ghci
   ___         ___ _
  / _ \ /\  /\/ __(_)
 / /_\// /_/ / /  | |      GHC Interactive, version 6.2.2, for Haskell 98.
/ /_\\/ __  / /___| |      http://www.haskell.org/ghc/
\____/\/ /_/\____/|_|      Type :? for help.

Loading package base ... linking ... done.
Prelude>

The first bit is rather unimportant.  Some nice ASCII art and a bit of info about the version of GHC Interactive, as well as information about how to get help.

The "Loading package base ... linking ... done" line gives us a bit of a hint as to what the compiler is doing in the background to prepare the environment.

The last line is the important one.  The ">" is the prompt, at which we can enter expressions for GHCI to evaluate.  Everything in front of it, in this case "Prelude" describes the modules which are currently loaded.  The Prelude module is described Prelude> sin (pi / 2)
1.0
Prelude>

Of course, there are times when we need functions not included in Prelude.  Let's say we want to convert a String to all lowercase.  Well, we need the "toLower" function which converts a character to lowercase.  So we'd write it as:

Prelude> map toLower "Hello"

:1: Variable not in scope: `toLower'
Prelude>

What this is telling us is that the module toLower is in hasn't yet been imported.  We can fix this by loading the Prelude> :m Char
Prelude Char> map toLower "Hello"
"hello"
Prelude Char>

Note that the prompt has changed to indicate that the Char module and all of its functions are also now fair game, in addition to those in Prelude.

Our own modules

Now, we can of course create our own modules and load them.  Let's create a simple module in a file named SimpleModule.hs and give it a pretty simple function:

module SimpleModule where

isDivisibleByThree number = 
  mod number 3 == 0

Loading it into the interpreter is done like so:

Prelude> :l SimpleModule
Compiling SimpleModule             ( SimpleModule.hs, interpreted )
Ok, modules loaded: SimpleModule.
*SimpleModule>*SimpleModule> isDivisibleByThree 4
False
*SimpleModule> isDivisibleByThree 111
True
*SimpleModule>

Now, let's say we have the following module Main which contains the function main that just prints back all of the arguments fed into it which begin with 'h', and the helper function printOrNot which determines if a function should be printed:

module Main where

import System
import List

main = do
  args  :l Main
Compiling Main             ( Main.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
*Main>

So, why did nothing happen?  Well, the getArgs function only gets the arguments that have been provided.  Since we're not running this as a full-fledged program, there are no args.  Fortunately, GHCI gives us a way to fake it for testing purposes.

Prelude> :l Main
Compiling Main             ( Main.hs, interpreted )
Ok, modules loaded: Main.
*Main> :set args hello world foo bar
*Main> main
hello
world
foo
bar
*Main>

Finding Expression Types

Haskell is a very statically-typed language.  Every function has very strict rules on what it will take and what it will return.  As a result, we occassionally need to know what those types are.

Let's consider the 'map toLower "hello"' example used earlier.  In it, "map toLower" is a function itself.  Now, if we want to figure out what we can pass as an argument to this function we'll need to see its type.  This is very easily accomplished.

Prelude Char> :t map toLower
map toLower :: [Char] -> [Char]
Prelude Char>

Here we can see that it takes a list of characters (Wrapping it up

I hope this has served to explain some of the most useful workings of the Haskell interactive environment, and made the language seem a little more accessible than it did before.  Questions are, as always, welcome.
