Introduction to Haskell

From Fram

Revision as of 06:10, 23 November 2009 by Haakon (Talk | contribs)
(diff) ←Older revision | Current revision (diff) | Newer revision→ (diff)
Jump to: navigation, search

Haskell is a functional programming language, and is rather different from imperative languages like C. Hal Daumé III has written Yet Another Haskell Tutorial, which I recommend as your first stop on your journey into Haskell. This introduction is based on Daumé's tutorial.

Naturally, Fram crew can add to the explanations or enhance the source code examples. For the convenience of both crew, passengers and possible stow aways, source code interspersed with explanation will be concatenated into downloadable Haskell files, which you may load directly into your interactive Haskell environment.

Contents


Getting started

For beginners, Daumé recommends getting The Glasgow Haskell Compiler - GHC for short, since it contains both a compiler and an interactive environment. The current stable release is GHC 6.10.1. Download and install GHC for your operating system now, start the interactive environment GHCi, and come back here.

Exercise 1 - Don't panic - get help

Upon starting GHCi, you will see something like this:

GHCi, version 6.10.1: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer ... linking ... done.
Loading package base ... linking ... done.
Prelude> _

The underscore sign at the end is the blinking cursor, and you can start typing Haskell into the interactive environment. Let's start by looking at the available help, so type the following and hit the Return- or Enter-key:

:?

You should get something like this:

Prelude> :?
 Commands available from the prompt:

   <statement>                 evaluate/run <statement>
   :                           repeat last command
   :{\n ..lines.. \n:}\n       multiline command
   :add [*]<module> ...        add module(s) to the current target set
   :browse[!] [[*]<mod>]       display the names defined by module <mod>
                               (!: more details; *: all top-level names)
   :cd <dir>                   change directory to <dir>
   :cmd <expr>                 run the commands returned by <expr>::IO String
   :ctags [<file>]             create tags file for Vi (default: "tags")
   :def <cmd> <expr>           define a command :<cmd>
   :edit <file>                edit file
   :edit                       edit last module
   :etags [<file>]             create tags file for Emacs (default: "TAGS")
   :help, :?                   display this list of commands
   :info [<name> ...]          display information about the given names
   :kind <type>                show the kind of <type>
   :load [*]<module> ...       load module(s) and their dependents
   :main [<arguments> ...]     run the main function with the given arguments
   :module [+/-] [*]<mod> ...  set the context for expression evaluation
   :quit                       exit GHCi
   :reload                     reload the current module set
   :run function [<arguments> ...] run the function with the given arguments
   :type <expr>                show the type of <expr>
   :undef <cmd>                undefine user-defined command :<cmd>
   :!<command>                 run the shell command <command>

 -- Commands for debugging:

   :abandon                    at a breakpoint, abandon current computation
   :back                       go back in the history (after :trace)
   :break [<mod>] <l> [<col>]  set a breakpoint at the specified location
   :break <name>               set a breakpoint on the specified function
   :continue                   resume after a breakpoint
   :delete <number>            delete the specified breakpoint
   :delete *                   delete all breakpoints
   :force <expr>               print <expr>, forcing unevaluated parts
   :forward                    go forward in the history (after :back)
   :history [<n>]              after :trace, show the execution history
   :list                       show the source code around current breakpoint
   :list identifier            show the source code for <identifier>
   :list [<module>] <line>     show the source code around line number <line>
   :print [<name> ...]         prints a value without forcing its computation
   :sprint [<name> ...]        simplifed version of :print
   :step                       single-step after stopping at a breakpoint
   :step <expr>                single-step into <expr>
   :steplocal                  single-step within the current top-level binding
   :stepmodule                 single-step restricted to the current module
   :trace                      trace after stopping at a breakpoint
   :trace <expr>               evaluate <expr> with tracing on (see :history)

 -- Commands for changing settings:

   :set <option> ...           set options
   :set args <arg> ...         set the arguments returned by System.getArgs
   :set prog <progname>        set the value returned by System.getProgName
   :set prompt <prompt>        set the prompt used in GHCi
   :set editor <cmd>           set the command used for :edit
   :set stop <cmd>             set the command to run when a breakpoint is hit
   :unset <option> ...         unset options

  Options for ':set' and ':unset':

    +r            revert top-level expressions after each evaluation
    +s            print timing/memory stats after each evaluation
    +t            print type after evaluation
    -<flags>      most GHC command line flags can also be set here
                         (eg. -v2, -fglasgow-exts, etc.)
                    for GHCi-specific flags, see User's Guide,
                    Flag reference, Interactive-mode options

 -- Commands for displaying information:

   :show bindings              show the current bindings made at the prompt
   :show breaks                show the active breakpoints
   :show context               show the breakpoint context
   :show modules               show the currently loaded modules
   :show packages              show the currently active package flags
   :show languages             show the currently active language flags
   :show <setting>             show value of <setting>, which is one of
                                  [args, prog, prompt, editor, stop]

