Download Chapter 11 - Functional Programming, Part II: ML, Delayed

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts

Closure (computer programming) wikipedia , lookup

Scala (programming language) wikipedia , lookup

Currying wikipedia , lookup

Falcon (programming language) wikipedia , lookup

Intuitionistic type theory wikipedia , lookup

Standard ML wikipedia , lookup

C Sharp (programming language) wikipedia , lookup

Transcript
Exercises
(1) What are the types of the following values?
[’a’,’b’,’c’]
(’a’,’b’,’c’)
[(False,’0’),(True,’1’)]
([False,True],[’0’,’1’])
[tail,init,reverse]
Chapter 11 - Part II
K. Louden, Programming Languages
1
(2) What are the types of the following functions?
second xs
= head (tail xs)
swap (x,y)
= (y,x)
pair x y
= (x,y)
double x
= x*2
palindrome xs = reverse xs == xs
twice f x
= f (f x)
(3) Check your answers using Hugs.
Chapter 11 - Part II
K. Louden, Programming Languages
2
Curried Functions
mult:: Int -> Int -> Int
mult x y = x * y

We could read this as a function taking two Int
arguments and returning a third. OR-- a
function taking a single Int argument and
returning a function from Int -> Int as a result.

Moral: the mult function can take either one or
two parameters; if you give it one, it returns a
function of the (missing) 2nd parameter

Ex: map (mult 3) [1,2,3]
Yields [3,6,9]
Chapter 11 - Part II
K. Louden, Programming Languages
3
Curried Functions

Contrast the previous definition of
mult with a definition using a tuple:
mult2 :: (Int,Int) -> Int
mult2 (x,y) = x *y
 Now we must always supply both
parameters at every call:
The mult function is Curried (named
after Haskell B. Curry), the mult2
function is not.
Chapter 11 - Part II
K. Louden, Programming Languages
4
Curried Functions

Definition: A function taking multiple parameters is
Curried if it can be viewed as a (higher-order)
function of a single parameter.

Currying is good, since all functions can be viewed
as having just a single parameter, and higher-order
functions can be obtained automatically.
Chapter 11 - Part II
K. Louden, Programming Languages
5
Haskell

A fully-Curried lazy purely functional
language with Hindley-Milner static typing.
(Fully-Curried means all functions,
including built-in arithmetic, are implicitly
Curried.)

Has many other features that make it
perhaps the most advanced functional
language available today.

Includes overloading and a built-in
mechanism for generator-filter
programming (called list
comprehensions).
Chapter 11 - Part II
K. Louden, Programming Languages
6
Sample Haskell code:
fact n =
if n == 0 then 1 else n * fact (n-1)
square x = x * x
pi1 = 3.14159
gcd1 u v =
if v == 0 then u else gcd1 v (mod u v)
squarelist lis =
if (null lis) then lis
else square (head lis):
squarelist (tail lis)
Chapter 11 - Part II
K. Louden, Programming Languages
7
Haskell properties

Parentheses are only necessary for grouping.
 Semicolons are usually unnecessary.
 Definitions have no special syntax, so they must be
loaded from a file—they cannot be entered directly
into an interpreter.
 Operators are infix.
 Lists are written using square brackets and commas
(e.g. [1,2,3]), with ++ as the list append operation,
and : (colon) as the list constructor operation.


All value and function names must start with a
lowercase letter, and all types are uppercase: Int.
Names cannot be redefined unless you state you are
overriding them - hence the use of pi1 & gcd1 in the
previous
slide K.
(pi
& Programming
gcd areLanguages
predefined).
Chapter
11 - Part II
Louden,
8
Haskell properties (2)

Haskell is purely functional, so there are no
variables or assignments, and I/O and sequencing
cannot be done except using special tools, called
monads (which we do not study here).

Of course, there are still local definitions (in other
words no value is being stored, we are just defining
the pattern):
let x = 2; y = 3 in x + y
or: let x = 2
y = 3 in x + y

Note indentation in the previous code to get rid of
the semicolon: Haskell uses a two-dimensional
Layout Rule to remove extra syntax. Leading white
space matters!
Chapter 11 - Part II
K. Louden, Programming Languages
9
Haskell properties (3)


Note lambda
syntax
All local definitions are recursive:
f x =
let z = x-1
g = \y->if y==0 then z else g(y-1)
in g x + 2
Comment
All expressions are delayed in Haskell:
ones = 1:ones -- can also write [1,1..]
ints_from n = n : ints_from (n+1)
ints = ints_from 1 -- also: [1..]

Infix operators can be made prefix by putting
parentheses around them:
doubleIt lis = map ((*) 2) lis
Chapter 11 - Part II
K. Louden, Programming Languages
10
Haskell properties (4)

