CSCC24 Functional Programming Typing, Scope, Exceptions ML

Similar documents
CSC324 Functional Programming Typing, Exceptions in ML

CSC324- TUTORIAL 5. Shems Saleh* *Some slides inspired by/based on Afsaneh Fazly s slides

Types and Type Inference

A First Look at ML. Chapter Five Modern Programming Languages, 2nd ed. 1

Types and Type Inference

Haske k ll An introduction to Functional functional programming using Haskell Purely Lazy Example: QuickSort in Java Example: QuickSort in Haskell

Processadors de Llenguatge II. Functional Paradigm. Pratt A.7 Robert Harper s SML tutorial (Sec II)

CSCC24 Functional Programming Scheme Part 2

An introduction introduction to functional functional programming programming using usin Haskell

OCaml. ML Flow. Complex types: Lists. Complex types: Lists. The PL for the discerning hacker. All elements must have same type.

CSCI-GA Scripting Languages

SML A F unctional Functional Language Language Lecture 19

CS 11 Haskell track: lecture 1

So what does studying PL buy me?

Mini-ML. CS 502 Lecture 2 8/28/08

Handout 2 August 25, 2008

CSE 505. Lecture #9. October 1, Lambda Calculus. Recursion and Fixed-points. Typed Lambda Calculi. Least Fixed Point

Lists. Michael P. Fourman. February 2, 2010

COP4020 Programming Languages. Functional Programming Prof. Robert van Engelen

Plan (next 4 weeks) 1. Fast forward. 2. Rewind. 3. Slow motion. Rapid introduction to what s in OCaml. Go over the pieces individually

Types, Type Inference and Unification

Exercises on ML. Programming Languages. Chanseok Oh

CSE 341 Section 5. Winter 2018

News. Programming Languages. Complex types: Lists. Recap: ML s Holy Trinity. CSE 130: Spring 2012

Summer 2017 Discussion 10: July 25, Introduction. 2 Primitives and Define

Typed Racket: Racket with Static Types

Introduction to ML. Based on materials by Vitaly Shmatikov. General-purpose, non-c-like, non-oo language. Related languages: Haskell, Ocaml, F#,

A Brief Introduction to Standard ML

COSE212: Programming Languages. Lecture 3 Functional Programming in OCaml

CPS 506 Comparative Programming Languages. Programming Language Paradigm

Introduction to Typed Racket. The plan: Racket Crash Course Typed Racket and PL Racket Differences with the text Some PL Racket Examples

OCaml Data CMSC 330: Organization of Programming Languages. User Defined Types. Variation: Shapes in Java

The Typed Racket Guide

Programming Languages

The type checker will complain that the two branches have different types, one is string and the other is int

Introduction to OCaml

A Third Look At ML. Chapter Nine Modern Programming Languages, 2nd ed. 1

cs242 Kathleen Fisher Reading: Concepts in Programming Languages, Chapter 6 Thanks to John Mitchell for some of these slides.

LECTURE 16. Functional Programming

OCaml. History, Variants. ML s holy trinity. Interacting with ML. Base type: Integers. Base type: Strings. *Notes from Sorin Lerner at UCSD*

SCHEME 8. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. March 23, 2017

Data Types The ML Type System

Scheme as implemented by Racket

Functional Programming

Chapter 15. Functional Programming Languages

Design Issues. Subroutines and Control Abstraction. Subroutines and Control Abstraction. CSC 4101: Programming Languages 1. Textbook, Chapter 8

Lecture 2: The Basis of SML

Programming Languages

Note that pcall can be implemented using futures. That is, instead of. we can use

Programming Languages

CS558 Programming Languages

G Programming Languages - Fall 2012

Programming Languages

Datatype declarations

Introduction to ML. Mooly Sagiv. Cornell CS 3110 Data Structures and Functional Programming

CSE 3302 Programming Languages Lecture 8: Functional Programming

Begin at the beginning

Inductive Data Types

Functional Programming. Big Picture. Design of Programming Languages

Type Checking and Type Inference

F28PL1 Programming Languages. Lecture 11: Standard ML 1

CSE 130 Programming Languages. Lecture 3: Datatypes. Ranjit Jhala UC San Diego

CSC 533: Organization of Programming Languages. Spring 2005

Polymorphism and Type Inference

Introduction to ML. Mooly Sagiv. Cornell CS 3110 Data Structures and Functional Programming

Chapter 11 :: Functional Languages

Functional Programming. Pure Functional Programming

SCHEME 7. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. October 29, 2015

A Second Look At ML. Chapter Seven Modern Programming Languages, 2nd ed. 1

Cunning Plan. One-Slide Summary. Functional Programming. Functional Programming. Introduction to COOL #1. Classroom Object-Oriented Language

CPL 2016, week 10. Clojure functional core. Oleg Batrashev. April 11, Institute of Computer Science, Tartu, Estonia