Prelude>

Exercise 2 - Division, multiplication - where do the parenthesis go?

As a professional handsome programmer, your wallet is naturally filled with different currencies from your international travels. On returning home, you want everything in one currency for your next trip. To check that operator precedence is working, try adding 10 US dollars to 5 Euro by exchanging both into Norwegian kroner and then into British pounds. The current exchange rate is 1 USD equals 6.9824 NOK, 1 EUR equals 9.7600 NOK and 1 GBD equals 10.340 NOK.

Prelude> 10*6.9824+5*9.7600/10.340
74.54353578336557
Prelude> (10*6.9824+5*9.7600)/10.340
11.472340425531915
Prelude>

Exercise 2 : Which is the correct answer - 74.54 or 11.47?

Your answer:

Bonus question: What's the answer to (10/3)*3 in GHCi? Were you surprised? Why not?

Your answer:

Exercise 3 - Loading source code

As a professional beautiful programmer, you don't care for wasting time looking up exchange rates yourself - you adhere to the ILD principle ("ild" is Norwegian for "fire"), ILD being a Norwegian acronym for Ikke Lek Datamaskin - Do Not Play Computer - that's the computer's job. At this point, you probably haven't read Chapter 27. Sockets and Syslog in the book Real World Haskell by Bryan O'Sullivan, Don Stewart, and John Goerzen, so you don't really know how to open a network connection to the Internet and get the current exchange rates. However, we can pretend, and in the process learn to load a file with Haskell source code - good, eh?

We will create a new module called Currency, and we will pretend that this module is getting the daily exchange rates over the network in the background from a nice, respectable place like Norway’s central bank - Norges Bank. You create the module in your favorite editor by typing the following:

module Currency

That didn't hurt too much? When reading the code, we want to know toward what end it is working, so we need a friendly keyword to tell us

	where

it is going. You noticed the keyword "where" was indented, right? Okay, we will finish by creating some single value variables to hold the different currencies. Remembering you are a beautiful Haskell programmer in high demand around the globe, your wallet is updated with a couple more currencies, like this:

usd = 6.9824
eur = 9.7600
gbp = 10.340
jpy = 7.7442

As you are on board the Fram, you may concatenate and download just the source code into Currency.hs, and then we will load this into GHCi to play with. Notice that the filename is the same as the module name. If your browser doesn't offer to open GHCi with Currency.hs as its argument, then load the module by first changing into the download directory like this

Prelude> :cd C:\Documents and Settings\Haakon\Desktop

and then loading the new module

Prelude> :load Currency.hs

If all goes well, you will have a new prompt

*Currency> _

and you can simplify the calculation we did earlier by using the variable names, like this

*Currency> (10*usd + 5*eur)/gbp
11.472340425531915
*Currency>

Now, if you make last minute changes to your travel plan, making Japan your next port of call, you can just use the up arrow on your keyboard and exchange gbp for jpy, with the following result:

*Currency> (10*usd + 5*eur)/jpy
15.317786214199012
*Currency>

Exercise 4 - Variable pairs and string concatenation

Rounding off our little excursion into arithmetic and module creation with Haskell, we will take a look at redoing our example with heterogeneous variable pairs.

Let us call this

module CurrencyVariablePairs

It doesn't roll easily over the tongue, but

	where

it is going is quite obvious. Haskell allows different types of variables - hence heterogeneous - to be stored in pairs, and so we can create these pairs:

usd = (6.9824,"US dollars")
eur = (9.7600,"Euro")
gbp = (10.340,"British pound")
jpy = (7.7442,"Japanese Yen")

Fram will help you download the source code in CurrencyVariablePairs.hs. Run this in GHCi before you continue.

We can still calculate the amount of British pounds, like this

*CurrencyVariablePairs> (10*fst usd + 5*fst eur)/fst gbp
11.472340425531915

I'm sure you noticed the fst in front of a pair name, and you were right to assume this read the first part of the pair. You would be equally right if you guessed that snd will allow access to the second part of the pair - like this:

*CurrencyVariablePairs> "USD is short for " ++ snd usd
"USD is short for US dollars"
*CurrencyVariablePairs>

In the process you have just learned to concatenate strings and variables in Haskell - well done!

Final words

If you have tried to download the source code from the exercises, I hope you too have found Fram helpful. A step-by-step guide on how to write in a literate programming way is provided in our help section, where you will also find more Haskell references if you want to explore this interesting programming language.

Personal tools