Hindley-Milner type checking is performed on all
definitions and expressions. Type variables use the
names a, b, c, etc.

Types are not automatically displayed, but can be
viewed by using the :t command in the interpreter.
Small sample interpreter session:
> [1]++[2]
[1,2]
> :t (++)
(++) :: [a] -> [a] -> [a]
> [1]++['a'] – wants types to match
error:
No instance for (Num Char) arising from the literal `1' at
<interactive>:1:1 Probable fix: add an instance declaration for (Num
Char) In the list element: 1 In the first argument of `(++)', namely
Chapter
11 - Part
K. Louden,
Languages
11
`[1]'
In IIthe definition
of `it':Programming
it = [1] ++
['a']
Strong typing

Checks whether or not expressions we
wish to evaluate or definitions we wish to
use obey typing rules without any
evaluation taking place.

A pattern is consistent with a type if it will
match some elements of that type.
– A variable is consistent with any type
– A pattern (t:ts) is consistent with [p] if t is
consistent with p and ts is consistent with [p]
Chapter 11 - Part II
K. Louden, Programming Languages
12
Type Checking
f:: a->b->c
fxy
| g1 = e1
| g2 = e2
|otherwise = e3
We must check
1. g1 and g2 are boolean
2. x is consistent with a and y is consistent
with b
3. e1,e2,e3 are all of type c.
Chapter 11 - Part II
K. Louden, Programming Languages
13
Examples of type inference
f (x,y) = (x,[‘a’..y])
 What is the type of f?
 The argument of f is a pair. We
consider separately the constraints
of x and y. Y is used with [‘a’..y] so it
must be a Char.
 f :: (a, Char) -> (a, [Char])

Chapter 11 - Part II
K. Louden, Programming Languages
14
Examples of type inference






g(m,z) = m + length z
What constraints are placed on m and z?
M must be numeric, as used with +. z
must be a list as used with length.
Furthermore, since length returns an int,
we assume m is also an Int.
Now consider the function composition
The input of g must be the output of f
g . f :: (Int, Char) -> Int
*Main>
Chapter 11 - Part II
K. Louden, Programming Languages
15
Unification
We describe the intersection of the
sets given by two type expressions.
 The unification of the two is the most
general common instance of the two
type expressions.

Chapter 11 - Part II
K. Louden, Programming Languages
16
Unification need not result in a
monotype.
(a,[a]) and ([b],c) unify as
 ([b], [[b]])

Chapter 11 - Part II
K. Louden, Programming Languages
17
Patterns in Haskell




Patterns can be used in function definitions
Typical patterns are 0 for zero, [] for the empty list,
and (x:xs) for a nonempty list.
fact and squarelist :
fact 0 = 1
fact n = n * fact (n-1)
squarelist [] = []
squarelist (x:xs) =
(square x) : squarelist xs
Of course, it would be better to use map:
squarelist lis = map square lis
Chapter 11 - Part II
K. Louden, Programming Languages
18
Patterns in Haskell (cont.)

Because of patterns, it is unusual in Haskell
to use head, tail, and null.
 The anonymous wildcard pattern (matches
anything) is the underscore in Haskell.
 Overlapping patterns are ok (see fact on
previous slide). Non-exhaustive patterns can
also be used, and do not generate a warning
 Patterns are syntactic sugar for case
expressions:
fact n = case n of
0 -> 1
_ -> n * fact (n-1)
Chapter 11 - Part II
K. Louden, Programming Languages
19
Haskell List Comprehensions

Generators and filters can be expressed together in
Haskell in a quasi-list notation called a list
comprehension:
odds = [n | n <- [1..], mod n 2 /= 0]
-- can also write [1,3..]

Multiple generators and filters can be combined in a
single list comprehension:
mystery =
[n+m|n <-[1..], m <-[1..], mod n m /=
0]

List comprehensions allow many snappy programs
to be written: (mod without quotes is prefix)
sieve (h:t)
= h:sieve [n|n <- t, mod n h /= 0]
Chapter
11 - Part II = sieve
K. Louden,
Programming Languages
20
primes
[2..]
Haskell Data Structures




So far we have only seen lists in Haskell,
although we know that static typing does not
allow lists to imitate structs or classes as in
Scheme.
Haskell has built-in tuple types. which are
Cartesian products (like structs but with no field
names):
intWithRoot:: Int -> (Int,Double)
intWithRoot x = (x , sqrt (fromInt x))
Use patterns to get the components out:
rootOf x = let (_,r)=intWithRoot x in r
Tuple types are written the same way values are,
Chapter 11 - Part II
K. Louden, Programming Languages
21
Haskell Data Structures (2)
Lists and tuples do not go far enough, since unions cannot be
expressed.
 User-defined types can be introduced using data definitions:
