CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming,...

27
CSC324 Functional Programming, Typing in ML Afsaneh Fazly 1 January 30, 2013 1 with many thanks to Anya Tafliovich, Gerald Penn, Sheila McIlraith, Wael Aboelsaddat, Tony Bonner, Eric Joanis, Suzanne Stevenson. 1 Good teaching is more a giving of the right questions than a giving of the right answers. – J. Albers 2 motivation Consider the following Scheme function definition: (define foobar (lambda (x) (if (even? x) (car x) (cdr x)))) Anything wrong? 3 motivation Consider the following Scheme function definition: (define foobar (lambda (x) (if (even? x) (car x) (cdr x)))) Anything wrong? even? expects a number and car, cdr expect a pair = we’ll get an error at run-time. 3

Transcript of CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming,...

Page 1: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

CSC324Functional Programming, Typing

in ML

Afsaneh Fazly1

January 30, 2013

1with many thanks to Anya Tafliovich, Gerald Penn, Sheila McIlraith, WaelAboelsaddat, Tony Bonner, Eric Joanis, Suzanne Stevenson.

1

Good teaching is more a giving of the right questionsthan a giving of the right answers.

– J. Albers

2

motivation

Consider the following Scheme function definition:

(define foobar

(lambda (x)

(if (even? x)

(car x)

(cdr x))))

Anything wrong?

even? expects a number andcar, cdr expect a pair=⇒ we’ll get an error at run-time.

Can we figure this out before run-time? Can’t a programminglanguage (PL) support this kind of reasoning?

3

motivation

Consider the following Scheme function definition:

(define foobar

(lambda (x)

(if (even? x)

(car x)

(cdr x))))

Anything wrong?

even? expects a number andcar, cdr expect a pair=⇒ we’ll get an error at run-time.

Can we figure this out before run-time? Can’t a programminglanguage (PL) support this kind of reasoning?

3

Page 2: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

motivation

Consider the following Scheme function definition:

(define foobar

(lambda (x)

(if (even? x)

(car x)

(cdr x))))

Anything wrong?

even? expects a number andcar, cdr expect a pair=⇒ we’ll get an error at run-time.

Can we figure this out before run-time? Can’t a programminglanguage (PL) support this kind of reasoning?

3

motivation

• It could be worse!

• At least Scheme checks that x is a pair before accessingmemory in the execution of (car x) and (cdr x) .

• In fact, Scheme is type safe: it will never execute (op arg)

if op is not applicable to arg .

• Languages which are not type safe (e.g., C) allow unsafememory accesses: a major source of security vulnerabilities.

4

motivation

• It could be worse!

• At least Scheme checks that x is a pair before accessingmemory in the execution of (car x) and (cdr x) .

• In fact, Scheme is type safe: it will never execute (op arg)

if op is not applicable to arg .

• Languages which are not type safe (e.g., C) allow unsafememory accesses: a major source of security vulnerabilities.

4

motivation

• It could be worse!

• At least Scheme checks that x is a pair before accessingmemory in the execution of (car x) and (cdr x) .

• In fact, Scheme is type safe: it will never execute (op arg)

if op is not applicable to arg .

• Languages which are not type safe (e.g., C) allow unsafememory accesses: a major source of security vulnerabilities.

4

Page 3: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

motivation

• It could be worse!

• At least Scheme checks that x is a pair before accessingmemory in the execution of (car x) and (cdr x) .

• In fact, Scheme is type safe: it will never execute (op arg)

if op is not applicable to arg .

• Languages which are not type safe (e.g., C) allow unsafememory accesses: a major source of security vulnerabilities.

4

motivation• Ideally, we would like to catch all errors at compile-time.

• Extremely difficult, if not impossible.

• Catch as many errors as possible?

• Costly: programmers’ expertise, computational resources, etc.• Slower development, less expressive languages.• Used in development of safety-critical systems.

• Compromise: identify a certain class of errors and guaranteeto catch all of them.

• E.g., use type systems to catch type errors.• Guarantees range widely: from basic safety of memory accesses

to just about anything you want.

5

motivation• Ideally, we would like to catch all errors at compile-time.

• Extremely difficult, if not impossible.

• Catch as many errors as possible?

• Costly: programmers’ expertise, computational resources, etc.• Slower development, less expressive languages.• Used in development of safety-critical systems.

• Compromise: identify a certain class of errors and guaranteeto catch all of them.

• E.g., use type systems to catch type errors.• Guarantees range widely: from basic safety of memory accesses

to just about anything you want.

5

motivation• Ideally, we would like to catch all errors at compile-time.

• Extremely difficult, if not impossible.

• Catch as many errors as possible?

• Costly: programmers’ expertise, computational resources, etc.• Slower development, less expressive languages.• Used in development of safety-critical systems.

• Compromise: identify a certain class of errors and guaranteeto catch all of them.

• E.g., use type systems to catch type errors.• Guarantees range widely: from basic safety of memory accesses

to just about anything you want.

5

Page 4: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

motivation• Ideally, we would like to catch all errors at compile-time.

