COP 4020 Lecture -*- Outline -*-
* The Haskell Language (4.7) (skip)
successful, practical, completely declarative language
** features
------------------------------------------
HASKELL (See haskell.org)
nonstrict
strongly typed
functional
implicit currying
monads
Example:
> t :: Integer -> Integer
> t n = if odd n then (3*n+1) `div` 2
> else n `div` 2
> hailstones :: Integer -> [Integer]
> hailstones n = n : hailstones (t n)
------------------------------------------
nonstrict means it's lazy
monads can be used to replace state
Explain the example's notation
Also note that indentation is significant!
Try: take 20 (hailstones 27)
** computation model (4.7.1)
------------------------------------------
COMPUTATION MODEL (4.7.1)
Don't evaluate expression unless
Consider an expression as a tree.
When needed, Haskell
- evaluates leftmost subexpression
if it's a constructor, then done
if it's a function, and no args,
then done
if it's a function with args,
then apply to first argument
by substituting argument in body
then reevaluate
Needs:
- built-in functions (+, -, *)
- pattern matching
------------------------------------------
... it is definitely needed
acts like normal order evaluation, only does as much as necessary
** Lazy evaluation (4.7.2)
------------------------------------------
LAZY EVALUATION (4.7.2)
Laziness:
functions evaluate expressions
at most once
Example:
> h27 = hailstones 27
------------------------------------------
This causes no problem, even though it's infinite,
unless we try to print it...
try: take 5 h27
** Currying (4.7.3)
------------------------------------------
IMPLICIT CURRYING (4.7.3)
Functions are implictly curried
> add x y = x + y
> add3 = add 3
> result = add3 7
> doubleList = map (2*)
------------------------------------------
The notation (2*) is a section, shorthand for (\ x -> 2*x)
which is like fun {$ X} 2 * X end in Oz
** polymorphic types (4.7.4)
------------------------------------------
TYPE NOTATION
Type operators
operator meaning
=============================
_ -> _ function type
(_ , _) product type
[ _ ] list type
Associativity
b f g x means (((b f) g) x)
a -> b -> c means a -> (b -> c)
------------------------------------------
Q: Why do you think the associativity is different for applications
and for function types?
** polymorphic types (Thompson 9.2, Davie 4.2)
------------------------------------------
POLYMORPHIC TYPES
Monomorphic examples:
Integer
[Bool] -> Bool
[(Integer, Integer) -> Bool]
Polymorphic examples:
[a]
[b] -> b
[(c,c) -> Bool]
------------------------------------------
Q: What are some expressions that have these types?
Explain in terms of universal quantifiers.
Q: What are some other instances of these types?
------------------------------------------
ALGEBRAIC TYPES
Can simulate enumerations
> data Font = Roman | Italic | Bold
data Color =
data Boolean =
Can also be used to define recursive
types, including
data HaskellType =
------------------------------------------
... Red | Blue | Yellow
... True | False
Note that the constructor names must start with upper case letter!
... including grammars
... Unit | BuiltIn String
| Synonym String
| List HaskellType
| HaskellType `Arrow` HaskellType
| Tuple [HaskellType]
| Data String
------------------------------------------
OVERVIEW OF TYPE INFERENCE
Type checking: you declare type,
compiler infers type,
compiler compares
Type inference: compiler infers type
In Haskell don't need to declare types
(usually)
Example
> mymap f [] = []
> mymap f (x:xs) = (f x):(mymap f xs)
------------------------------------------
do the inference (quickly, we'll see more later)
mymap :: a = (b -> c)
f :: b
etc.
since the variables are unconstrained, this is polymorphic
** Type classes (Thompson 12, Davie 4.8) (CTM 4.7.5)
See paper by Wadler and Blott (POPL 89)
------------------------------------------
AD HOC POLYMORPHISM
parametric polymorphism:
map :: (a -> b) -> [a] -> [b]
ad hoc polymorphism
> square :: Num a => a -> a
> square x = x * x
------------------------------------------
the point is that square x only works on those x for which * is defined
it's illuminating to make a parametric version of this:
squareP :: (a -> a -> a) -> a -> a
squareP mult x = x `mult` x
So what square needs to be polymorphic, is an additional "capability",
the appropriate multiplication routine.
Q: Why not require that you actually pass the multiplication yourself?
Q: What's done in OO programming?
lookup the method, based on the run-time type
this isn't the run-time type, but the static type
(note that there's no subtyping in Haskell,
we know exact type of everything)
But the idea is similar, we group types with related sets of
operations into type classes,
the type context (Num a =>) gives the name of the type class
required
*** type classes (Thompson 12.4)
------------------------------------------
TYPE CLASSES IN HASKELL
-- abbreviated Eq type class
class Eq a where
(==), (/=) :: a -> a -> Bool
x /= y = not (x==y)
-- abbreviated Ord type class
class (Eq a) => Ord a where
compare :: a -> a -> Ordering
(<), (<=), (>=), (>) :: a -> a -> Bool
max, min :: a -> a -> a
------------------------------------------
there is a "default method" provided for /=, and the ordering stuff
This is just like an "abstract class" in OOP,
but remember it's *static* overloading
data Ordering = LT | EQ | GT
deriving (Eq, Ord, Ix, Enum, Read, Show, Bounded)
The Ord type class is a *subclass* of Eq, it inherits defs from Eq,
really if you think of these as requirements,
it's a refinement (stronger requirement) on clients.
There are also various ways to declare that a type you make up
is an instance of a type class.
*** type class instances (Thompson 12.3)
------------------------------------------
DECLARING TYPE CLASS INSTANCES
> data Prod a b = a :* b
> instance (Eq a, Eq b)
> => Eq (Prod a b) where
> (x :* y) == (x' :* y') =
> (x == x' && y == y')
or you can write:
> data Cartesian a b = a :** b deriving Eq
------------------------------------------
pitfall: apparently the "where" can't be at the same indentation level
as "instance", although I don't see why...