data Direction = North|East|South|West
 Haskell can have polymorphic types and constructors with
more than one parameter
data Either1 a b = Left1 a | Right1 b
fun ::Either1 a b -> Bool
fun (Left1 _) = True
fun (Right1 _) = False

fun (Left1 5)
True
:t Left1 "hi"
Left1 "hi" :: Either1 [Char] b
Chapter 11 - Part II
K. Louden, Programming Languages
22
Haskell Data Structures (3)


Note how the previous definition expresses a
tagged union of two polymorphic types.
Binary search tree example (recursive type):
data BST a = Nil | Node a (BST a) (BST a)
simpleBST = Node "horse" Nil Nil -- value

Constructors can be used as patterns:
tree_to_list Nil = []
tree_to_list (Node val left right) =
(tree_to_list left) ++ [val]
++ (tree_to_list right)

Type synonyms can also be defined:
type IntDouble = (Int,Double)

Note: all type and constructor names must be
uppercase.
Chapter 11 - Part II
K. Louden, Programming Languages
23
member a -> [a] ->Bool
Can check for equality, but this only works
when a is a type for which equality is
defined.
How do we express that?
We call the collection of types over which a
function is defined to be the type class.
For instance, the set of types over which ==
is defined is the equality class.
Chapter 11 - Part II
K. Louden, Programming Languages
24
How do we define such a class

We say what is needed for a type a to be in
a class. In this case, we need == defined
over a. In other words, a ->a->Bool
class Eq a where
(==) :: a-> a-> Bool
Members of a type class are called its
instances.
Functions from Int->Int are NOT of type Eq
since there is no algorithm to decide if two
functions have the same behavior
Chapter 11 - Part II
K. Louden, Programming Languages
25
Overloading in Haskell

Many functions in Haskell are overloaded, in that
they can take values from a (finite) number of
different types. An easy example is the square
function, defined by square x = x * x.
 The type of square in Haskell is:
square :: Num a => a -> a
 This says basically that square is defined for any
Num a type (such types all have a * function).
 The type Num a => a is called a qualified type,
and Num is called a type class.

Type classes are a bit like Java interfaces: they
require that a certain function be defined, and
each associated type must then implement it.
Chapter 11 - Part II
K. Louden, Programming Languages
26
Overloading in Haskell (2)

Here is an example of how to define a Sizeable
type class that provides a measure of the size of
a piece of data:
class Sizeable a where
size:: a -> Int

Now any type that you want to implement
Sizeable must be declared an instance of the
Sizeable class: (i.e., belongs to that class):
instance Sizeable [a] where
size = length
instance Sizeable (BST a) where
size Nil = 0
size (Node d l r) =
(size l)+(size r) + 1
Chapter 11 - Part II
K. Louden, Programming Languages
27
Overloading in Haskell (3)


Now any use of the size function automatically
adds the Sizeable qualification to a type:
trivial x = size x == 0
This function has type:
Sizeable a => a -> Bool
Type classes usually require multiple functions:
class Num a where
(+), (-), (*) :: a -> a -> a
negate
:: a -> a
abs
:: a -> a ...etc.
Built-in "hidden"
instance Num Int where
definitions
(+)
= primPlusInt
(-)
= primMinusInt
negate
= primNegInt ...etc.
Chapter 11 - Part II
K. Louden, Programming Languages
28
Overloading in Haskell (4)

Type classes may need to "inherit" functions
from other type classes (similar to interface
inheritance in Java):
class Eq a where
(==), (/=) :: a -> a -> Bool
x == y
= not (x/=y)
Note default
definitions
x /= y
= not (x==y)
class (Eq a) => Ord a where
(<),(<=),(>=),(>) :: a -> a -> Bool
max, min
:: a -> a -> a
instance Eq Int where …
instance Ord Int where ...
Chapter 11 - Part II
K. Louden, Programming Languages
29
Numeric Type Class Hierarchy
Eq
(==)
Show
(show )
Ord
(<)
Num
(+,-,*)
Fractional
(/)
Real
Integral
(div, mod)
RealFrac
(round, trunc)
is read “is an instance of”
Chapter 11 - Part II
Floating
(sin, cos)
RealFloat
K. Louden, Programming Languages
30
Overloading in Haskell (5)

The Show class allows a data type to be displayed
as a String (e.g. by the interpreter):
class Show a where
show
:: a -> String

So many data types need to be made instances of
Show and Eq that Haskell can do it automatically:
data BST a =
Nil | Node a (BST a) (BST a)
deriving (Show,Eq)

Overloading presents challenges for Hindley-Milner
type checking that result in some surprises:
squarelist = map square
gives squarelist the type
[Integer] -> [Integer] (no overloading!)
Chapter 11 - Part II
K. Louden, Programming Languages
31