• Extremely difficult, if not impossible.

• Catch as many errors as possible?• Costly: programmers’ expertise, computational resources, etc.

• Slower development, less expressive languages.• Used in development of safety-critical systems.

• Compromise: identify a certain class of errors and guaranteeto catch all of them.

• E.g., use type systems to catch type errors.• Guarantees range widely: from basic safety of memory accesses

to just about anything you want.

5

motivation• Ideally, we would like to catch all errors at compile-time.

• Extremely difficult, if not impossible.

• Catch as many errors as possible?• Costly: programmers’ expertise, computational resources, etc.• Slower development, less expressive languages.

• Used in development of safety-critical systems.

• Compromise: identify a certain class of errors and guaranteeto catch all of them.

• E.g., use type systems to catch type errors.• Guarantees range widely: from basic safety of memory accesses

to just about anything you want.

5

motivation• Ideally, we would like to catch all errors at compile-time.

• Extremely difficult, if not impossible.

• Catch as many errors as possible?• Costly: programmers’ expertise, computational resources, etc.• Slower development, less expressive languages.• Used in development of safety-critical systems.

• Compromise: identify a certain class of errors and guaranteeto catch all of them.

• E.g., use type systems to catch type errors.• Guarantees range widely: from basic safety of memory accesses

to just about anything you want.

5

motivation• Ideally, we would like to catch all errors at compile-time.

• Extremely difficult, if not impossible.

• Catch as many errors as possible?• Costly: programmers’ expertise, computational resources, etc.• Slower development, less expressive languages.• Used in development of safety-critical systems.

• Compromise: identify a certain class of errors and guaranteeto catch all of them.

• E.g., use type systems to catch type errors.• Guarantees range widely: from basic safety of memory accesses

to just about anything you want.

5

Page 5: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

motivation• Ideally, we would like to catch all errors at compile-time.

• Extremely difficult, if not impossible.

• Catch as many errors as possible?• Costly: programmers’ expertise, computational resources, etc.• Slower development, less expressive languages.• Used in development of safety-critical systems.

• Compromise: identify a certain class of errors and guaranteeto catch all of them.

• E.g., use type systems to catch type errors.

• Guarantees range widely: from basic safety of memory accessesto just about anything you want.

5

motivation• Ideally, we would like to catch all errors at compile-time.

• Extremely difficult, if not impossible.

• Catch as many errors as possible?• Costly: programmers’ expertise, computational resources, etc.• Slower development, less expressive languages.• Used in development of safety-critical systems.

• Compromise: identify a certain class of errors and guaranteeto catch all of them.

• E.g., use type systems to catch type errors.• Guarantees range widely: from basic safety of memory accesses

to just about anything you want.

5

typing

A type is:

“A name for a set of values and some operations which can beperformed on that set of values.”

“A collection of computational entities that share some commonproperty.”

6

typing

Some examples of types (in ML notation):

• int : the integers (and their operations).

• real : the real numbers (and their operations).

• string : the strings (and their operations).

• bool : the booleans (and their operations).

• int → bool : the functions that take integers as input andreturn booleans as output.

Note: what constitutes a type is language dependent.

7

Page 6: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

typing

Some examples of types (in ML notation):

• int : the integers (and their operations).

• real : the real numbers (and their operations).

• string : the strings (and their operations).

• bool : the booleans (and their operations).

• int → bool : the functions that take integers as input andreturn booleans as output.

Note: what constitutes a type is language dependent.

7

typing

Benefits of having a type system:

• Easier to debug programs: compiler can catch many errors.

• Static analysis: a lot of useful information about the programcan be obtained at compile-time.

• Efficiency: typing can be used by the compiler to generatequicker code.

• Correctness: typing can be used (by the programmer or by thecompiler) to prove correctness of code.

• Documentation: types declare your intent with well-chosennames.

8

typing

Benefits of having a type system:

• Easier to debug programs: compiler can catch many errors.

• Static analysis: a lot of useful information about the programcan be obtained at compile-time.

• Efficiency: typing can be used by the compiler to generatequicker code.

• Correctness: typing can be used (by the programmer or by thecompiler) to prove correctness of code.

• Documentation: types declare your intent with well-chosennames.

8

typing

Benefits of having a type system:

• Easier to debug programs: compiler can catch many errors.

• Static analysis: a lot of useful information about the programcan be obtained at compile-time.

• Efficiency: typing can be used by the compiler to generatequicker code.

• Correctness: typing can be used (by the programmer or by thecompiler) to prove correctness of code.

• Documentation: types declare your intent with well-chosennames.

8

Page 7: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

typing

Benefits of having a type system:

• Easier to debug programs: compiler can catch many errors.

• Static analysis: a lot of useful information about the programcan be obtained at compile-time.

• Efficiency: typing can be used by the compiler to generatequicker code.

• Correctness: typing can be used (by the programmer or by thecompiler) to prove correctness of code.

• Documentation: types declare your intent with well-chosennames.

8

typing

Benefits of having a type system:

