Difference between revisions of "A brief introduction to Haskell"

From HaskellWiki
Jump to navigation Jump to search
(more notes)
m (more notes)
Line 213: Line 213:
 
<haskell>
 
<haskell>
 
import Char
 
import Char
  +
</haskell>
  +
  +
=== Overloading ===
  +
  +
Haskell uses ''typeclasses'' to methodically allow overloading. A
  +
typeclass describes a set of functions, and any type which provides
  +
those functions can be made an ''instance'' of that type. This avoids
  +
the syntactic redundancy of languages like OCaml.
  +
  +
For example, the function <hask>*</hask> is a method of the typeclass
  +
<hask>Num</hask>, as we can see from its type:
  +
  +
<haskell>
  +
Prelude> :t (*)
  +
(*) :: (Num a) => a -> a -> a
  +
</haskell>
  +
  +
Which can be read as "* is a polymorphic function, taking two types 'a',
  +
and returning a result of the same type, where those types are members
  +
of the class Num".
  +
  +
This means that it will operate on any type in the <hask>Num</hask>
  +
class, of which the following types are members: <hask>Double, Float,
  +
Int, Integer</hask>. Thus:
  +
  +
<haskell>
  +
Prelude> 2.3 * 5.7
  +
13.11
  +
</haskell>
  +
  +
or on integers:
  +
  +
<haskell>
  +
Prelude> 2 * 5
  +
10
  +
</haskell>
  +
  +
The type of the arguments determines which instance of <hask>*</hask> is
  +
used. Haskell also never performs implicit coercions, all coercions must
  +
be explicit. For example, if we try to multiply two different types,
  +
then the type check against <hask>* :: Num a => a -> a -> a</hask> will
  +
fail.
  +
  +
<haskell>
  +
Prelude> (2.3 :: Double) * (5 :: Int)
  +
  +
<interactive>:1:19:
  +
Couldn't match `Double' against `Int'
  +
Expected type: Double
  +
Inferred type: Int
  +
In the expression: 5 :: Int
  +
In the second argument of `(*)', namely `(5 :: Int)'
  +
</haskell>
  +
  +
To convert 5 to a <hask>Double</hask> we'd write:
  +
  +
<haskell>
  +
Prelude> (2.3 :: Double) * fromIntegral (5 :: Int)
  +
11.5
  +
</haskell>
  +
  +
Why bother -- why not allow the system to implicitly coerce types?
  +
Implicit type conversions by the system are the source of innumerable
  +
hard to find bugs in languages that support them, and makes reasoning
  +
about a program harder, since you must apply not just the language's
  +
semantics, but an extra set of coercion rules.
  +
  +
Note that If we leave off the type signatures however, Haskell will
  +
helpfully infer the most general type:
  +
  +
<haskell>
  +
Prelude> 2.3 * 5
  +
11.5
 
</haskell>
 
</haskell>
   

Revision as of 01:41, 27 October 2006


Haskell is:

  • A language developed by the programming languages research community.
  • Is a lazy, purely functional language (that also has imperative features such as side effects and mutable state, along with strict evaluation)
  • Born as an open source vehicle for programming language research
  • One of the youngest children of ML and Lisp
  • Particularly useful for programs that manipulate data structures (such as compilers and interpreters), and for concurrent/parallel programming

Inspired by the Introduction to OCaml.

Background

History:

  • 1990. Haskell 1.0
  • 1991. Haskell 1.1
  • 1993. Haskell 1.2
  • 1996. Haskell 1.3
  • 1997. Haskell 1.4
  • 1998. Haskell 98
  • 2000-2006. Period of rapid language and community growth
  • ~2007. Haskell Prime

Implementations:

Haskell features

Has some novel features relative to Java (and C++).

  • Immutable variables by default (mutable state programmed via monads)
  • Pure by default (side effects are programmed via monads)
  • Lazy evaluation: results are only computed if they're required (strictness optional)
  • Everything is an expression
  • First-class functions: functions can be defined anywhere, passed as arguments, and returned as values.
  • Both compiled and interpreted implementations available
  • Full type inference -- type declarations optional
  • Pattern matching on data structures -- data structures are first class!
  • Parametric polymorphism
  • Bounded parametric polymorphism

These are all conceptually more advanced ideas.

Compared to similar functional languages, Haskell differs in that it has support for:

  • Lazy evaluation
  • Pure functions by default
  • Monadic side effects
  • Type classes
  • Syntax based on layout

The GHC Haskell compiler, in particular, provides some interesting extensions:

  • Generalised algebraic data types
  • Impredicative types system
  • Software transactional memory
  • Parallel, SMP runtime system