Functional Programming Languages (FPL)

PROGRAMMING IN HASKELL. Chapter 2 - First Steps

Any questions. Say hello to OCaml. Say hello to OCaml. Why readability matters. History, Variants. Plan (next 4 weeks)

n n Try tutorial on front page to get started! n spring13/ n Stack Overflow!

Background. CMSC 330: Organization of Programming Languages. Useful Information on OCaml language. Dialects of ML. ML (Meta Language) Standard ML

SCHEME AND CALCULATOR 5b

Typed Scheme: Scheme with Static Types

Semantic Analysis. Outline. The role of semantic analysis in a compiler. Scope. Types. Where we are. The Compiler Front-End

Standard ML. Data types. ML Datatypes.1

CSE341: Programming Languages Lecture 9 Function-Closure Idioms. Dan Grossman Winter 2013

CS558 Programming Languages

Recap from last time. Programming Languages. CSE 130 : Fall Lecture 3: Data Types. Put it together: a filter function

Chapter 3 Linear Structures: Lists

Recap: ML s Holy Trinity

Some Advanced ML Features

CS558 Programming Languages

Functional Programming and Haskell

Announcements. CSCI 334: Principles of Programming Languages. Lecture 5: Fundamentals III & ML

Lecture 12: Data Types (and Some Leftover ML)

Closures. Mooly Sagiv. Michael Clarkson, Cornell CS 3110 Data Structures and Functional Programming

Lexical Considerations

A Fourth Look At ML. Chapter Eleven Modern Programming Languages, 2nd ed. 1

1 Lexical Considerations

CS321 Languages and Compiler Design I Winter 2012 Lecture 13

Functional Languages. CSE 307 Principles of Programming Languages Stony Brook University

Programming Paradigms

Chapter 3 Linear Structures: Lists

CS 320: Concepts of Programming Languages

CMSC 330, Fall 2013, Practice Problem 3 Solutions

Transcription:

CSCC24 Functional Programming Typing, Scope, Exceptions ML Carolyn MacLeod 1 winter 2012 1 Based on slides by Anya Tafliovich, with many thanks to Gerald Penn and Sheila McIlraith.

motivation Consider the following Scheme function definition: (define foobar (lambda (x) (if (even? x) (first x) (rest x)))) Anything wrong?

motivation Consider the following Scheme function definition: (define foobar (lambda (x) (if (even? x) (first x) (rest x)))) Anything wrong? even? expects a number and first, rest expect a list = we ll get an error at run-time.

motivation Consider the following Scheme function definition: (define foobar (lambda (x) (if (even? x) (first x) (rest x)))) Anything wrong? even? expects a number and first, rest expect a list = we ll get an error at run-time. We are able to figure it out before run-time. Can t a PL support this kind of reasoning?

What is the property of Scheme that is related to this property

3 What is the property of Scheme that is related to this property Dynamic typing

3 What is the property of Scheme that is related to this property Dynamic typing What other language do you know with dynamic typing?

3 What is the property of Scheme that is related to this property Dynamic typing What other language do you know with dynamic typing? Python

It could be worse! motivation

It could be worse! motivation At least Scheme checks that x is a list (cons pair) before accessing memory in the execution of (rest x).

It could be worse! motivation At least Scheme checks that x is a list (cons pair) before accessing memory in the execution of (rest x). In fact, Scheme is type safe: it will never execute if op is not applicable to arg. (op arg)

It could be worse! motivation At least Scheme checks that x is a list (cons pair) before accessing memory in the execution of (rest x). In fact, Scheme is type safe: it will never execute if op is not applicable to arg. (op arg) What s a language you know that is not type safe?

It could be worse! motivation At least Scheme checks that x is a list (cons pair) before accessing memory in the execution of (rest x). In fact, Scheme is type safe: it will never execute if op is not applicable to arg. (op arg) What s a language you know that is not type safe? C is one you most likely know.

It could be worse! motivation At least Scheme checks that x is a list (cons pair) before accessing memory in the execution of (rest x). In fact, Scheme is type safe: it will never execute if op is not applicable to arg. (op arg) What s a language you know that is not type safe? C is one you most likely know. Languages which are not type safe (e.g., C) allow unsafe memory accesses: a major source of security vulnerabilities.

5 motivation Want to catch all errors at compile-time.

5 motivation Want to catch all errors at compile-time. Extremely difficult, if not impossible.

5 motivation Want to catch all errors at compile-time. Extremely difficult, if not impossible. Identify a certain class of errors and guarantee to catch all of them.

5 motivation Want to catch all errors at compile-time. Extremely difficult, if not impossible. Identify a certain class of errors and guarantee to catch all of them. Catch as many errors as possible?

5 motivation Want to catch all errors at compile-time. Extremely difficult, if not impossible. Identify a certain class of errors and guarantee to catch all of them. Catch as many errors as possible? Costly: programmers expertise, computational resources, etc.