• Easier to debug programs: compiler can catch many errors.

• Static analysis: a lot of useful information about the programcan be obtained at compile-time.

• Efficiency: typing can be used by the compiler to generatequicker code.

• Correctness: typing can be used (by the programmer or by thecompiler) to prove correctness of code.

• Documentation: types declare your intent with well-chosennames.

8

typing

A programming language is type safe to the extent that it doesnot allow programs to violate its type distinctions:

• a type-safe language doesn’t allow programmers to performoperations on mixed types

• ML and Scheme are more type-safe than Python or C

The process of verifying and enforcing the constraints of types iscalled type checking, which may occur either:

• at compile-time ⇒ static type checking, or

• at run-time ⇒ dynamic type checking.

9

typing

A programming language is type safe to the extent that it doesnot allow programs to violate its type distinctions:

• a type-safe language doesn’t allow programmers to performoperations on mixed types

• ML and Scheme are more type-safe than Python or C

The process of verifying and enforcing the constraints of types iscalled type checking, which may occur either:

• at compile-time ⇒ static type checking, or

• at run-time ⇒ dynamic type checking.

9

Page 8: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

static vs. dynamic typing

Dynamic type checking (performed at run-time):

• Slower execution: need to carry type information around, lotsof run-time checks.

• Allows for more flexible programming.

• e.g., Scheme and Python.

Static type checking (performed at compile-time):

• Faster execution: no need to carry type information; compilercan do a lot of optimization.

• Some argue that resulting programs are safer.

• Some argue that resulting programs are more elegant andmodular.

• Some argue that programmers will write horrible code to getaround a static type-checker.

• e.g., ML and Java.

10

static vs. dynamic typing

Dynamic type checking (performed at run-time):

• Slower execution: need to carry type information around, lotsof run-time checks.

• Allows for more flexible programming.

• e.g., Scheme and Python.

Static type checking (performed at compile-time):

• Faster execution: no need to carry type information; compilercan do a lot of optimization.

• Some argue that resulting programs are safer.

• Some argue that resulting programs are more elegant andmodular.

• Some argue that programmers will write horrible code to getaround a static type-checker.

• e.g., ML and Java.

10

static vs. dynamic typing

Dynamic type checking (performed at run-time):

• Slower execution: need to carry type information around, lotsof run-time checks.

• Allows for more flexible programming.

• e.g., Scheme and Python.

Static type checking (performed at compile-time):

• Faster execution: no need to carry type information; compilercan do a lot of optimization.

• Some argue that resulting programs are safer.

• Some argue that resulting programs are more elegant andmodular.

• Some argue that programmers will write horrible code to getaround a static type-checker.

• e.g., ML and Java.

10

static typing

Explicit static typing: code contains type annotations.• For example, in Java:

• Variable declarationsint x,y,z;

• Function headerspublic static void main(String[] arg)

Type inference: code does not contain explicit type annotations;types are inferred from the code.

• fun foo(x,y,z) = if x then y + z + 1.5 else 0.0;

=⇒• x must be boolean

• y, z must be reals

• the return value is real

• foo : bool * real * real -> real

11

Page 9: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

static typing

Explicit static typing: code contains type annotations.• For example, in Java:

• Variable declarationsint x,y,z;

• Function headerspublic static void main(String[] arg)

Type inference: code does not contain explicit type annotations;types are inferred from the code.

• fun foo(x,y,z) = if x then y + z + 1.5 else 0.0;

=⇒

• x must be boolean

• y, z must be reals

• the return value is real

• foo : bool * real * real -> real

11

static typing

Explicit static typing: code contains type annotations.• For example, in Java:

• Variable declarationsint x,y,z;

• Function headerspublic static void main(String[] arg)

Type inference: code does not contain explicit type annotations;types are inferred from the code.

• fun foo(x,y,z) = if x then y + z + 1.5 else 0.0;

=⇒• x must be boolean

• y, z must be reals

• the return value is real

• foo : bool * real * real -> real

11

static typing

Explicit static typing: code contains type annotations.• For example, in Java:

• Variable declarationsint x,y,z;

• Function headerspublic static void main(String[] arg)

Type inference: code does not contain explicit type annotations;types are inferred from the code.

• fun foo(x,y,z) = if x then y + z + 1.5 else 0.0;

=⇒• x must be boolean

• y, z must be reals

• the return value is real

• foo : bool * real * real -> real

11

static typing

Explicit static typing: code contains type annotations.• For example, in Java:

• Variable declarationsint x,y,z;

• Function headerspublic static void main(String[] arg)

Type inference: code does not contain explicit type annotations;types are inferred from the code.

• fun foo(x,y,z) = if x then y + z + 1.5 else 0.0;

=⇒• x must be boolean

• y, z must be reals

• the return value is real

• foo : bool * real * real -> real

11

Page 10: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

static typing

Explicit static typing: code contains type annotations.• For example, in Java:

• Variable declarationsint x,y,z;

• Function headerspublic static void main(String[] arg)

Type inference: code does not contain explicit type annotations;types are inferred from the code.