The Basics

Read the language definition to supplement these notes. For more depth and examples see the Haskell wiki.

Interacting with the language

Haskell is both compiled and interpreted. For exploration purposes, we'll consider interacting with Haskell via the GHCi interpreter:

  • expressions are entered at the prompt
  • newline signals end of input

Here is a GHCi sessoin, starting from a UNIX prompt.

   $ ghci
      ___         ___ _
     / _ \ /\  /\/ __(_)
    / /_\// /_/ / /  | |      GHC Interactive, version 6.4.2, for Haskell 98.
   / /_\\/ __  / /___| |      http://www.haskell.org/ghc/
   \____/\/ /_/\____/|_|      Type :? for help.
   Loading package base-1.0 ... linking ... done.

Here the incredibly simple Haskell program let x = 3+4 is compiled and loaded, and available via the variable x.

Prelude> let x = 3 + 4

We can ask the system what type it automaticaly inferred for our variable. x :: Integer means that the variable x "has type" Integer, the type of unbounded integer values.

Prelude> :t x
x :: Integer

A variable evaluates to its value.

Prelude> x
7

The variable x is in scope, so we can reuse it in later expressions.

Prelude> x + 4
11

Local variables may be bound using let, which declares a new binding for a variable with local scope.

Prelude> let x = 4 in x + 3
7

Alternatively, declarations typed in at the top level are like an open-ended let:

Prelude> let x = 4
Prelude> let y = x + 3
Prelude> x * x
16

Prelude> :t x
x :: Integer
Prelude> :t y
y :: Integer
Prelude> :t x * x
x * x :: Integer

Notice that type inference infers the correct type for all the expressions, without us having to ever specify the type explicitly.

Basic types

There is a range of basic types, defined in the language Prelude.

Int         -- bounded, word-sized integers
Integer     -- unbounded integers
Double      -- floating point values
Char        -- characters
String      -- strings
()          -- the unit type
Bool        -- booleans
[a]         -- lists
(a,b)       -- tuples / product types
Either a b  -- sum types
Maybe a     -- optional values

For example:

7
12312412412412321
3.1415
'x'
"haskell"
()
True, False
[1,2,3,4,5]
('x', 42)
Left True, Right "string"
Nothing, Just True

These types have all the usual operations on them, in the standard libraries.

Libraries

  • The Prelude contains the core operations on basic types. It is imported by default into every Haskell module. For example;
+ - div mod && || not < > == /=

Learn the Prelude well. Less basic functions are found in the standard libraries. For data structures such as List, Array and finite maps.

To use functions from these modules you have to import them, or in GHCi, refer to the qualified name, for example to use the toUpper function on Chars:

Prelude> Char.toUpper 'x'
'X'

Prelude> :m + Char
Prelude Char> toUpper 'y'
'Y'

In a source file, you have to import the module explicitly:

import Char

Overloading

Haskell uses typeclasses to methodically allow overloading. A typeclass describes a set of functions, and any type which provides those functions can be made an instance of that type. This avoids the syntactic redundancy of languages like OCaml.

For example, the function * is a method of the typeclass Num, as we can see from its type:

Prelude> :t (*)
(*) :: (Num a) => a -> a -> a

Which can be read as "* is a polymorphic function, taking two types 'a', and returning a result of the same type, where those types are members of the class Num".

This means that it will operate on any type in the Num class, of which the following types are members: Double, Float, Int, Integer. Thus:

Prelude> 2.3 * 5.7
13.11

or on integers:

Prelude> 2 * 5
10

The type of the arguments determines which instance of * is used. Haskell also never performs implicit coercions, all coercions must be explicit. For example, if we try to multiply two different types, then the type check against * :: Num a => a -> a -> a will fail.

Prelude> (2.3 :: Double) * (5 :: Int)

<interactive>:1:19:
    Couldn't match `Double' against `Int'
      Expected type: Double
      Inferred type: Int
    In the expression: 5 :: Int
    In the second argument of `(*)', namely `(5 :: Int)'

To convert 5 to a Double we'd write:

Prelude> (2.3 :: Double) * fromIntegral (5 :: Int)
11.5

Why bother -- why not allow the system to implicitly coerce types? Implicit type conversions by the system are the source of innumerable hard to find bugs in languages that support them, and makes reasoning about a program harder, since you must apply not just the language's semantics, but an extra set of coercion rules.

Note that If we leave off the type signatures however, Haskell will helpfully infer the most general type:

Prelude> 2.3 * 5
11.5

Functions (including Patterns and Higher-Order Functions)

Declaring our own types

Imperative features: monadic IO, references, mutable arrays, exceptions

Type classes