ML Intro part 2

advertisement

More ML

Compiling Techniques

David Walker

Today

More data structures

 lists

More functions

More modules

Next week: Lexical Analysis

element

Lists

Lists are created with

 nil (makes empty list) head :: tail (makes a longer list)

5 :: nil : int list list of elements

element

Lists

Lists are created with

 nil (makes empty list) head :: tail (makes a longer list)

4 :: (5 :: nil) : int list list of elements

element

Lists

Lists are created with

 nil (makes empty list) head :: tail (makes a longer list)

3 :: 4 :: (5 :: nil) : int list list of elements

Lists

Lists are created with

3 :: (4 :: (5 :: nil)) : int list

3 :: 4 :: 5 :: nil : int list

(true, 1) :: (false, 2) :: nil : (bool * int) list

(3 :: nil) :: (2 :: nil) :: nil : (int list) list

Lists

Lists:

3 :: [] : int list

3 :: [4, 5] : int list

[true] : bool list a different way of writing “nil” a different way of writing a list

Lists

Bad List:

[4]::3;

???

Lists

Bad List:

[4]::3; stdIn:1.1-2.2 Error: operator and operand don't agree

[literal] operator domain: int list * int list list operand: int list * int in expression:

(4 :: nil) :: 3

Lists

Bad List:

[true, 5];

???

Lists

Bad List:

[true, 5]; stdIn:17.1-17.9 Error: operator and operand don't agree

[literal] operator domain: bool * bool list operand: bool * int list in expression: true :: 3 :: nil

List Processing

Functions over lists are usually defined by pattern matching on the structure of a list fun length l = case l of nil => 0 l _ :: l => 1 + (length l)

Hint: often, the structure of a function is guided by the type of the argument (recall eval)

List Processing

Two arguments f and l fun map f l = case l of nil => [] l x :: l => (f x) :: (map f l)

What does it do?

List Processing fun map f l = case l of nil => [] l x :: l => (f x) :: (map f l) applies the function f to every element in the list

- fun add1 x = x + 1;

- map add1 [1,2,3];

> val it = [2,3,4] : int list

ML is all about functions

There are many different ways to define functions!

I almost always use “fun f x = ...”

When I am only going to use a function once and it is not recursive, I write an anonymous function:

(fn x => ...)

Anonymous functions val n = 3 binds a variable (n) to a value (3) binds a variable

(isValue) fn keyword introduces anonymous fun val isValue =

(fn t => case t of

Bool _ => true

| t => false) to the anonymous function value

Anonymous functions fun map f l = case l of nil => [] l x :: l => (f x) :: (map f l) fun addlist x l = map ( fn y => y + x ) l anonymous functions

Anonymous functions a type definition (very convenient) type ifun = int -> int val intCompose : ifun * ifun -> ifun = ...

a pair of anonymous functions fun add3 x = intCompose ((fn x => x + 2), (fn y => y + 1)) x

Anonymous functions type ifun = int -> int pattern match against arg val intCompose : ifun * ifun -> ifun = fn (f,g) =>

(fn x => f (g x)) argument is pair of functions result is a function!

fun add3 x = intCompose ((fn x => x + 2), (fn y => y + 1)) x

Another way to write a function fun f x = ........ can be written as: val f = (fn x => ......) provided the function is not recursive; f does not appear in ........

Another way to write a function fun f x = ....

can always be written as: val rec f = (fn x => ...f can be used here...) keyword rec declares a recursive function value

Yet another way to write a function fun isValue Num n = true

| isValue (Bool b) = true

| isValue (_) = false

This is just an abbreviation for fun isValue t = case t of

Num n => true

| Bool b => true

| _ => false

Yet another way to create a type error fun isValue 0 = true

| isValue (Bool b) = true

| isValue (_) = false ex.sml:9.1-11.29 Error: parameter or result constraints of clauses don't agree [literal] this clause: term -> 'Z previous clauses: int -> 'Z in declaration: isValue =

(fn 0 => true

| Bool b => true

| _ => false)

Parametric Polymorphism

Functions like compose work on objects of many different types val compose = fn f => fn g => fn x => f (g x) compose (fn x => x + 1) (fn x => x + 2) compose not (fn x => x < 17)

Parametric Polymorphism

Functions like compose work on objects of many different types val compose = fn f => fn g => fn x => f (g x) compose not (fn x => x < 17) compose (fn x => x < 17) not

BAD!!

Parametric Polymorphism

Functions like compose work on objects of many different types val compose = fn f => fn g => fn x => f (g x) compose: (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) a type variable stands for any type

Note: type variables are written with ‘

Parametric Polymorphism