• fun foo(x,y,z) = if x then y + z + 1.5 else 0.0;

=⇒• x must be boolean

• y, z must be reals

• the return value is real

• foo : bool * real * real -> real

11

type inference

Widely regarded as an important language innovation.

ML is designed to make type inference tractable, using constraintsatisfaction techniques.

ML’s type inference algorithm [ from Mitchell 2003, pg. 136 ]:

1. assign a type to an expression and each subexpression by using theknown type of a symbol, or a type variable.

2. generate a set of constraints on types by using parse tree ofexpression.

3. solve these constraints using a substitution-based algorithm (a.k.a.unification).

12

ML data types

Basic types:

• unit : the only member is ().

• bool : booleans.

• int : integers.

• real : reals.

• string : strings.

More types:

• (�type0� ∗ �type1� ∗ . . . ∗ �typen�) : tuples.

• �type� list : lists.

• �input-type� → �output-type� : functions.

13

ML data types

Basic types:

• unit : the only member is ().

• bool : booleans.

• int : integers.

• real : reals.

• string : strings.

More types:

• (�type0� ∗ �type1� ∗ . . . ∗ �typen�) : tuples.

• �type� list : lists.

• �input-type� → �output-type� : functions.

13

Page 11: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

ML basic types

unit: this type has only one element ()

- ();

val it = () : unit

ML assigns the last evaluated value to the special variable it.Reading the above snippet of the ML interpreter session:

• evaluate (), please

• the special variable it now has the value () of type unit

14

ML basic types

bool: this type has two elements: true and false.

Operations on bools: not

Special operators: andalso, orelse.

For example:

- if (not true andalso false) orelse true

= then true

= else false;

val it true : bool

15

ML basic types

int: {... ˜2, ˜1, 0 , 1 , 2, ... }

Operations: +, −, ∗, ˜, div, mod, <, >, =, <=, >=, <>

For example:

- 5 + 6 * 2 - 3 div 2;

val it = 16 : int

- 5 mod 2 >= 6 mod 2;

val it = true : bool

16

ML basic types

int: {... ˜2, ˜1, 0 , 1 , 2, ... }Note the use of ~ for negation. Here’s why: - is a binaryoperator while ~ is a unary operator.

- -5;

stdIn:27.1 Error: expression or pattern begins

with infix identifier "-"

stdIn:27.1-27.3 Error: operator and operand don’t

agree [literal]

operator domain: ’Z * ’Z

operand: int

in expression:

- 5

- ~5;

val it = ~5 : int

17

Page 12: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

ML basic types

real: { 1.0, 3.14159, ˜11.7, .... }

Operations: +, −, ∗, /, <, >, <=, >=

Note: You cannot mix reals and integers in one expression.

Every ML expression has a type. If an expression cannot be typed,we get an error.

18

ML basic types

For example:

- 2 - 1.5;

stdIn:43.1-43.8 Error: operator and operand

don’t agree

operator domain: int * int

operand: int * real

in expression:

2 - 1.5

Understanding the error message...

Why int ? For mathematical operators, if the type is notspecified (or inferred), then type int is assumed.

19

ML basic types

Note that SML inspects the leftmost argument first. For example:

- 1.5 + 2;

stdIn:1.1-2.3 Error: operator and operand

don’t agree

operator domain: real * real

operand: real * int

in expression:

1.5 + 2

Now it expects two reals.

20

ML basic types

More examples:

- 2;

val it = 2 : int

- 2.0;

val it = 2.0 : real

- real(2);

val it = 2.0 : real

- 2.0 - 1.5;

val it = 0.5 : real

- real(2) - 1.5;

val it = 0.5 : real

21

Page 13: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

ML basic types

Note that while <, <=, >, >= are defined for all numeric types,= and <> are, for example, not defined for reals. In fact,

= : ’’a * ’’a -> bool

<> : ’’a * ’’a -> bool

In ML ’’a is a type variable used to represent a type for whichequality is defined.

With reals we can use a <= b andalso b >= a. Or, muchbetter, use real arithmetic:

- Real.==(1.0, 1.0);

val it = true : bool

- Real.==(1.0, 1.00000);

val it = true : bool

22

ML basic types

string: { “Hello, world”, “CSC324 is fun!”, ... }

Operations: ^ for concatenation

For example:

- "Hello";

val it = "Hello" : string

- "Hello" ^ " " ^ "Jim";

val it = "Hello Jim" : string

23

ML basic data types: summary

• unit : the only member is ().Note: unit is basically an empty tuple.

• bool : booleans.Operations: not, andalso, orelse, ...Note: and is not a logical operator, it has a special meaningin ML.

• int : integers.Operations: all arithmetic and comparison operators.

• real : reals.Operations: all arithmetic and comparison, except = and <>.Note: use Real.== for equality.

• string : strings.Operations: ^ for concatenation

24

ML types: tuples

A tuple packs together several types.

- ("foo", "bar");

val it = ("foo","bar") : string * string

- ("foo", "bar", 123);

