Introduction
Install Haskell Platform 8.2.1. Make sure you're using the right version. Should be available on lab machines. Installing locally is also a good idea.
REPL (Read-Eval-Print Loop)
Launch the interactive Haskell shell (ghci
) from
the UNIX command prompt.
# ghci
GHCi, version 8.2.1: http://www.haskell.org/ghc/ :? for help
Prelude>
Numbers, Booleans, Strings, Characters
Try out integers, floating-point numbers, booleans strings, characters. Try some expressions that result in type errors.
Prelude> 4
Prelude> :set prompt "> "
> 4
> 4.0
> 4 + 4
> 4 + 4.0
> 'a'
> "a"
> "a" + "b"
> "a" ++ "b"
> "a" ++ 'b'
> True
> true
Types
We'll talk about types in much more detail later. For now, let's experiment a little.
> :type True
> :t True
Numeric Types
> :t 4
We'll dig into this in detail later. For now, read Num p => p
as "Int
and Float
and Double
and every other numeric
type you can think of". We can "convert" 4
to one of these
specific numeric types with an explicit type ascription, using
the ::
syntax.
> :t 4 :: Int
4 :: Int :: Int
> :t 4 :: Integer
4 :: Integer :: Integer
> :t 4 :: Float
4 :: Float :: Float
> :t 4 :: Double
4 :: Double :: Double
> :t 4.0 :: Double
4.0 :: Double :: Double
> :t 4.0 :: Int
Notice that operators like (+)
can work on many different
kinds of numbers.
> 4 + 5
> (+) 4 5
> :t (+)
(+) :: Num a => a -> a -> a
You can read the type for (+)
as saying "(+)
takes two
Int
s or two Float
s or two Double
s or two values of any
other numeric type and returns another number of the same type
as the arguments". Haskell automatically infers the
specific types based on how the expressions are used. But
we can also explicitly tell Haskell which types we want
using type ascription.
> :t (4 :: Int) + (5 :: Int)
> :t (4 + 5) :: Int
> :t (4 + 5) :: Float
> :t (4 :: Float) + 5
> :t (4 :: Float) + (5 :: Double)
> :t (4 :: Float) + (5 :: Int)
Strings as Lists of Characters
Notice that strings are lists of characters, written
[Char]
. We can define String
to be another way of
writing the type [Char]
.
> :t 'a'
> :t "a"
> type String = [Char]
> :t "a" :: String
> :t (++) :: String -> String -> String
Tuples
> :t ('a', True)
> :t ('a', True, 1)
History and Tab Completion
> <UP>
> :<TAB> -- lots of commands
> :set <TAB> -- lots of options and flags
> :set prompt "> "
Let-bindings
> let x = 1
> x + 1
> let y = 2
> x + y
If you're familar with variables in other languages, this is not the same thing. These bindings never change...
> let z = 0
> let z = 1 -- "shadowing"
> let z = z + 1 -- recursive def (not shadowing)
> z -- "forces" evaluation (diverges)
> Ctrl-C -- to interrupt
> Ctrl-D -- to quit GHCI
Multiple bindings in a single let
.
> let a = 1; b = 2 -- multiple bindings
> let a = 1; b = 2; -- last semi-colon is optional
Top-level vs. local let-bindings.
let x = 1 -- "global" binding
let y = 2 in y + y -- "local" binding
y -- not in scope
let y = 6
let y = 5 in y + y -- previous binding "shadowed" (locally)
y
Simple Function Definitions
> let add3 x = x + 3
> add3 1
> add3 1.1
> add3 0xDEADBEEF
> let add x y = x + y
> add 3 4
> add 3
... No instance for Show ... -- more on this next time
> let also_add (x, y) = x + y
> also_add 1 2 -- error
> add (1, 2) -- error
The the add
function takes two arguments, whereas the
also_add
function takes a single argument (a 2-element
tuple, a.k.a. a pair).
All functions in Haskell take exactly one argument and produce exactly one value in return. But what about the "multi-argument" functions above?
> let add4 = add 4 -- "currying" or partial application
> add4 10
> (add 4) 5
> add (4 5)
Functions can return functions! Hence, "multi-argument" functions are just nested single-argument functions.
e1 e2 e3 e4 e5 === ((((e1 e2) e3) e4) e5)
(e1 (e2 (e3 (e4 e5)))) === e1 $ e2 $ e3 $ e4 e5
We'll talk more about ($)
later. For now, just note that
it's a way to write right-associative nested function calls
without so many parentheses.
Source Files
So far, we've used the interactive shell. Now let's create
a standalone Haskell source file called Introduction.hs
.
Unlike in the shell, top-level definitions in Haskell source files
do not start with let
. Instead, they are written like
equations.
minutesPerDay = 60 * 24
Definitions can refer to subsequent definitions later in the file. Helper definitions can help improve readability and maintainability.
minutesPerDay = minutesPerHour * hoursPerDay
minutesPerHour = 60
hoursPerDay = 24
All definitions are at the "top-level" and are mutually recursive. These two features help make Haskell definitions read more like math.
Local variables can help improve readability and maintainability, by emphasizing the scope of the definitions. For example, instead of the previous three top-level definitions, we can use two locally defined bindings:
minutesPerDay =
let minutesPerHour = 60 in
let hoursPerDay = 24 in
minutesPerHour * hoursPerDay
Or better yet:
minutesPerDay =
let
minutesPerHour = 60
hoursPerDay = 24
in
minutesPerHour * hoursPerDay
Alternatively, where-clauses can be used instead of let-bindings.
minutesPerDay = minutesPerHour * hoursPerDay
where minutesPerHour = 60
hoursPerDay = 24
Like with let-bindings, minutesPerHour
and hoursPerDay
are
not accessible outside this definition.
For both let-bindings and where-clauses, can choose indendation depth but must choose the same starting column for all variables being defined. So, there are many possible formatting styles. Here's another one.
minutesPerDay = minutesPerHour * hoursPerDay where
minutesPerHour = 60
hoursPerDay = 24
Booleans and Guarded Definitions
> :t True
> :t 1 == 2
> :t 1 < 2
If-then-else expressions.
absoluteValue n =
if n >=0
then n
else -1 * n
Alternatively:
absoluteValue n
| n >= 0 = n
| n < 0 = -1 * n
We can use otherwise
as the last, "catch-all" guard.
And we can negate n
more concisely.
absoluteValue n
| n >= 0 = n
| otherwise = -n
Comments
-- single line comment
{- multi-line comment
-}
Loading Files
Load a source file from the shell.
> :load Introduction.hs
> :l Introduction.hs
> :l Introduction
> :reload
> :r
Library Documentation
The documentation for Haskell libraries
will be very useful throughout the course. Prelude
is a good place to start browsing.
This documentation should also be installed locally somewhere, such as
file:///Library/Haskell/doc/start.html
on a Mac; then click "Libraries".
Additional resources are listed at Haskell Documentation.
Hello, world!
Okay, we've said hello to the Haskell world. But where was printing "Hello, world!" to the output console? Patience...