Вы находитесь на странице: 1из 32

15-150 Fall 2014

Lecture 9

Stephen Brookes

Polymorphism

Functions as values

Higher-order functions

the score so far


MEAN

MEDIAN

15-150 Homeworks
96

93.75

91.5

89.25

87
HW01

HW02

HW03

Previously
Types and the type guarantee

Polymorphic types and their instances

Rules for type checking or inference
If e has type t and e =>* v

then v is a value of type t

polymorphic values?
What are the values of type a list
Only [ ]

What are the values of type a ?


There are none!
Reason:
the type guarantee

typability
t is a type for e

iff (e has type t) is provable

In the scope of d, x has type t

from the


inference rules

iff (d declares x:t) is provable

fun rev [ ] = [ ]

| rev (x::L) = (rev L) @ [x]
int list -> int list

is a type for

rev

real list -> real list


a list -> a list

is a type for
is a type for

rev
rev

Instantiation
If e has type t, and t is an instance of t,
then e also has type t

An expression can be used at


any instance of its type
id : a -> a
let

val id = fn x => x

in

id : bool -> bool
(id 2, id true)

end
id : int -> int

has type

int * bool

Most general types


Every well-typed expression
has a most general type
t is a most general type for e
iff every instance of t is a type for e

& every type for e is an instance of t

rev has most general type a list -> a list

type inference
ML computes most general types

statically, using syntax as guide
Standard ML of New Jersey v110.75

-
fun rev [ ] = [ ] | rev (x::L) = (rev L) @ [x];
val rev = fn : 'a list -> 'a list

datatypes
ML allows parameterized datatypes
datatype a tree = Empty

| Node of a tree * a * a tree
a type constructor tree
and polymorphic value constructors
Empty : a tree
Node : a tree * a * a tree -> a tree

example
fun trav Empty = [ ]

| trav (Node(t1, x, t2)) = (trav t1) @ x :: (trav t2)
declares trav : a tree -> a list

options
datatype a option = NONE | SOME of a
fun try (f, [ ]) = NONE

| try (f, x::L) = case (f x) of

NONE => try (f, L)

| SOME y => SOME y
try : (a -> b option) * a list -> b option

equality
ML allows use of = only on certain types

These are called equality types

int

tuples, lists, built from equality types



not real and not function types

ML uses type variables a, b, c


to stand for equality types

must be instantiated with an equality type

example
fun mem (x, [ ]) = false

| mem (x, y::L) = (x=y) orelse mem (x, L)
declares mem : a * a list -> bool
OK instances include
int * int list -> bool
(int list) * (int list) list -> bool
but not real * real list -> bool

And now
Functions as values

Higher-order functions

The power of polymorphism

Math problem
For real numbers a < b,
there is a linear function f : real -> real
such that
f(a) = ~1.0

f(b) = 1.0
Given a and b, find f.

linear means there are reals ,



such that for all x:real, f x = *x +

ML version
A function
norm : real * real -> (real -> real)
such that for all a, b : real with a < b,
norm(a, b) =>* a linear function f
satisfying

f(a) = ~1.0 and f(b) = 1.0
calculate , such that

*a + = ~1.0 and *b + =1.0

norm
fun norm(a, b) = fn x => (2.0 * x - a - b) / (b - a)

- val norm = fn : real * real -> real -> real


norm : real * real -> (real -> real)
norm is a function

that returns a function
The type of norm(~2.0, 2.0) is real -> real
The value of norm(~2.0, 2.0) is
fn x => (2.0 * x - (~2.0) - 2.0) / (2.0 - (~2.0))

This value is extensionally equal to fn x => x / 2.0

norm
Let a < b

For all x such that

a x b,
~1.0 norm(a, b) (x) 1.0

and

norm(a, b)

norm(a, b) a = ~1.0

norm(a, b) b = 1.0
normalizes the real interval [a...b]

using norm
Given a pair of reals,

normalize both components



using norm(~2.0, 2.0),

convert (1.0,1.5) to (0.5, 0.75)

Given a list of reals,

normalize each item in the list


using norm(~2.0, 2.0),