val it = ("foo","bar",123) : string * string * int

- ("foo", (123, 456));

val it = ("foo",(123,456)) : string * (int * int)

Operations: accessing component N of a tuple t is written #N t.

- #2 ("foo", 123, 3.14);

val it = 123 : int

25

Page 14: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

ML types: lists

In ML all elements in a list must have the same type.

- [1,2,3,4];

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

- ["cscc24","is","fun"];

val it = ["cscc24","is","fun"] : string list

- [("foo",1.0),("bar",3.14)];

val it = [("foo",1.0),("bar",3.14)] : (string * real) list

[ ] (or nil) is the empty list.

Constructor: ::

Selectors: hd, tl

More operations: @, null, length, map, foldr, ...

26

ML types: lists

Some examples:

- "foo"::["bar","foobar"];

val it = ["foo","bar","foobar"] : string list

- [1,2]@[3,4];

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

- hd [1,2];

val it = 1 : int

- tl [1,2];

val it = [2] : int list

27

ML syntax

A variable declaration in ML looks like:

• val �name� = �expr�

• val x = 42 + 24

• in Scheme: (define x (+ 42 24))

28

ML syntax

A variable declaration in ML looks like:

• val �name� = �expr�• val x = 42 + 24

• in Scheme: (define x (+ 42 24))

28

Page 15: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

ML syntax

A variable declaration in ML looks like:

• val �name� = �expr�• val x = 42 + 24

• in Scheme: (define x (+ 42 24))

28

ML functions

The syntax for anonymous functions in ML is:

• fn �arg� => �body�

• fn x => x + 1

• in Scheme: (lambda (x) (+ x 1))

Giving a name to a function:

• val �name� = fn �arg� => �body�• val inc = fn x => x + 1

• in Scheme: (define inc (lambda (x) (+ x 1)))

Or:

• fun �name� �arg� = �body�• fun inc x = x + 1

• in Scheme: (define (inc x) (+ x 1))

In ML every function accepts exactly one argument.This argumentcould be a tuple.

29

ML functions

The syntax for anonymous functions in ML is:

• fn �arg� => �body�• fn x => x + 1

• in Scheme: (lambda (x) (+ x 1))

Giving a name to a function:

• val �name� = fn �arg� => �body�• val inc = fn x => x + 1

• in Scheme: (define inc (lambda (x) (+ x 1)))

Or:

• fun �name� �arg� = �body�• fun inc x = x + 1

• in Scheme: (define (inc x) (+ x 1))

In ML every function accepts exactly one argument.This argumentcould be a tuple.

29

ML functions

The syntax for anonymous functions in ML is:

• fn �arg� => �body�• fn x => x + 1

• in Scheme: (lambda (x) (+ x 1))

Giving a name to a function:

• val �name� = fn �arg� => �body�• val inc = fn x => x + 1

• in Scheme: (define inc (lambda (x) (+ x 1)))

Or:

• fun �name� �arg� = �body�• fun inc x = x + 1

• in Scheme: (define (inc x) (+ x 1))

In ML every function accepts exactly one argument.This argumentcould be a tuple.

29

Page 16: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

ML functions

The syntax for anonymous functions in ML is:

• fn �arg� => �body�• fn x => x + 1

• in Scheme: (lambda (x) (+ x 1))

Giving a name to a function:

• val �name� = fn �arg� => �body�• val inc = fn x => x + 1

• in Scheme: (define inc (lambda (x) (+ x 1)))

Or:

• fun �name� �arg� = �body�• fun inc x = x + 1

• in Scheme: (define (inc x) (+ x 1))

In ML every function accepts exactly one argument.This argumentcould be a tuple.

29

ML functions

The syntax for anonymous functions in ML is:

• fn �arg� => �body�• fn x => x + 1

• in Scheme: (lambda (x) (+ x 1))

Giving a name to a function:

• val �name� = fn �arg� => �body�

• val inc = fn x => x + 1

• in Scheme: (define inc (lambda (x) (+ x 1)))

Or:

• fun �name� �arg� = �body�• fun inc x = x + 1

• in Scheme: (define (inc x) (+ x 1))

In ML every function accepts exactly one argument.This argumentcould be a tuple.

29

ML functions

The syntax for anonymous functions in ML is:

• fn �arg� => �body�• fn x => x + 1

• in Scheme: (lambda (x) (+ x 1))

Giving a name to a function:

• val �name� = fn �arg� => �body�• val inc = fn x => x + 1

• in Scheme: (define inc (lambda (x) (+ x 1)))

Or:

• fun �name� �arg� = �body�• fun inc x = x + 1

• in Scheme: (define (inc x) (+ x 1))

In ML every function accepts exactly one argument.This argumentcould be a tuple.

29

ML functions

The syntax for anonymous functions in ML is:

• fn �arg� => �body�• fn x => x + 1

• in Scheme: (lambda (x) (+ x 1))

Giving a name to a function:

• val �name� = fn �arg� => �body�• val inc = fn x => x + 1