5 motivation Want to catch all errors at compile-time. Extremely difficult, if not impossible. Identify a certain class of errors and guarantee to catch all of them. Catch as many errors as possible? Costly: programmers expertise, computational resources, etc. Slower development, less expressive languages.

5 motivation Want to catch all errors at compile-time. Extremely difficult, if not impossible. Identify a certain class of errors and guarantee to catch all of them. Catch as many errors as possible? Costly: programmers expertise, computational resources, etc. Slower development, less expressive languages. Used in development of safety-critical systems.

5 motivation Want to catch all errors at compile-time. Extremely difficult, if not impossible. Identify a certain class of errors and guarantee to catch all of them. 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: type systems.

5 motivation Want to catch all errors at compile-time. Extremely difficult, if not impossible. Identify a certain class of errors and guarantee to catch all of them. 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: type systems. Guarantees range widely: from basic safety of memory accesses to just about anything you want.

A type is: typing A name for a set of values and some operations which can be performed on that set of values. A collection of computational entities that share some common property.

7 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 and return booleans as output.

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 and return booleans as output. What constitutes a type is language dependent.

Benefits of having a type system: typing Easier to debug programs: compiler can catch many errors. Static analysis: a lot of useful information about the program can be obtained at compile-time. Efficiency: typing can be used by the compiler to generate quicker code. Correctness: typing can be used (by the programmer or by the compiler) to prove correctness of code. Documentation: types declare your intent with well-chosen names.

Recall - last lecture We saw this Scheme function definition: (define foobar (lambda (x) (if (even? x) (first x) (rest x)))) What is wrong with the code?

Recall - last lecture We saw this Scheme function definition: (define foobar (lambda (x) (if (even? x) (first x) (rest x)))) What is wrong with the code? What programming language feature would stop us creating programs with this kind of bugs?

10 typing A programming language is type safe if no program is allowed to violate its type distinctions. The process of verifying and enforcing the constraints of types is called type checking. Type checking can either occur at compile-time (static type checking) or at run-time (dynamic type checking).

11 Dynamic type checking: static vs dynamic typing Static type checking:

static vs dynamic typing Dynamic type checking: Performed at run-time. Slower execution: need to carry type information around, lots of run-time checks. More flexible. Easier refactoring. Static type checking: Faster execution. Compiler can do a lot of optimization. Some argue that resulting programs are safer. Some argue that resulting programs are more elegant and modular. Some argue that programmers will write horrible code to get around a static type-checker. 11

12 static typing Explicit static typing: code contains type annotations. For example, in Java: Variable declarations int x,y,z; Function headers public static void main(string[] arg)

static typing Explicit static typing: code contains type annotations. For example, in Java: Variable declarations int x,y,z; Function headers public static void main(string[] arg) Type inference: infer all types from the code that does not contain explicit type annotations. fun foo(x,y,z) = if x then y + z + 1.5 else 0.0;

static typing Explicit static typing: code contains type annotations. For example, in Java: Variable declarations int x,y,z; Function headers public static void main(string[] arg) Type inference: infer all types from the code that does not contain explicit type annotations. fun foo(x,y,z) = if x then y + z + 1.5 else 0.0; x must be boolean

static typing Explicit static typing: code contains type annotations. For example, in Java: Variable declarations int x,y,z; Function headers public static void main(string[] arg) Type inference: infer all types from the code that does not contain explicit type annotations. fun foo(x,y,z) = if x then y + z + 1.5 else 0.0; x y, z must be boolean must be reals

static typing Explicit static typing: code contains type annotations. For example, in Java: Variable declarations int x,y,z; Function headers public static void main(string[] arg) Type inference: infer all types from the code that does not contain explicit type annotations. fun foo(x,y,z) = if x then y + z + 1.5 else 0.0; x y, z must be boolean must be reals the return value is real

static typing Explicit static typing: code contains type annotations. For example, in Java: Variable declarations int x,y,z; Function headers public static void main(string[] arg) Type inference: infer all types from the code that does not contain explicit type annotations. fun foo(x,y,z) = if x then y + z + 1.5 else 0.0; x y, z must be boolean must be reals the return value is real foo : bool * real * real -> real

Features of Standard ML What is ML? ML is an ALGOL family language, a child of Pascal and Lisp, and a distant cousin of C ML was designed as the Meta- Language of the Logic for Computable Functions (LCF) System. Its original purpose was for writing programs that would attempt to construct mathematical proofs. Question - What have we seen that is similar? ML is a functional language, although ML can be written in a more imperative style. Arguably, you can call it a mostly functional language, or a function-oriented imperative language.