convert [1.0,1.5,1.8] to [0.5, 0.75, 0.9]

first attempts
fun normpair(a, b) =

fn (x,y) => (norm(a,b) x, norm(a,b) y)
or, better,
fun normpair(a, b) =

fn (x, y) => let

val f = norm(a,b)

in

(f x, f y)

end

critique
We often need to apply a function to
components of a data structure

to build a new data structure of the same shape


Its the same idea, regardless of the function


fun normpair(a, b) = fn (x,y) => (norm(a,b) x, norm(a,b) y)
fun fibpair(a, b) = (fib a, fib b)
fun factpair(x, y) = (fact x, fact y)

general problem
For pairs, we want a generic way to

apply a given function to components


For lists, we want a generic way to

apply a given function to list items


transforming a data structure

by applying a function to each datum

solutions
For pairs, a polymorphic function

pair : (a -> b) -> (a * a -> b * b)

For lists, a polymorphic function

map : (a -> b) -> (a list -> b list)


these are also

higher-order

functions

pair spec
pair : (a -> b) -> (a * a -> b * b)
(* REQUIRES true
(* ENSURES pair f (x, y) = (f x, f y)

*)

*)

For all types t1 and t2,



all values f : t1 -> t2, and all values x, y:t1,

pair f (x, y) = (f x, f y).

pair
fun pair f = fn (x, y) => (f x, f y)
pair : (a -> b) -> (a * a -> b * b)
pair(norm (~2.0, 2.0)) : real * real -> real * real

pair (norm (~2.0, 2.0)) (1.5, 1.5) =>* ?

map spec
map : (a -> b) -> (a list -> b list)
(* REQUIRES true
(* ENSURES For all n0,

map f [x1, ..., xn] = [f x1, ..., f xn]

*)

*)

For all n0, all types t1 and t2,



all values f : t1 -> t2, and all values x1, ..., xn:t1,

map f [x1, ..., xn] = [f x1, ..., f xn].

map

map f R = (map f) R

fun map f [ ] = [ ]

| map f (x::R) = (f x) :: (map f R)
map : (a -> b) -> (a list -> b list)
map (norm(~2.0, 2.0)) : real list -> real list
map (norm(~2.0, 2.0)) [1.0, 1.5. 2.0] =>* [0.5, 0.75, 1.0]

map

map f R = (map f) R

fun map f = fn L =>



case L of

[ ] => [ ]

| x::R => (f x) :: (map f R)
map : (a -> b) -> (a list -> b list)
map (norm(~2.0, 2.0)) : real list -> real list
map (norm(~2.0, 2.0)) [1.0, 1.5. 2.0] =>* [0.5, 0.75, 1.0]

adding sugar to curry


ML has a streamlined syntax for defining
curried higher-order functions

fun pair f = fn (x, y) => (f x, f y)

fun pair f (x,y) = (f x, f y)


fun map f = fn L => case L of

[ ] => [ ]

| x::R => (f x) :: (map f R)

fun map f [ ] = [ ]

| map f (x::R) = (f x) :: (map f R)

syntactic sugar
fun F p11 p12 = e1

| F p21 p22 = e2

...

| F pk1 pk2 = ek
declares F : t1 -> t2 -> t
if for each i, pi1 fits t1, pi2 fits t2, and
they produce type bindings for
which, assuming F : t1 -> t2 -> t,
ei has type t

so far

Using pair and map we can easily apply a

function f to the data in any data structure


built from pairs and lists

pair (fn x => x+1)

int * int -> int * int

map (fn x => x+1)

int list -> int list

map (pair (fn x => x+1))


(int * int) list -> (int * int) list
pair (map (fn x => x+1))
int list * int list -> int list * int list

using map

to build a recursively definable list


(* sublists : 'a list -> 'a list list *)!
(* ENSURES sublists L = a list of all sublists of L *)
fun
sublists [ ] = [ [ ] ]
!
| sublists (x::R) = !
! let !
val S = sublists R !
in
S @ map (fn A => x::A) S !
end
sublists [1,2,3] = [[ ],[3],[2],[2,3],[1],[1,3],[1,2],[1,2,3]]

Вам также может понравиться