• in Scheme: (define inc (lambda (x) (+ x 1)))

Or:

• fun �name� �arg� = �body�• fun inc x = x + 1

• in Scheme: (define (inc x) (+ x 1))

In ML every function accepts exactly one argument.This argumentcould be a tuple.

29

Page 17: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

ML functions

The syntax for anonymous functions in ML is:

• fn �arg� => �body�• fn x => x + 1

• in Scheme: (lambda (x) (+ x 1))

Giving a name to a function:

• val �name� = fn �arg� => �body�• val inc = fn x => x + 1

• in Scheme: (define inc (lambda (x) (+ x 1)))

Or:

• fun �name� �arg� = �body�• fun inc x = x + 1

• in Scheme: (define (inc x) (+ x 1))

In ML every function accepts exactly one argument.This argumentcould be a tuple.

29

ML functions

The syntax for anonymous functions in ML is:

• fn �arg� => �body�• fn x => x + 1

• in Scheme: (lambda (x) (+ x 1))

Giving a name to a function:

• val �name� = fn �arg� => �body�• val inc = fn x => x + 1

• in Scheme: (define inc (lambda (x) (+ x 1)))

Or:

• fun �name� �arg� = �body�

• fun inc x = x + 1

• in Scheme: (define (inc x) (+ x 1))

In ML every function accepts exactly one argument.This argumentcould be a tuple.

29

ML functions

The syntax for anonymous functions in ML is:

• fn �arg� => �body�• fn x => x + 1

• in Scheme: (lambda (x) (+ x 1))

Giving a name to a function:

• val �name� = fn �arg� => �body�• val inc = fn x => x + 1

• in Scheme: (define inc (lambda (x) (+ x 1)))

Or:

• fun �name� �arg� = �body�• fun inc x = x + 1

• in Scheme: (define (inc x) (+ x 1))

In ML every function accepts exactly one argument.This argumentcould be a tuple.

29

ML functions

The syntax for anonymous functions in ML is:

• fn �arg� => �body�• fn x => x + 1

• in Scheme: (lambda (x) (+ x 1))

Giving a name to a function:

• val �name� = fn �arg� => �body�• val inc = fn x => x + 1

• in Scheme: (define inc (lambda (x) (+ x 1)))

Or:

• fun �name� �arg� = �body�• fun inc x = x + 1

• in Scheme: (define (inc x) (+ x 1))

In ML every function accepts exactly one argument.This argumentcould be a tuple.

29

Page 18: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

ML functions

The syntax for anonymous functions in ML is:

• fn �arg� => �body�• fn x => x + 1

• in Scheme: (lambda (x) (+ x 1))

Giving a name to a function:

• val �name� = fn �arg� => �body�• val inc = fn x => x + 1

• in Scheme: (define inc (lambda (x) (+ x 1)))

Or:

• fun �name� �arg� = �body�• fun inc x = x + 1

• in Scheme: (define (inc x) (+ x 1))

In ML every function accepts exactly one argument.This argumentcould be a tuple.

29

ML functions

The syntax for anonymous functions in ML is:

• fn �arg� => �body�• fn x => x + 1

• in Scheme: (lambda (x) (+ x 1))

Giving a name to a function:

• val �name� = fn �arg� => �body�• val inc = fn x => x + 1

• in Scheme: (define inc (lambda (x) (+ x 1)))

Or:

• fun �name� �arg� = �body�• fun inc x = x + 1

• in Scheme: (define (inc x) (+ x 1))

In ML every function accepts exactly one argument.

This argumentcould be a tuple.

29

ML functions

The syntax for anonymous functions in ML is:

• fn �arg� => �body�• fn x => x + 1

• in Scheme: (lambda (x) (+ x 1))

Giving a name to a function:

• val �name� = fn �arg� => �body�• val inc = fn x => x + 1

• in Scheme: (define inc (lambda (x) (+ x 1)))

Or:

• fun �name� �arg� = �body�• fun inc x = x + 1

• in Scheme: (define (inc x) (+ x 1))

In ML every function accepts exactly one argument.This argumentcould be a tuple.

29

ML types

The type of a function is determined by the type of the input andthe type of the output.

Some examples:

- fun inc x = x + 1;

val inc = fn : int -> int

- fun addInc(x,y) = x + y + 1;

val addInc = fn : int * int -> int

- fun optInc(x,y,c) = if c then x + y + 1 else x + y;

val optInc = fn : int * int * bool -> int

30

Page 19: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

parametric polymorphism

What is the type of identity function fn x => x ?

- fun id x = x;

val id = fn : ??

- id 324;

val it = 324 : int

- id 3.14;

val it = 3.14 : real

- id "foo";

val it = "foo" : string

- id [1,2,3];

val it = [1,2,3] : int list

- id (fn x => x + 1);

val it = fn : int -> int

31

parametric polymorphism

What is the type of identity function fn x => x ?

- fun id x = x;

val id = fn : ??

- id 324;

val it = 324 : int

- id 3.14;

val it = 3.14 : real

- id "foo";

val it = "foo" : string