14 Features of Standard ML Modular:Supports modules and interfaces, determining what components and types from the module are visible outside. Strict Pass By Value: The arguments of the function are evaluated before the beginning of the function being evaluated. Polymorphic: Supports both data type and function polymorphism. Type System: The ML type system is considered one of the cleanest in any language Static typing Type inference Type safe Functions are first class values: Functions can be defined, passed as arguments, and returned as function results. Garbage Collection Exception Handling

A few examples Standard ML

ML Examples (* This is a comment in SML *) fun factorial (n:int):int = if n = 0 then 1 else n * factorial(n-1) Notice, we use = both for comparison, and for the function definition. We use an infix notation, unlike in Scheme. In this example, we have explicit static typing.

ML Examples Another form of writing function definitions: (* Our first fibonacci solution in Scheme, translated to ML *) fun fib 1 = 1 fib 2 = 1 fib (n) = fib(n-1) + fib(n-2) This is also an example of type inference - notice that this version does not specify types for the input and output of fib, however, when compiled or entered into the interpreter, it gives a type signature of val fib = fn : int -> int

18 type inference Trade-off: Want the language to be expressive. Want tractable type inference. ML is designed to make type inference tractable. Widely regarded as an important language innovation.

19 ML data types Basic types: unit : the only member is (). bool : booleans. int : integers. real : reals. string : strings.

19 ML data types Basic types: unit : the only member is (). bool : booleans. int : integers. real : reals. string : strings. More types: ( type 0 type 1... type n ) : tuples. type list : lists. input-type output-type : functions.

20 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

ML basic types bool: this type has two elements: true and false. Operations on bools: not, tostring,... Special operators: andalso, orelse. For example: - if (not true andalso false) orelse true then true else false; val it true : bool

ML basic types The structure of lists in ML is the same as in Scheme and Prolog. Strings must have elements of all the same type. - hd [1,2,3]; val it = 1 : int - tl [1,2,3]; val it = [2,3] : int list - "a" :: ["b","c"]; val it = ["a","b","c"] : string list - [1,2]@[3]; val it = [1,2,3] : int list These are the equivalents of first/car, rest/cdr, and cons in Scheme. The @ operator is used like append in Scheme.

ML basic types fun elem_to_list [] = [] elem_to_list (h::t) = [h] :: (elem_to_list t)

24 int: {... 2, 1, 0, 1, 2,... } ML basic types 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

ML basic types int: {... 2, 1, 0, 1, 2,... } Note the use of ~ for negation. Here s why: - is a binary operator 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

real: { 1.0, 3.14159, 11.7,... } ML basic types 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.

For example: ML basic types - 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 not specified (or inferred), then type int is assumed.

More examples: ML basic types - 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

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.

30 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 means the type for which equality is defined. With reals we can use a <= b andalso b >= a. Or, much better, use real arithmetic: - Real.==(1.0, 1.0); val it = true : bool - Real.==(1.0, 1.00000); val it = true : bool

31 ML basic types string: { Hello, world, CSCC24 is fun!,... } Operations: ^ for concatenation For example: - "Hello"; val it = "Hello" : string - "Hello" ^ " " ^ "Jim"; val it = "Hello Jim" : string

32 ML data types Basic types: unit : the only member is (). bool : booleans. int : integers. real : reals. string : strings.

32 ML data types Basic types: unit : the only member is (). bool : booleans. int : integers. real : reals. string : strings. More types: ( type 0 type 1... type n ) : tuples. type list : lists. input-type output-type : functions.

ML types 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

ML types 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,... 34

Some examples: ML types - "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

36 ML syntax A variable declaration in ML looks like: val name = expr

36 ML syntax A variable declaration in ML looks like: val name = expr val x = 42 + 24

36 ML syntax A variable declaration in ML looks like: val name = expr val x = 42 + 24 (define x (+ 42 24))

37 ML functions The syntax for anonymous functions in ML is: fn arg => body

37 ML functions The syntax for anonymous functions in ML is: fn arg => body fn x => x + 1

37 ML functions The syntax for anonymous functions in ML is: fn arg => body fn x => x + 1 (lambda (x) (+ x 1))

37 ML functions The syntax for anonymous functions in ML is: fn arg => body fn x => x + 1 (lambda (x) (+ x 1))

ML functions The syntax for anonymous functions in ML is: fn arg => body fn x => x + 1 (lambda (x) (+ x 1)) Giving a name to a function: val name = fn arg => body

ML functions The syntax for anonymous functions in ML is: fn arg => body fn x => x + 1 (lambda (x) (+ x 1)) Giving a name to a function: val name = fn arg => body val inc = fn x => x + 1

ML functions The syntax for anonymous functions in ML is: fn arg => body fn x => x + 1 (lambda (x) (+ x 1)) Giving a name to a function: val name = fn arg => body val inc = fn x => x + 1 (define inc (lambda (x) (+ x 1)))