Functions like compose work on objects of many different types compose: (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) can be used as if it had the type: compose: (int -> ‘b) -> (‘c -> int) -> (‘c -> ‘b)

Parametric Polymorphism

Functions like compose work on objects of many different types compose: (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) can be used as if it had the type: compose: ((int * int) -> ‘b) -> (‘c -> (int * int)) -> (‘c -> ‘b)

Parametric Polymorphism

Functions like compose work on objects of many different types compose: (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) can be used as if it had the type: compose: (unit -> int) -> (int -> unit) -> (int -> int)

Parametric Polymorphism compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool

 compose not : ??

Parametric Polymorphism compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool

 compose not : ??

type of compose’s argument must equal the type of not: bool -> bool == (‘a -> ‘b)

Parametric Polymorphism compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool

 compose not : ??

therefore: ‘a must be bool

‘b must be bool as well

(in this use of compose)

Parametric Polymorphism compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool

‘a = bool

‘b = bool

 compose not : (‘c -> bool) -> (c’ -> bool)

Parametric Polymorphism compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool

 compose not : (‘c -> bool) -> (c’ -> bool)

(compose not) not : ??

Parametric Polymorphism compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b) not : bool -> bool

 compose not : (‘c -> bool) -> (c’ -> bool)

(compose not) not : bool -> bool

Parametric Polymorphism compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b)

 compose (fn x => x) : ?

Parametric Polymorphism compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b)

 compose (fn x => x) : ?

‘d -> ‘d

Parametric Polymorphism compose : (‘a -> ‘b) -> (‘c -> ‘a) -> (‘c -> ‘b)

 compose (fn x => x) : ?

‘d -> ‘d must be the same ie:

‘a = ‘d

‘b = ‘d

Parametric Polymorphism compose : (‘d -> ‘d) -> (‘c -> ‘d) -> (‘c -> ‘d)

 compose (fn x => x) : ?

‘d -> ‘d must be the same ie:

‘a = ‘d

‘b = ‘d

Parametric Polymorphism compose : (‘d -> ‘d) -> (‘c -> ‘d) -> (‘c -> ‘d)

 compose (fn x => x) : ?

‘d -> ‘d

(‘c -> ‘d) -> (‘c -> ‘d)

What is the type of map?

fun map f l = case l of nil => [] l x :: l => (f x) :: (map f l)

What is the type of map?

fun map f l = case l of nil => [] l x :: l => (f x) :: (map f l)

Hint: top-level shape is:

.... -> ... -> ....

What is the type of map?

fun map f l = case l of nil => [] l x :: l => (f x) :: (map f l)

Solution:

( ‘ a -> ‘ b) -> ‘ a list -> ‘ b list

ML Modules

Signatures

Interfaces

Structures

Implementations

Functors

Parameterized structures

Functions from structures to structures

Structures

 structure Queue = struct type ‘a queue = ‘a list * ‘a list exception Empty val empty = (nil, nil) fun insert (x, q) = … fun remove q = … end

Structures structure Queue = struct type ‘a queue = ‘a list * ‘a list end exception Empty

...

fun insert2 q x y =

Queue.insert (y, Queue.insert (q, x))

Structures structure Queue = struct

...

end structure Q = Queue fun insert2 q x y =

Q.insert (y, Q.insert (q, x)) convenient abbreviation

Structures structure Queue = struct end type ‘a queue = ‘a list * ‘a list

...

fun insert2 (q1,q2) x y : ‘a queue =

(x::y::q1,q2) by default, all components of the structure may be used

-- we know the type ‘ a queue

Signatures signature QUEUE = sig type ‘a queue exception Empty abstract type

-- we don ’ t know the type ‘ a queue val empty : ‘a queue val insert : ‘a * ‘a queue -> ‘a queue val remove : ‘a queue -> ‘a * ‘a queue end

Information hiding signature QUEUE = sig type ‘a queue

... end structure Queue :> QUEUE = struct type ‘ a queue = ‘ a list * ‘ a list val empty = (nil, nil)

… end does not type check fun insert2 (q1,q2) x y : ‘ a queue =

(x::y::q1,q2)

Signature Ascription

Opaque ascription

Provides abstract types structure Queue :> QUEUE = …

Transparent ascription

A special case of opaque ascription

Hides fields but does not make types abstract structure Queue_E : QUEUE = …

SEE Harper, chapters 18-22 for more on modules

Other things

 functors (functions from structures to structures) references (mutable data structures)

 ref e; !e; e1 := e2 while loops, for loops arrays

(* comments (* can be *) nested *) a bunch of other stuff...

Last Things

Learning to program in SML can be tricky at first

But once you get used to it, you will never want to go back to imperative languages

Check out the reference materials listed on the course homepage

Download