- id [1,2,3];

val it = [1,2,3] : int list

- id (fn x => x + 1);

val it = fn : int -> int

31

parametric polymorphism

- fun id x = x;

val id = fn : ’a -> ’a

The ’a in ML stands for α. It is a type variable.

id is a polymorphic function.

α → α means “for every valid type α, α → α”∀α · α → α

When id is applied to 324 , the type variable α is instantiatedto int .

32

parametric polymorphism

- fun id x = x;

val id = fn : ’a -> ’a

The ’a in ML stands for α. It is a type variable.

id is a polymorphic function.

α → α means “for every valid type α, α → α”∀α · α → α

When id is applied to 324 , the type variable α is instantiatedto int .

32

Page 20: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

parametric polymorphism

- fun id x = x;

val id = fn : ’a -> ’a

The ’a in ML stands for α. It is a type variable.

id is a polymorphic function.

α → α means “for every valid type α, α → α”∀α · α → α

When id is applied to 324 , the type variable α is instantiatedto int .

32

parametric polymorphism

- fun id x = x;

val id = fn : ’a -> ’a

The ’a in ML stands for α. It is a type variable.

id is a polymorphic function.

α → α means “for every valid type α, α → α”∀α · α → α

When id is applied to 324 , the type variable α is instantiatedto int .

32

... admin notes ...

• A1 marks and reports have been emailed to your CDF account.Please send me and the TA (Jackie) an email if you have notreceived your report.

• Message from The Accessibility Services Note-TakingProgram: The Accessibility Services requires dependable volunteernote-takers to provide notes to students living with disabilities toachieve academic success.

To find out more information on how to register, please contact me,or see “Announcements” on the course webpage.

33

parametric polymorphism

Examples:

fun choose (a,b,c) = if a then b else c;

choose : bool * ’a * ’a -> ’a;

fun swap (x,y) = (y,x);

swap : ’a * ’b -> ’b * ’a;

34

Page 21: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

parametric polymorphism

Examples:

fun choose (a,b,c) = if a then b else c;

choose : bool * ’a * ’a -> ’a;

fun swap (x,y) = (y,x);

swap : ’a * ’b -> ’b * ’a;

34

parametric polymorphism

Examples:

fun choose (a,b,c) = if a then b else c;

choose : bool * ’a * ’a -> ’a;

fun swap (x,y) = (y,x);

swap : ’a * ’b -> ’b * ’a;

34

parametric polymorphism

Examples:

fun choose (a,b,c) = if a then b else c;

choose : bool * ’a * ’a -> ’a;

fun swap (x,y) = (y,x);

swap : ’a * ’b -> ’b * ’a;

34

parametric polymorphism

What is the type of the following function?

fun len lst = if (null lst) then 0

else 1 + len (tl lst);

len : ’a list -> int

List is a polymorphic data type.

35

Page 22: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

parametric polymorphism

What is the type of the following function?

fun len lst = if (null lst) then 0

else 1 + len (tl lst);

len : ’a list -> int

List is a polymorphic data type.

35

parametric polymorphism

What is the type of the following function?

fun len lst = if (null lst) then 0

else 1 + len (tl lst);

len : ’a list -> int

List is a polymorphic data type.

35

currying

- fun sum x y = x + y;

is short form for:

- fun sum x = (fn y => x + y)

So its type is:

val sum = fn: int -> int -> int

Note: this means int -> (int -> int), i.e. sum takes an int

and returns a function of type int -> int.

36

currying

- fun sum x y = x + y;

val sum = fn: int -> int -> int

Example calls to function sum:

- sum 2;

?

- sum 2 3;

?

37

Page 23: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

currying

- fun sum x y = x + y;

val sum = fn: int -> int -> int

Example calls to function sum:

- sum 2;

val it = fn : int -> int

- sum 2 3;

val it = 5 : int

38

important: fn versus fun

Keywords fun and fn are both short for “function”, but they havedifferent meanings:

• fun is used for function declaration, i.e., to bind a particularidentifier to a function.

• fn is used to introduce a value that has a function type.

Remember syntax for anonymous and named functions.

39

more on currying

Compare sum1 and sum2:

- fun sum1 x y = x + y;

val sum1 = fn: int -> int -> int

- fun sum2(x,y) = x + y;

val sum2 = fn: int * int -> int

Are sum1 and sum2 different in what they do?

40

more on currying

Compare sum1 and sum2:

- fun sum1 x y = x + y;

val sum1 = fn: int -> int -> int

- fun sum2(x,y) = x + y;

val sum2 = fn: int * int -> int

Are sum1 and sum2 different in what they do? Compare:

- sum1 2;

?

- sum2 2;

?

41

Page 24: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

currying

With anonymous functions:

- fn x => fn y => x + y;

val it = fn : int -> int -> int

- (fn x => fn y => x + y) 2;

val it = fn : int -> int

- (fn x => fn y => x + y) 2 3;

val it = 5 : int

• Curried functions allow the use of partially instantiatedfunctions, i.e., forming new functions by binding one or moreof parameters of an existing function.