ML functions The syntax for anonymous functions in ML is: fn arg => body fn x => x + 1 (lambda (x) (+ x 1)) Giving a name to a function: val name = fn arg => body val inc = fn x => x + 1 (define inc (lambda (x) (+ x 1)))

37 ML functions The syntax for anonymous functions in ML is: fn arg => body fn x => x + 1 (lambda (x) (+ x 1)) Giving a name to a function: val name = fn arg => body val inc = fn x => x + 1 (define inc (lambda (x) (+ x 1))) Or: fun name arg = body

ML functions The syntax for anonymous functions in ML is: fn arg => body fn x => x + 1 (lambda (x) (+ x 1)) Giving a name to a function: val name = fn arg => body val inc = fn x => x + 1 (define inc (lambda (x) (+ x 1))) Or: fun name arg = body fun inc x = x + 1

ML functions The syntax for anonymous functions in ML is: fn arg => body fn x => x + 1 (lambda (x) (+ x 1)) Giving a name to a function: val name = fn arg => body val inc = fn x => x + 1 (define inc (lambda (x) (+ x 1))) Or: fun name arg = body fun inc x = x + 1 (define (inc x) (+ x 1))

ML functions The syntax for anonymous functions in ML is: fn arg => body fn x => x + 1 (lambda (x) (+ x 1)) Giving a name to a function: val name = fn arg => body val inc = fn x => x + 1 (define inc (lambda (x) (+ x 1))) Or: fun name arg = body fun inc x = x + 1 (define (inc x) (+ x 1))

ML functions The syntax for anonymous functions in ML is: fn arg => body fn x => x + 1 (lambda (x) (+ x 1)) Giving a name to a function: val name = fn arg => body val inc = fn x => x + 1 (define inc (lambda (x) (+ x 1))) Or: fun name arg = body fun inc x = x + 1 (define (inc x) (+ x 1)) In ML every function accepts exactly one argument.

ML functions The syntax for anonymous functions in ML is: fn arg => body fn x => x + 1 (lambda (x) (+ x 1)) Giving a name to a function: Or: val name = fn arg => body val inc = fn x => x + 1 (define inc (lambda (x) (+ x 1))) fun name arg = body fun inc x = x + 1 (define (inc x) (+ x 1)) In ML every function accepts exactly one argument.this argument could be a tuple.

38 ML types The type of a function is determined by the type of the input and the 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

39 parametric polymorphism What is the type of fn x => x?

parametric polymorphism What is the type of fn x => x? - fun id x = x; val id = fn : a -> a - 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

- fun id x = x; val id = fn : a -> a parametric polymorphism The a in ML stands for α. It is a type variable.

- fun id x = x; val id = fn : a -> a parametric polymorphism The a in ML stands for α. It is a type variable. id is a polymorphic function.

- fun id x = x; val id = fn : a -> a parametric polymorphism The a in ML stands for α. It is a type variable. id is a polymorphic function. α α means for every valid type α, α α α α α

- fun id x = x; val id = fn : a -> a parametric polymorphism 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 instantiated to int.

Examples: parametric polymorphism fun choose (a,b,c) = if a then b else c;

Examples: parametric polymorphism fun choose (a,b,c) = if a then b else c; choose : bool * a * a -> a;

Examples: parametric polymorphism fun choose (a,b,c) = if a then b else c; choose : bool * a * a -> a; fun swap (x,y) = (y,x);

Examples: parametric polymorphism 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;

parametric polymorphism What is the type of the following function? fun length lst = if (null lst) then 0 else 1 + length (tl lst);

parametric polymorphism What is the type of the following function? fun length lst = if (null lst) then 0 else 1 + length (tl lst); length : a list -> int

parametric polymorphism What is the type of the following function? fun length lst = if (null lst) then 0 else 1 + length (tl lst); length : a list -> int List is a polymorphic data type.

With named functions: currying - fun sum x y = x + y; val sum = fn : int -> int -> int - sum 2; val it = fn : int -> int - sum 2 3; val it = 5 : int

With anonymous functions: currying - 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

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; stdin:52.5-52.18 Warning: binding not exhaustive h :: r =... val h = 1 : int val r = [2,3,4] : int list 45

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]; stdin:66.5-66.19 Warning: binding not exhaustive h :: _ =... val h = 1 : int

pattern matching Function declaration with pattern matching: fun <name> <pattern1> = <exp1> <name> <pattern2> = <exp2>.. <name> <patternn> = <expn>; This means: the function name is name. It takes one argument (as any other ML function). It tries to match the argument to pattern1. If it succeeds, it returns value of exp1. Otherwise, tries to match the argument to pattern2. Etc, etc.

pattern matching For example we can (and should!) rewrite len to use pattern matching: fun len [] = 0 len (x::xs) = 1 + len xs; len : a list -> int 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:...

pattern matching Function firstlist takes a list of pairs and returns the list consisting of the first elements only. 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

