+ All Categories
Home > Documents > Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai...

Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai...

Date post: 13-Jan-2016
Category:
Upload: eugenia-rosemary-kennedy
View: 219 times
Download: 0 times
Share this document with a friend
Popular Tags:
51
Programming Languages Section 4 1 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015
Transcript
Page 1: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 1

Programming Languages

Section 4. SML Remaining Topics

Xiaojuan Cai

Spring 2015

Page 2: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 2

Remaining Topics

• Type Inference

• Mutual Recursion

• Module System

• Equivalence – optional

• Lambda calculus – in separate slides

Page 3: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 3

Some concepts• Static type-checking v.s. Dynamic type-

checkingStatic: ML, Java, C, ..Dynamic: Racket, Ruby, Python, ...

• Implicit type v.s. Explicit typeImplicit: ML, Racket, ...Explicit: C, Java, ...

• Type-checking v.s. Type inferenceType-checking: check type consistencyType inference: infer types for implicit type language

Page 4: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 4

ML type inference• Determine types of bindings in order • (Except for mutual recursion)

• For each val or fun binding:• Analyze definition for all necessary facts

(constraints)• Example: If see x > 0, then x must have

type int• Type error if no way for all facts to hold

• Afterward, use type variables (e.g., 'a) for any unconstrained types• Example: An unused argument can have any

type

Page 5: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 5

Very simple example

val x = 42 (* val x : int *)

fun f (y, z, w) = if y (* y must be bool *) then z + x (* z must be int *) else 0 (* both branches have same type *)(* f must return an int f must take a bool * int * ANYTHING so val f : bool * int * 'a -> int *)

Page 6: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 6

Relation to Polymorphism

•Central feature of ML type inference: it can infer types with type variables

•But remember there are two orthogonal concepts•Languages can have type

inference without type variables•Languages can have type variables

without type inference

Page 7: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 7

More examples

•Collect all the facts needed for type-checking

•These facts constrain the type of the function

•See the code file and/or the reading notes

Page 8: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 8

A local optimum• Despite the value restriction, ML type inference

is elegant and fairly easy to understand

• More difficult without polymorphism

• What type should length-of-list have?

• More difficult with subtyping

• val (y,z) = x constrains x to have at least two fields, not exactly two fields

• Depending on details, languages can support this, but types often more difficult to infer and understand

• Will study subtyping later, but not with type inference

Page 9: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 9

Where are we?

•Type Inference

•Mutual Recursion

•Module System

•Equivalence

Page 10: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 10

Mutual Recursion

•Allow f to call g and g to call f

•Useful? Yes.

• Idiom we will show: implementing state machines

Page 11: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 11

New language features• Mutually recursive functions (the and

keyword)

• Similarly, mutually recursive datatype bindings

• Everything in “mutual recursion bundle” type-checked together and can refer to each other

fun f1 p1 = e1and f2 p2 = e2 and f3 p3 = e3

datatype t1 = …and t2 = … and t3 = …

Page 12: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 12

State-machine example

• Each “state of the computation” is a function

• “State transition” is “call another function” with “rest of input”

• Generalizes to any finite-state-machine example

fun state1 input_left = …

and state2 input_left = …

and …

Page 13: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 13

Work-around

• Suppose we did not have support for mutually recursive functions

• Can have the “later” function pass itself to the “earlier” one

• Yet another higher-order function idiom

fun earlier (f,x) = … f y …

… (* no need to be nearby *)

fun later x = … earlier(later,y) …

Page 14: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 14

Where are we?

•Type Inference

•Mutual Recursion

•Module System

•Equivalence

Page 15: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 15

Modules

Inside a moduleCan have any kind of binding (val, datatype, exception, ...)

Outside a module, Can refer to modules’ bindings via

ModuleName.bindingName

structure MyModule = struct bindings end

Page 16: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 16

Examplestructure MyMathLib = struct

fun fact x = if x=0 then 1 else x * fact(x-1)

val half_pi = Math.pi / 2

fun doubler x = x * 2

end

Page 17: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 17

Open

• Can use open ModuleName to get “direct” access to a module’s bindings

• Never necessary; just a convenience; often bad style

• Often better to create local val-bindings for just the bindings you use a lot, e.g., val map = List.map

Page 18: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 18

Namespace management

• So far, this is just namespace management

• Giving a hierarchy to names to avoid shadowing

• Allows different modules to reuse names, e.g., map

• Very important, but not very interesting

Page 19: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 19

Signatures

signature MATHLIB = sig val fact : int -> intval half_pi : real val doubler : int -> intend

structure MyMathLib :> MATHLIB = struct fun fact x = …val half_pi = Math.pi / 2.0fun doubler x = x * 2end

Page 20: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 20

In general

• Signatures

• Can include variables, types, datatypes, and exceptions defined in module

• Ascribing a signature to a module

• Module will not type-check unless it matches the signature, meaning it has all the bindings at the right types

structure MyModule :> SIGNAME = struct bindings end

signature SIGNAME = sig types-for-bindings end

Page 21: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 21

Hiding things

Real value of signatures is to to hide bindings and type definitions

Hiding implementation details is the most important strategy for writing correct, robust, reusable software

Page 22: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 23

Example

Outside the module, MyMathLib.doubler is simply unbound

signature MATHLIB = sig val fact : int -> intval half_pi : real end

structure MyMathLib :> MATHLIB = struct fun fact x = …val half_pi = Math.pi / 2.0fun doubler x = x * 2end

Page 23: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 24

A larger exampleNow consider a module that defines an Abstract Data Type (ADT)Our example: rational numbers supporting add and toString

structure Rational1 = struct datatype rational = Whole of int | Frac of int*intexception BadFrac

(*internal functions gcd and reduce not on slide*)

fun make_frac (x,y) = …fun add (r1,r2) = …fun toString r = …end

Page 24: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 25

Library spec and invariants

Properties [externally visible guarantees, up to library writer]

• Disallow denominators of 0

• Return strings in reduced form (“4” not “4/1”, “3/2” not “9/6”)

• No infinite loops or exceptions

Invariants [part of the implementation, not the module’s spec]

• All denominators are greater than 0

• All rational values returned from functions are reduced

Page 25: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 26

More on invariantsOur code maintains the invariants and relies on them

Maintain:

• make_frac disallows 0 denominator, removes negative denominator, and reduces result

• add assumes invariants on inputs, calls reduce if needed

Rely:

• gcd does not work with negative arguments, but no denominator can be negative

• add uses math properties to avoid calling reduce

• toString assumes its argument is already reduced

Page 26: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 27

A first signatureWith what we know so far, this signature makes sense:

•gcd and reduce not visible outside the module

signature RATIONAL_A = sig datatype rational = Whole of int | Frac of int*intexception BadFracval make_frac : int * int -> rationalval add : rational * rational -> rationalval toString : rational -> stringend

structure Rational1 :> RATIONAL_A = …

Page 27: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 28

The problem

Any of these would lead to exceptions, infinite loops, or wrong results, which is why the module’s code would never return them

• Rational1.Frac(1,0)

• Rational1.Frac(3,~2)

• Rational1.Frac(9,6)

signature RATIONAL_A = sig datatype rational = Whole of int | Frac of int*int…

Page 28: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 29

So hide more

Key idea: An ADT must hide the concrete type definition

Alas, this attempt doesn’t work because the signature now uses a type rational that is not known to exist:

signature RATIONAL_WRONG = sig exception BadFracval make_frac : int * int -> rationalval add : rational * rational -> rationalval toString : rational -> stringend

structure Rational1 :> RATIONAL_WRONG = …

Page 29: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 30

Abstract typesSo ML has a feature for exactly this situation:

In a signature:

type foo

means the type exists, but clients do not know its definition

signature RATIONAL_B = sig type rationalexception BadFracval make_frac : int * int -> rationalval add : rational * rational -> rationalval toString : rational -> stringend

structure Rational1 :> RATIONAL_B = …

Page 30: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 31

This works!

• Only way to make first rational is Rational1.make_frac

• After that can use only Rational1.make_frac, Rational1.add, and Rational1.toString

• Hides constructors and patterns – don’t even know whether or not Rational1.rational is a datatype

signature RATIONAL_B = sig type rationalexception BadFracval make_frac : int * int -> rationalval add : rational * rational -> rationalval toString : rational -> stringend

Page 31: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 32

Two key restrictions

So we have two powerful ways to use signatures for hiding:

1. Deny bindings exist (val-bindings, fun-bindings, constructors)

2. Make types abstract (so clients cannot create values of them or access their pieces directly)

Page 32: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 33

A cute twistIn our example, exposing the Whole constructor is no problem

• Still hiding the rest of the datatype

• Still does not allow using Whole as a pattern

signature RATIONAL_C = sig type rationalexception BadFracval Whole : int -> rationalval make_frac : int * int -> rationalval add : rational * rational -> rationalval toString : rational -> stringend

Page 33: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 34

Signature matching

structure Foo :> BAR is allowed if:

• Every non-abstract type in BAR is provided in Foo, as specified

• Every abstract type in BAR is provided in Foo in some way

• Can be a datatype or a type synonym

• Every val-binding in BAR is provided in Foo, possibly with a more general and/or less abstract internal type

• Every exception in BAR is provided in Foo

Page 34: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 35

Equivalent implementationsExample (see code file):

•structure Rational2 does not keep rationals in reduced form, instead reducing them “at last moment” in toString

• Also make gcd and reduce local functions

•Not equivalent under RATIONAL_A

• Rational1.toString(Rational1.Frac(9,6)) = "9/6"

• Rational2.toString(Rational2.Frac(9,6)) = "3/2”

•Equivalent under RATIONAL_B or RATIONAL_C

• Different invariants, but same properties

• Essential that type rational is abstract

Page 35: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 36

More interesting example

Given a signature with an abstract type, different structures can:

• Have that signature

• But implement the abstract type differentlySuch structures might or might not be equivalentExample (see code):

• type rational = int * int

• Does not have signature RATIONAL_A

• Equivalent to both previous examples under RATIONAL_B or RATIONAL_C

Page 36: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 37

More interesting example

structure Rational3 = struct type rational = int * intexception BadFrac

fun make_frac (x,y) = …fun Whole i = (i,1) (* needed for RATIONAL_C *) fun add ((a,b)(c,d)) = (a*d+b*c,b*d)fun toString r = … (* reduce at last minute *)end

Page 37: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 38

Some interesting things• Internally make_frac has type int * int ->

int * int, but externally int * int -> rational

• Could give type rational -> rational in signature, but this is awful: makes entire module unusable – why?

• Internally Whole has type 'a -> 'a * int but externally int -> rational

• This matches because we can specialize 'a to int and then abstract int * int to rational

Page 38: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 39

Can’t mix-and-match module bindings

Modules with the same signatures still define different typesSo things like this do not type-check:

• Rational1.toString(Rational2.make_frac(9,6))

• Rational3.toString(Rational2.make_frac(9,6))

Page 39: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 40

Where are we?

•Type Inference

•Mutual Recursion

•Module System

•Equivalence

Page 40: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 41

Equivalence

Must reason about “are these equivalent” all the time

•Code maintenance: Can I simplify this code?

•Backward compatibility: Can I add new features without changing how any old features work?

•Optimization: Can I make this code faster?

•Abstraction: Can an external client tell I made this change?

Page 41: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 42

A definition

Two functions are equivalent if they have the same “observable behavior” in any context: Given equivalent arguments, they:

• Produce equivalent results

• Have the same (non-)termination behavior

• Mutate (non-local) memory in the same way

• Do the same input/output

• Raise the same exceptions

Page 42: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 43

ExampleSince looking up variables in ML has no side effects, these two functions are equivalent:

But these next two are not equivalent in general: it depends on what is passed for f

• Are equivalent if argument for f has no side-effects

• Example: g ((fn i => print "hi" ; i), 7)

fun f x = x + xval y = 2fun f x = y * x

fun g (f,x) = (f x) + (f x)

val y = 2fun g (f,x) = y * (f x)

Page 43: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 44

Another example• Again: pure functions make more things

equivalent

• Example: g divides by 0 and h mutates a top-level reference

• Example: g writes to a reference that h reads from

fun f x = let val y = g x val z = h x in (y,z) end

fun f x = let val z = h x

val y = g x in (y,z) end

Page 44: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 45

Syntactic sugar

Using or not using syntactic sugar is always equivalent

• By definition, else not syntactic sugar

But be careful about evaluation order

fun f x = if x then g x else false

fun f x = x andalso g x

fun f x = if g x then x else false

fun f x = x andalso g x

Page 45: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 46

Standard equivalences1. Consistently rename bound variables and uses

But notice you can’t use a variable name already used in the function body to refer to something else

val y = 14fun f x = x+y+x

val y = 14fun f z = z+y+z

val y = 14fun f x = x+y+x

val y = 14fun f y = y+y+y

fun f x = let val y = 3 in x+y end

fun f y = let val y = 3 in y+y end

Page 46: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 47

Standard equivalences2. Use a helper function or do not

But notice you need to be careful about environments

val y = 14fun f x = x+y+xfun g z = (f z)+z

val y = 14fun g z = (z+y+z)+z

val y = 14fun f x = x+y+xval y = 7fun g z = (f z)+z

val y = 14val y = 7fun g z = (z+y+z)+z

Page 47: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 48

Standard equivalences

3. Unnecessary function wrapping

But notice that if you compute the function to call and that computation has side-effects, you have to be careful

fun f x = x+xfun g y = f y

fun f x = x+xval g = f

fun f x = x+xfun h () = (print "hi";

f)fun g y = (h()) y

fun f x = x+xfun h () = (print "hi";

f)val g = (h())

Page 48: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 49

One moreIf we ignore types, then ML let-bindings can be syntactic sugar for calling an anonymous function:

• These both evaluate e1 to v1, then evaluate e2 in an environment extended to map x to v1

• So exactly the same evaluation of expressions and result

But in ML, there is a type-system difference:

• x on the left can have a polymorphic type, but not on the right

• Can always go from right to left

• If x need not be polymorphic, can go from left to right

let val x = e1in e2 end

(fn x => e2) e1

Page 49: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 50

What about performance?

According to our definition of equivalence, these two functions are equivalent, but we learned one is awful

• (Actually we studied this before pattern-matching)fun max xs =

case xs of [] => raise Empty | x::[] => x | x::xs’ => if x > max xs’ then x else max xs’

fun max xs = case xs of [] => raise Empty | x::[] => x | x::xs’ => let val y = max xs’ in if x > y then x else y end

Page 50: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 51

Different definitions for different jobs• PL Equivalence: given same inputs, same

outputs and effects

• Good: Lets us replace bad max with good max

• Bad: Ignores performance in the extreme

• Asymptotic equivalence: Ignore constant factors

• Good: Focus on the algorithm and efficiency for large inputs

• Bad: Ignores “four times faster”

• Systems equivalence: Account for constant overheads, performance tune

• Good: Faster means different and better

• Bad: Beware overtuning on “wrong” (e.g., small) inputs; definition does not let you “swap in a different algorithm”

Page 51: Programming LanguagesSection 41 Programming Languages Section 4. SML Remaining Topics Xiaojuan Cai Spring 2015.

Programming Languages Section 4 52

Conclusion

•Type Inference

•Mutual Recursion

•Module System

•Equivalence


Recommended