42

pattern matching

Value declaration (general form):

val <pat> = <exp>

- val myTuple = ("foo", "bar");

val myTuple = ("foo","bar") : string * string

- val (x,y) = myTuple;

val x = "foo" : string

val y = "bar" : string

- val myList = [1,2,3,4];

val myList = [1,2,3,4] : int list

- val h::r = myList;

val h = 1 : int

val r = [2,3,4] : int list

43

pattern matching

“ ” is “don’t care”: matches everything, binds nothing

- val myTuple = ("foo", "bar");

val myTuple = ("foo","bar") : string * string

- val (first, _) = myTuple;

val first = "foo" : string

- val h::_ = [1,2,3];

val h = 1 : int

44

pattern matching

Matching a pattern to an expression, e.g.:

val (a, f::s::r) = ("hello", [1,2,3,4]);

Parse trees for the pattern and the expression:

, ,

/ \ / \

a :: "hello" ::

/ \ / \

f :: 1 ::

/ \ / \

s r 2 ::

/ \

3 ::

/ \

4 nil

45

Page 25: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

pattern matching

Function declaration with pattern matching:

fun <name> <pattern1> = <exp1>

| <name> <pattern2> = <exp2>

.

.

| <name> <patternN> = <expN>;

This means:

• function name is name,

• function takes one argument (like any other ML function),

• function tries to match the argument to pattern1. If itsucceeds, it returns value of exp1. Otherwise, tries to matchthe argument to pattern2, etc, etc.

46

pattern matching

Let’s define rev that takes a list and returns its reverse:

- fun rev lst =

= if null(lst) then []

= else rev(tl lst) @ [hd lst];

val rev = fn : ’a list -> ’a list

47

pattern matching

Let’s define rev that takes a list and returns its reverse:

- fun rev lst =

= if null(lst) then []

= else rev(tl lst) @ [hd lst];

val rev = fn : ’a list -> ’a list

Now we want to define rev using pattern matching:

48

pattern matching

Let’s define rev that takes a list and returns its reverse:

- fun rev lst =

= if null(lst) then []

= else rev(tl lst) @ [hd lst];

val rev = fn : ’a list -> ’a list

Now we want to define rev using pattern matching:

- fun rev nil = []

= | rev(x::xs) = rev(xs) @ [x];

val rev = fn : ’a list -> ’a list

49

Page 26: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

pattern matching

Recall the function len that takes a list and returns its length:

fun len lst =

if (null lst) then 0

else 1 + len (tl lst);

50

pattern matching

Recall the function len that takes a list and returns its length:

fun len lst =

if (null lst) then 0

else 1 + len (tl lst);

We can (and should!) rewrite len to use pattern matching:

fun len [] = 0

| len (x::xs) = 1 + len xs;

len : ’a list -> int

51

pattern matching

Non-exhaustive patterns:

- fun len (x::xs) = 1 + len xs;

stdIn ... Warning: match nonexhaustive

x :: xs => ...

val len = fn : ’a list -> int

- len [1,2,3];

uncaught exception nonexhaustive match failure

raised at: ...

52

pattern matching

Write a function firstlist that takes a list of pairs, and returnsa new list consisting of the first elements of all pairs. For example:

firstlist [] ==> []

firstlist [(1,2),(1,3)] ==> [1,1]

firstlist [(1,"a"),(2,"b"),(3,"c")] ==> [1,2,3]

firstlist [([],"a"),([1],"b"),([1,2],"c")] ==>

[[],[1],[1,2]]

53

Page 27: CSC324 Functional Programming, Typing in MLafsaneh/csc324w13/mlI-4up.pdfFunctional Programming, Typing in ML Afsaneh Fazly1 January 30, 2013 1with many thanks to Anya Tafliovich,

pattern matching

Write a function firstlist that takes a list of pairs, and returnsa new list consisting of the first elements of all pairs. For example:

firstlist [] ==> []

firstlist [(1,2),(1,3)] ==> [1,1]

firstlist [(1,"a"),(2,"b"),(3,"c")] ==> [1,2,3]

firstlist [([],"a"),([1],"b"),([1,2],"c")] ==>

[[],[1],[1,2]]

fun firstlist [] = []

| firstlist ((e1,e2)::es) = e1::(firstlist es);

firstlist : (’a * ’b) list -> ’a list

54

pattern matching

fun <name> <pattern1> = <exp1>

| <name> <pattern2> = <exp2>

.

.

| <name> <patternN> = <expN>;

Notes:

• All patterns must have the same type.

• All expressions must have the same type.

Why?

55

ML notes

Syntax of an ML let expression:

let

<declaration>

<declaration>

...

in

<body-expression>

end

56

ML notes

Example:

fun reverse lst =

let

fun reverseAcc [] acc = acc

| reverseAcc (x::xs) acc =

reverseAcc xs (x::acc)

in

reverseAcc lst []

end;

reverse : ’a list -> ’a list

- reverse [1,2,3];

val it = [3,2,1] : int list

57