Syntax of an ML let expression: let <declaration> <declaration>... in <expression> end ML notes

Example: ML notes 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

52 ML notes Function application is left-associative: Example: - reverse 1::[2,3]; f g h == (f g) h... Error: operator and operand don t agree operator domain: Z list operand: int in expression: reverse 1 - reverse (1::[2,3]); val it = [3,2,1] : int list

-val i = 3 : count; val i = 3: count type synonyms We can give existing types new names. Syntax: type new type = ty new type becomes an alias (a synonym) for the existing type ty. -type float = real; type float = real -type count = int and average = real; type count = int type average = real -val f : float = 2.3; val f = 2.3: float

54 type synonyms But notice float, real, and average are all of the same base type, i.e. real -val f : float = 2.3; val f = 2.3 : float -val a = f : average; val a = 2.3 : average -val sum = a+f; val sum = 4.6 : average -val sum = f+a; val sum = 4.6 : float

55 user defined datatypes General Syntax: datatype new_type = Cons1 of type1 Cons2 of type2... ConsN of typen Defines a new type called new type. type1,...,typen are previously defined types. Cons1,...,ConsN are constructors. They are used to create a value of new type type. of type is omitted if a constructor does not need any argument (such constructors are called constants).

56 enumerated types All constructors are constants (no argument). Example: -datatype color = Red Blue Green; datatype color = Blue Green Red -val c = Red; val c = Red : color -fun colorstr Red = "Red" colorstr Blue = "Blue" colorstr Green= "Green"; val colorstr = fn : color -> string -colorstr c; val it = "Red" : string

57 Create union of different types: variant types datatype number = R of real I of int; val n1 = I 2; val n2 = R 3.0; val lst = [R 2.2, I 3, I 4, R 0.1]; (* val lst = [R 2.2,I 3,I 4,R 0.1] : number list *)

variant types datatype number = R of real I of int; val lst = [R 2.2, I 3, I 4, R 0.1]; fun sumints [] = 0 sumints ((I x)::rest) = x + sumints rest sumints ((R x)::rest) = sumints rest; sumints : number list -> int; sumints lst; (* val it = 7 : int *)

recursive types A datatype can be recursive: e.g. a linked list datatype llist = Nil Node of int * llist; (* datatype llist = Nil Node of int * llist *) val x = Nil; (* val x = Nil : llist *) val y = Node (5,Nil); (* val y = Node (5,Nil) : llist *) val z = Node(3, Node(2, Node(1,Nil))); (* val z = Node (3,Node (2,Node #)) : llist *)

recursive types datatype llist = Nil Node of int * llist; (* datatype llist = Nil Node of int * llist *) (* length of a linked list *) fun len Nil = 0 len (Node (_,xs)) = 1 + len xs; len : llist -> int len z; (* val it = 3 : int*)

recursive types What about a polymorphic linked list? datatype a llist = Nil Node of a * ( a llist); (* datatype a llist = Nil Node of a * a llist *) val y = Node (5,Nil); (* val y = Node (5,Nil) : int llist *) val z = Node("A", Node("B",Nil)); (* val z = Node ("A",Node ("B",Nil)) : string llist *)

recursive types datatype a llist = Nil Node of a * ( a llist); (* datatype a llist = Nil Node of a * a llist *) fun len Nil = 0 len (Node (_,xs)) = 1 + len xs; len : a llist -> int len y; (* val it = 1 : int *) len z; (* val it = 2 : int *)

recursive types Example: Tree representation of simple mathematical expressions. ( 3 + 2) + (( 1) + 4) 7) add add mult abs 2 add 7 neg neg 4 3 What is the datatype we need? 1 63

The datatype: recursive types datatype math_tree = Leaf of int Unary of (int -> int) * math_tree Binary of (int * int -> int) * math_tree * math_tree

The tree in the figure: recursive types val t = Binary(op +, Binary(op +, Unary((fn x => if x > 0 then x else ~x), Unary (op ~, Leaf 3)), Leaf 2), Binary(op *, Binary(op +, Unary(op ~, Leaf 1), Leaf 4), Leaf 7))

Evaluating the tree: recursive types (* evaluate the math_tree *) fun eval (Leaf n) = n eval (Unary (f,t)) = f (eval t) eval (Binary (f,l,r)) = f (eval l,eval r); eval : math_tree -> int - eval t; val it = 26 : int

SML notes mutual recursion Let s mimic the Scheme definitions of even and odd: fun even 0 = true even x = odd (x - 1); fun odd 0 = false odd x = even (x - 1); Error: unbound variable or constructor: odd

68 Use the keyword and. fun even 0 = true even x = odd (x - 1) and odd 0 = false odd x = even (x - 1); even : int -> bool; odd : int -> bool mutual recursion even 42; (* val it = true : bool *) odd 42; (* val it = false : bool *)

69 mutually recursive types Example: a tree with labeled branches: 1 6 2 3 7 8 4 5 datatype a tree = Empty Node of a branch * a branch and a branch = Branch of a * a tree;

The tree in the figure: mutually recursive types val lt = Node(Branch(1, Node(Branch(2, Empty), Branch(3, Node(Branch(4, Empty), Branch(5, Empty))))), Branch(6, Node(Branch(7, Empty), Branch(8, Empty))));

mutually recursive types datatype a tree = Empty Node of a branch * a branch and a branch = Branch of a * a tree Return the list of branch labels, in order: fun listtree Empty = [] listtree (Node (l,r)) = (listbranch l) @ (listbranch r) and listbranch (Branch (b,t)) = b :: (listtree t); listtree : a tree -> a list; listbranch : a branch -> a list listtree t; (* val it = [1,2,3,4,5,6,7,8] : int list *)

72 recursive types 1. A powerful tool for constructing new types. 2. The structure of the datatype suggests the structure of the recursive function on the datatype.

recursive types Recall our CFG for arithmetic expressions: <expn> --> <expn> + <expn> <expn> - <expn> <expn> * <expn> <expn> / <expn> <identifier> <literal> Let s define a (somewhat) corresponding datatype: datatype expn = Plus of expn * expn Minus of expn * expn Times of expn * expn Divn of expn * expn Num of real;

recursive types datatype expn = Plus of expn * expn Minus of expn * expn Times of expn * expn Divn of expn * expn Num of real; For example: 5 + 7*(-2) - 8 / 4 val e = Minus(Plus(Num 5.0, Times(Num 7.0, Num ~2.0)), Divn(Num 8.0, Num 4.0));

Evaluating the expression: recursive types fun eval (Num n) = n eval (Divn (x,y)) = (eval x) / (eval y) eval (Times (x,y)) = (eval x) * (eval y) eval (Minus (x,y)) = (eval x) - (eval y) eval (Plus (x,y)) = (eval x) + (eval y); eval : expn -> real; eval e; (* val it = ~11.0 : real *)

76 SML types, types, types... built-in types type synonyms, user-defined types enumerated, variant, union, recursive, mutually recursive recursion, higher-order functions (maps, folds)

using let in ML fun sumcube n = let fun cube x = x * x * x in if n = 0 then 0 else cube (n) + sumcube (n-1) end; Is it a good idea? cube is redefined every recursive call.

Better idea: using let in ML fun sumcube n = let fun cube x = x * x * x; fun sumc 0 = 0 sumc m = cube m + sumc (m-1) in sumc n end;

79 scope Name resolution: Given the use of a name (variable or function name), which instance of the entity with that name is referred to? Each use of a name must be associated with a single entity at run-time (i.e., an offset within a stack frame). The scope of a declaration of a name is the part of the program in which a use of that name refers to that declaration. The design of a language includes scope rules for resolving the mapping from the use of each name to its appropriate declaration.

80 A name is: scope visible to a piece of code if its scope includes that piece of code. local to a piece of code (block/ procedure/main program) if its declaration is within that piece of code. non-local to a piece of code if it is visible, but its declaration is not within that piece of code. A declaration of a name is hidden if another declaration supersedes it in scope.

scope example program L; var n: char; {n declared in L} procedure W; begin write(n); {n referenced in W} end; procedure D; var n: char; {n declared in D} begin n:= D ; {n referenced in D} W end; begin n:= L ; {n referenced in L} W; D

82 lexical scope Names are associated with declarations at compile time. Find the smallest block syntactically enclosing the reference and containing a declaration of the name. Example: The reference to n in W is associated with the declaration of n in L. The output is? Also called static scope.

83 dynamic scope Names are associated with declarations at run time. Find the most recent, currently active run-time stack frame containing a declaration of the name. Example: The reference to n in W is associated with two different declarations at two different times. The output is?

scope Lexical scope is based on the principle that consistent renaming of variables should not effect the value of an expression. Lexical scope goes a long way to establishing referential transparency easy to determine where variables obtain their values, because program itself (unlike run-time contexts) is not changing. Most modern languages (including Scheme and ML) use lexical scope, although early interpreted languages (LISP) used dynamic scope because of the flexibility and ease of implementation.

exceptions 85

85 exceptions Many functions are partial, i.e. they are only defined for a subset of the function s domain type. Other values in the domain type may be treated as exceptions: e.g., f (x) = 1/x Exceptions are a control construct. They provide a structured form of jump to exit a construct such as a function invocation or a block.

85 exceptions Many functions are partial, i.e. they are only defined for a subset of the function s domain type. Other values in the domain type may be treated as exceptions: e.g., f (x) = 1/x Exceptions are a control construct. They provide a structured form of jump to exit a construct such as a function invocation or a block. Terminate part of computation: Jump out of construct. Pass data as part of jump. Return to most recent site set up to handle exception. Unnecessary activation records may be deallocated (may need to free heap space, other resources).

86 Consider the following functions: fun f x = 1.0 / x; fun g x = hd x; fun h x = tl x; exceptions Will the type-checker complain? Is there a problem?

87 SML exceptions ML s exceptions (similar to the ones in Java or Python) provide a uniform way to handle errors, and eliminate the need for ad hoc, special exceptional return values from functions. When encountering a special case, raise an exception. The caller will catch/handle the exception and will take care of it. Primitive exceptions: 3 div 0 (* raises Div *) hd nil (* raises Empty *)

SML user-defined exceptions Defining an exception: exception <name> of <type> Raising an exception: raise <name> <arg> exception factneg; fun robustfact n= let fun fact 0 = 1 fact n = n * fact (n-1); in if n < 0 then raise factneg else fact n end; robustfact 5; (* val it = 120 : int *) robustfact ~5; (* uncaught exception factneg *)

SML exceptions Exceptions can have arguments, just like datatypes: exception factneg of int; fun robustfact n= let fun fact 0 = 1 fact n = n * fact (n-1); in if n < 0 then raise factneg n else fact n end;

Handling exceptions: SML exceptions <expr> handle <match1> => <expr1> <match2> => <expr2>... <matchn> => <exprn> Any restrictions on types?

Example: SML exceptions fun printfact n = print (Int.toString(robustFact n)^"\n") handle factneg x => print ("printfact "^(Int.toString x)^ ": argument must be >= 0\n"); (* val printfact = fn : int -> unit *) printfact ~5; (* printfact ~5: argument must be >= 0 val it = () : unit *)

SML exceptions Exceptions are handled according to dynamic scoping. exception e1 and e2 and e3; fun h 1 = raise e1 h 2 = raise e2 h 3 = raise e3 h _ = "ok"; fun g(n) = h(n) handle e2 => "error g2" e3 => "error g3"; fun f(n) = g(n) handle e1 => "error f1" e2 => "error f2"; f(4); f(3); f(2); f(1); f(0);

93 General dynamic scoping rule: exception handling Jump to most recently established handler on run-time stack. Dynamic scoping is not an accident: User knows how to handler error. Author of library function does not.

exceptions Why do we need exceptions in a strongly typed language?

exceptions Why do we need exceptions in a strongly typed language? Type-checker only checks types of parameters, not their values. In languages w/o exception handling, when an exception occurs, control goes to the OS and the program is terminated, or special code must be written to handle exceptions (e.g., pass special parameter or use return value of procedure to indicate status of program, etc.) In contrast, with exception handling, programs can fix the problem and continue, if desirable. Some uses: (see examples in Mitchell 8.2) Error handling (when no reasonable value can be returned). Efficiency (we don t like this one). Two types of exceptions in SML: Built-in exceptions. User-defined exceptions.

immutable data Variables (and data structures), once created and initialized, are immutable: they are never changed, updated, or stored into. Powerful guarantees of noninterference. Build new data structures (and let the old ones be garbage collected) instead of modifying old ones.

96 references functional programming no side effects ML separates pure expressions and side effects. Assignment is restricted to reference cells, which are of a different type. Assignment restrictions are enforced by the type system.

references Syntax: ref v creates a reference cell with value v!r returns contents of reference cell r r:=v sets contents of reference cell r to value v val x = ref 0; (* val x = ref 0 : int ref *) x; (* val it = ref 0 : int ref *)!x; (* val it = 0 : int *) x:=3; (* val it = () : unit *) x; (* val it = ref 3 : int ref *)!x; (* val it = 3 : int *) 97

references The type of a reference cell that contains value v of type α is α ref. Assignment to a reference cell must be consistent with the type of the reference cell Example: val x = ref "cscc24"; (* val x = ref "cscc24" : string ref *) x:=0; (* Error: operator and operand don t agree operator domain: string ref * string operand: string ref * int in expression: x := 0 *)

references (* abslst = fn : int ref list -> unit *) fun abslst [] = () abslst (x::rest) = (x:=abs(!x); abslst rest); val L = [ref ~1, ref 0, ref ~2]; (* val L = [ref ~1,ref 0,ref ~2] : int ref list*) abslst L; (* val it = () : unit *) L; (* val it = [ref 1,ref 0,ref 2] : int ref list *)

references (* abslst = fn : int list -> int list *) fun abslst [] = [] abslst (x::rest) = (abs x)::(abslst rest); fun abslst lst = map abs lst; val L = [~1, 0, ~2]; (* val L = [~1,0,~2] : int list *) abslst L; (* val it = [1,0,2] : int list *) L; (* val it = [~1,0,~2] : int list *)

101 ML functional type safe static type checking type inference polymorphic abstract data types exception handling static scope formal definition garbage collection immutable data types updatable references